diff --git a/application/application.go b/application/application.go index 574e6391..a8200790 100644 --- a/application/application.go +++ b/application/application.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" + "github.com/MadBase/MadNet/dynamics" "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/utils" "github.com/sirupsen/logrus" @@ -14,6 +15,7 @@ import ( "github.com/MadBase/MadNet/application/objs/uint256" "github.com/MadBase/MadNet/application/pendingtx" "github.com/MadBase/MadNet/application/utxohandler" + "github.com/MadBase/MadNet/application/wrapper" trie "github.com/MadBase/MadNet/badgerTrie" "github.com/MadBase/MadNet/consensus/appmock" consensusdb "github.com/MadBase/MadNet/consensus/db" @@ -36,8 +38,9 @@ type Application struct { } // Init initializes Application ... -func (a *Application) Init(conDB *consensusdb.Database, memDB *badger.DB, dph *deposit.Handler) error { +func (a *Application) Init(conDB *consensusdb.Database, memDB *badger.DB, dph *deposit.Handler, storageInterface dynamics.StorageGetter) error { a.logger = logging.GetLogger(constants.LoggerApp) + storage := wrapper.NewStorage(storageInterface) uHdlr := utxohandler.NewUTXOHandler(conDB.DB()) pHdlr := pendingtx.NewPendingTxHandler(memDB) pHdlr.UTXOHandler = uHdlr @@ -50,6 +53,7 @@ func (a *Application) Init(conDB *consensusdb.Database, memDB *badger.DB, dph *d dHdlr: dph, uHdlr: uHdlr, cdb: conDB, + storage: storage, } a.txHandler.dHdlr.IsSpent = a.txHandler.uHdlr.TrieContains // initialize the application with a random key. @@ -151,7 +155,7 @@ func (a *Application) IsValid(txn *badger.Txn, chainID uint32, height uint32, st utils.DebugTrace(a.logger, err) return false, err } - if err := txs.Validate(height, vout); err != nil { + if err := txs.Validate(height, vout, a.txHandler.storage); err != nil { utils.DebugTrace(a.logger, err) return false, err } @@ -221,7 +225,11 @@ func (a *Application) SetMiningKey(privKey []byte, curveSpec constants.CurveSpec switch curveSpec { case constants.CurveBN256Eth: signer := &crypto.BNSigner{} - signer.SetPrivk(privKey) + err := signer.SetPrivk(privKey) + if err != nil { + utils.DebugTrace(a.logger, err) + return err + } pubk, err := signer.Pubkey() if err != nil { utils.DebugTrace(a.logger, err) diff --git a/application/deposit/deposit.go b/application/deposit/deposit.go index 1b15d77c..ed64a230 100644 --- a/application/deposit/deposit.go +++ b/application/deposit/deposit.go @@ -86,6 +86,7 @@ func (dp *Handler) Add(txn *badger.Txn, chainID uint32, utxoID []byte, biValue * Value: value, ChainID: chainID, Owner: vso, + Fee: new(uint256.Uint256).SetZero(), }, TxHash: n2, } diff --git a/application/deposit/deposit_test.go b/application/deposit/deposit_test.go index 4ab9dec6..c17fc5cb 100644 --- a/application/deposit/deposit_test.go +++ b/application/deposit/deposit_test.go @@ -29,12 +29,15 @@ func newDepositHandler() *Handler { func testingOwner() *objs.Owner { signer := &crypto.BNSigner{} - signer.SetPrivk([]byte("secret")) + err := signer.SetPrivk([]byte("secret")) + if err != nil { + panic(err) + } pubk, _ := signer.Pubkey() acct := crypto.GetAccount(pubk) owner := &objs.Owner{} curveSpec := constants.CurveSecp256k1 - err := owner.New(acct, curveSpec) + err = owner.New(acct, curveSpec) if err != nil { panic(err) } diff --git a/application/indexer/expsize.go b/application/indexer/expsize.go index 83b58b63..9c8412a5 100644 --- a/application/indexer/expsize.go +++ b/application/indexer/expsize.go @@ -94,34 +94,38 @@ func (esi *ExpSizeIndex) Drop(txn *badger.Txn, utxoID []byte) error { return utils.DeleteValue(txn, key) } -func (esi *ExpSizeIndex) GetExpiredObjects(txn *badger.Txn, epoch uint32, maxBytes uint32) ([][]byte, uint32) { +func (esi *ExpSizeIndex) GetExpiredObjects(txn *badger.Txn, epoch uint32, maxBytes uint32, maxObjects int) ([][]byte, uint32) { result := [][]byte{} byteCount := uint32(0) + objCount := 0 opts := badger.DefaultIteratorOptions prefix := esi.prefix() opts.Prefix = prefix iter := txn.NewIterator(opts) defer iter.Close() for iter.Seek(prefix); iter.ValidForPrefix(prefix); iter.Next() { + if objCount+1 > maxObjects { + break + } + if byteCount+constants.HashLen > maxBytes { + break + } itm := iter.Item() key := itm.KeyCopy(nil) key = key[len(prefix):] epochBytes := key[:4] // slice is 4 bytes so no error will be raised epochObj, _ := utils.UnmarshalUint32(epochBytes) - sizeInvBytes := key[4:8] - // slice is 4 bytes so no error will be raised - sizeObjInv, _ := utils.UnmarshalUint32(sizeInvBytes) - sizeObj := constants.MaxUint32 - sizeObjInv utxoID := key[8:] - if epochObj <= epoch { - if byteCount+sizeObj <= maxBytes { - byteCount += sizeObj - result = append(result, utxoID) - } + if epochObj > epoch { + break } + byteCount += constants.HashLen + result = append(result, utxoID) + objCount++ } - return result, byteCount + remainingBytes := maxBytes - byteCount + return result, remainingBytes } func (esi *ExpSizeIndex) makeKey(epoch uint32, size uint32, utxoID []byte) *ExpSizeIndexKey { diff --git a/application/indexer/indexer_test.go b/application/indexer/indexer_test.go index 266a5e05..6c6bf04c 100644 --- a/application/indexer/indexer_test.go +++ b/application/indexer/indexer_test.go @@ -623,7 +623,8 @@ func TestExpSizeIndex(t *testing.T) { if err != nil { t.Fatal(err) } - expObjs, expSize := index.GetExpiredObjects(txn, epoch1, 99) + maxBytes := uint32(99) + expObjs, remainingBytes := index.GetExpiredObjects(txn, epoch1, maxBytes, constants.MaxTxVectorLength) if len(expObjs) != 2 { t.Fatal("expObjs len wrong", len(expObjs)) } @@ -633,22 +634,23 @@ func TestExpSizeIndex(t *testing.T) { if !bytes.Equal(expObjs[1], txHash1) { t.Fatal("exp1 wrong") } - if expSize != size1+size2 { - t.Fatal("exp size wrong") + if maxBytes-remainingBytes != 2*constants.HashLen { + t.Fatal("wrong size for remainingBytes") } err = index.Drop(txn, txHash1) if err != nil { t.Fatal(err) } - expObjs, expSize = index.GetExpiredObjects(txn, epoch1, 99) + maxBytes = 99 + expObjs, remainingBytes = index.GetExpiredObjects(txn, epoch1, maxBytes, constants.MaxTxVectorLength) if len(expObjs) != 1 { t.Fatal("expObjs len wrong", len(expObjs)) } if !bytes.Equal(expObjs[0], txHash2) { t.Fatal("exp0 wrong") } - if expSize != size2 { - t.Fatal("exp size wrong") + if maxBytes-remainingBytes != constants.HashLen { + t.Fatal("wrong size for remainingBytes") } return nil }) diff --git a/application/indexer/valueold.go b/application/indexer/valueold.go deleted file mode 100644 index 7824f437..00000000 --- a/application/indexer/valueold.go +++ /dev/null @@ -1,169 +0,0 @@ -package indexer - -import ( - "github.com/MadBase/MadNet/application/objs" - "github.com/MadBase/MadNet/constants" - "github.com/MadBase/MadNet/utils" - "github.com/dgraph-io/badger/v2" -) - -/* -|| - -| - | - -*/ - -// TODO HANDLE UINT32 OVERFLOW - -func newValueIndexOld(p, pp prefixFunc) *valueIndexOld { - return &valueIndexOld{p, pp} -} - -// valueIndexOld creates an index that allows objects to be dropped -// by epoch -type valueIndexOld struct { - prefix prefixFunc - refPrefix prefixFunc -} - -type valueIndexOldKey struct { - key []byte -} - -func (vik *valueIndexOldKey) MarshalBinary() []byte { - return utils.CopySlice(vik.key) -} - -func (vik *valueIndexOldKey) UnmarshalBinary(data []byte) { - vik.key = utils.CopySlice(data) -} - -type valueIndexOldRefKey struct { - refkey []byte -} - -// MarshalBinary returns the byte slice for the key object -func (virk *valueIndexOldRefKey) MarshalBinary() []byte { - return utils.CopySlice(virk.refkey) -} - -// UnmarshalBinary takes in a byte slice to set the key object -func (virk *valueIndexOldRefKey) UnmarshalBinary(data []byte) { - virk.refkey = utils.CopySlice(data) -} - -// Add adds an item to the list -func (vi *valueIndexOld) Add(txn *badger.Txn, utxoID []byte, owner *objs.Owner, value uint32) error { - viKey, err := vi.makeKey(owner, value, utxoID) - if err != nil { - return err - } - key := viKey.MarshalBinary() - viRefKey := vi.makeRefKey(utxoID) - refKey := viRefKey.MarshalBinary() - valueIndexOld, err := vi.makevalueIndexOld(owner, value, utxoID) - if err != nil { - return err - } - err = utils.SetValue(txn, refKey, valueIndexOld) - if err != nil { - return err - } - return utils.SetValue(txn, key, utils.CopySlice(utxoID)) -} - -// Drop returns a list of all txHashes that should be dropped -func (vi *valueIndexOld) Drop(txn *badger.Txn, utxoID []byte) error { - utxoIDCopy := utils.CopySlice(utxoID) - viRefKey := vi.makeRefKey(utxoIDCopy) - refKey := viRefKey.MarshalBinary() - valueIndexOld, err := utils.GetValue(txn, refKey) - if err != nil { - return err - } - key := []byte{} - key = append(key, vi.prefix()...) - key = append(key, valueIndexOld...) - err = utils.DeleteValue(txn, refKey) - if err != nil { - return err - } - return utils.DeleteValue(txn, key) -} - -func (vi *valueIndexOld) GetValueForOwner(txn *badger.Txn, owner *objs.Owner, minValue uint32, exclude [][]byte) ([][]byte, uint32, error) { - exclusionSet := make(map[[constants.HashLen]byte]bool) - var hsh [constants.HashLen]byte - for _, ID := range exclude { - copy(hsh[:], utils.CopySlice(ID)) - exclusionSet[hsh] = true - } - result := [][]byte{} - valueCount := uint32(0) - opts := badger.DefaultIteratorOptions - prefix := vi.prefix() - ownerBytes, err := owner.MarshalBinary() - if err != nil { - return nil, 0, err - } - prefix = append(prefix, ownerBytes...) - opts.Prefix = prefix - iter := txn.NewIterator(opts) - defer iter.Close() - for iter.Seek(prefix); iter.ValidForPrefix(prefix); iter.Next() { - itm := iter.Item() - key := itm.KeyCopy(nil) - valueBytes := key[len(prefix) : len(prefix)+4] - value, _ := utils.UnmarshalUint32(valueBytes) - utxoID, err := itm.ValueCopy(nil) - if err != nil { - return nil, 0, err - } - copy(hsh[:], utxoID) - if !exclusionSet[hsh] { - valueCount += value - result = append(result, utxoID) - } - if valueCount >= minValue { - break - } - } - return result, valueCount, nil -} - -func (vi *valueIndexOld) makeKey(owner *objs.Owner, value uint32, utxoID []byte) (*valueIndexOldKey, error) { - valueIndexOld, err := vi.makevalueIndexOld(owner, value, utxoID) - if err != nil { - return nil, err - } - key := []byte{} - key = append(key, vi.prefix()...) - key = append(key, valueIndexOld...) - viKey := &valueIndexOldKey{} - viKey.UnmarshalBinary(key) - return viKey, nil -} - -func (vi *valueIndexOld) makeRefKey(utxoID []byte) *valueIndexOldRefKey { - refKey := []byte{} - refKey = append(refKey, vi.refPrefix()...) - refKey = append(refKey, utils.CopySlice(utxoID)...) - viRefKey := &valueIndexOldRefKey{} - viRefKey.UnmarshalBinary(refKey) - return viRefKey -} - -func (vi *valueIndexOld) makevalueIndexOld(owner *objs.Owner, value uint32, utxoID []byte) ([]byte, error) { - valueBytes := utils.MarshalUint32(value) - ownerBytes, err := owner.MarshalBinary() - if err != nil { - return nil, err - } - valueIndexOld := []byte{} - valueIndexOld = append(valueIndexOld, ownerBytes...) - valueIndexOld = append(valueIndexOld, valueBytes...) - valueIndexOld = append(valueIndexOld, utils.CopySlice(utxoID)...) - return valueIndexOld, nil -} diff --git a/application/indexer/valueold_test.go b/application/indexer/valueold_test.go deleted file mode 100644 index d8f0987d..00000000 --- a/application/indexer/valueold_test.go +++ /dev/null @@ -1,179 +0,0 @@ -package indexer - -import ( - "bytes" - "io/ioutil" - "os" - "testing" - - "github.com/MadBase/MadNet/application/objs" - "github.com/MadBase/MadNet/crypto" - "github.com/MadBase/MadNet/utils" - "github.com/dgraph-io/badger/v2" -) - -func makeValueIndexOld() *valueIndexOld { - prefix1 := func() []byte { - return []byte("ya") - } - prefix2 := func() []byte { - return []byte("yb") - } - index := newValueIndexOld(prefix1, prefix2) - return index -} - -func TestValueOldIndexAdd(t *testing.T) { - dir, err := ioutil.TempDir("", "badger-test") - if err != nil { - t.Fatal(err) - } - defer func() { - if err := os.RemoveAll(dir); err != nil { - t.Fatal(err) - } - }() - opts := badger.DefaultOptions(dir) - db, err := badger.Open(opts) - if err != nil { - t.Fatal(err) - } - defer db.Close() - - index := makeValueIndexOld() - owner := &objs.Owner{} - utxoID := crypto.Hasher([]byte("utxoID")) - value := uint32(25519) - - db.Update(func(txn *badger.Txn) error { - err := index.Add(txn, utxoID, owner, value) - if err == nil { - // Invalid Owner - t.Fatal("Should have raised error (1)") - } - return nil - }) - owner = makeOwner() - db.Update(func(txn *badger.Txn) error { - err = index.Add(txn, utxoID, owner, value) - if err != nil { - t.Fatal(err) - } - return nil - }) -} - -func TestValueOldIndexDrop(t *testing.T) { - dir, err := ioutil.TempDir("", "badger-test") - if err != nil { - t.Fatal(err) - } - defer func() { - if err := os.RemoveAll(dir); err != nil { - t.Fatal(err) - } - }() - opts := badger.DefaultOptions(dir) - db, err := badger.Open(opts) - if err != nil { - t.Fatal(err) - } - defer db.Close() - - index := makeValueIndexOld() - owner := makeOwner() - utxoID := crypto.Hasher([]byte("utxoID")) - value := uint32(25519) - - db.Update(func(txn *badger.Txn) error { - err := index.Drop(txn, utxoID) - if err == nil { - // Not present - t.Fatal("Should have raised error") - } - err = index.Add(txn, utxoID, owner, value) - if err != nil { - t.Fatal(err) - } - err = index.Drop(txn, utxoID) - if err != nil { - t.Fatal(err) - } - return nil - }) -} - -func TestValueOldIndexMakeKey(t *testing.T) { - dir, err := ioutil.TempDir("", "badger-test") - if err != nil { - t.Fatal(err) - } - defer func() { - if err := os.RemoveAll(dir); err != nil { - t.Fatal(err) - } - }() - opts := badger.DefaultOptions(dir) - db, err := badger.Open(opts) - if err != nil { - t.Fatal(err) - } - defer db.Close() - - index := makeValueIndexOld() - owner := &objs.Owner{} - utxoID := crypto.Hasher([]byte("utxoID")) - value := uint32(25519) - _, err = index.makeKey(owner, value, utxoID) - if err == nil { - t.Fatal("Should have raised error (1)") - } - - owner = makeOwner() - ownerBytes, err := owner.MarshalBinary() - if err != nil { - t.Fatal(err) - } - trueKey := []byte{} - trueKey = append(trueKey, index.prefix()...) - trueKey = append(trueKey, ownerBytes...) - trueKey = append(trueKey, utils.MarshalUint32(value)...) - trueKey = append(trueKey, utxoID...) - viKey, err := index.makeKey(owner, value, utxoID) - if err != nil { - t.Fatal(err) - } - key := viKey.MarshalBinary() - if !bytes.Equal(key, trueKey) { - t.Fatal("keys do not agree") - } -} - -func TestValueOldIndexMakeRefKey(t *testing.T) { - dir, err := ioutil.TempDir("", "badger-test") - if err != nil { - t.Fatal(err) - } - defer func() { - if err := os.RemoveAll(dir); err != nil { - t.Fatal(err) - } - }() - opts := badger.DefaultOptions(dir) - db, err := badger.Open(opts) - if err != nil { - t.Fatal(err) - } - defer db.Close() - - index := makeValueIndex() - utxoID := crypto.Hasher([]byte("utxoID")) - trueKey := []byte{} - trueKey = append(trueKey, index.refPrefix()...) - trueKey = append(trueKey, utxoID...) - viKey := index.makeRefKey(utxoID) - key := viKey.MarshalBinary() - if !bytes.Equal(key, trueKey) { - t.Fatal("keys do not agree") - } -} diff --git a/application/minedtx/mined_test.go b/application/minedtx/mined_test.go index 58434710..4d2c093d 100644 --- a/application/minedtx/mined_test.go +++ b/application/minedtx/mined_test.go @@ -46,6 +46,7 @@ func makeVS(ownerSigner objs.Signer) *objs.TXOut { ChainID: cid, Value: val, Owner: owner, + Fee: uint256.Zero(), } vs := &objs.ValueStore{ VSPreImage: vsp, @@ -173,7 +174,13 @@ func TestMined(t *testing.T) { hndlr := NewMinedTxHandler() signer := &crypto.BNSigner{} - signer.SetPrivk([]byte("secret")) + err = signer.SetPrivk([]byte("secret")) + if err != nil { + t.Fatal(err) + } + + msg := makeMockStorageGetter() + storage := makeStorage(msg) //////////////////////////////////////// ownerSigner := testingOwner() @@ -181,11 +188,11 @@ func TestMined(t *testing.T) { tx2 := makeTxConsuming(ownerSigner, consumedUTXOs) - _, err = tx.Validate(nil, 1, consumedUTXOs) + _, err = tx.Validate(nil, 1, consumedUTXOs, storage) if err != nil { t.Fatal(err) } - _, err = tx2.Validate(nil, 1, tx.Vout) + _, err = tx2.Validate(nil, 1, tx.Vout, storage) if err != nil { t.Fatal(err) } @@ -284,7 +291,10 @@ func TestMinedDelete(t *testing.T) { ownerSigner := testingOwner() consumedUTXOs, tx := makeTxInitial(ownerSigner) - _, err = tx.Validate(nil, 1, consumedUTXOs) + msg := makeMockStorageGetter() + storage := makeStorage(msg) + + _, err = tx.Validate(nil, 1, consumedUTXOs, storage) if err != nil { t.Fatal(err) } @@ -344,7 +354,10 @@ func TestMinedGet(t *testing.T) { ownerSigner := testingOwner() consumedUTXOs, tx := makeTxInitial(ownerSigner) - _, err = tx.Validate(nil, 1, consumedUTXOs) + msg := makeMockStorageGetter() + storage := makeStorage(msg) + + _, err = tx.Validate(nil, 1, consumedUTXOs, storage) if err != nil { t.Fatal(err) } @@ -424,7 +437,10 @@ func TestMinedGetHeightForTx(t *testing.T) { ownerSigner := testingOwner() consumedUTXOs, tx := makeTxInitial(ownerSigner) - _, err = tx.Validate(nil, 1, consumedUTXOs) + msg := makeMockStorageGetter() + storage := makeStorage(msg) + + _, err = tx.Validate(nil, 1, consumedUTXOs, storage) if err != nil { t.Fatal(err) } @@ -488,7 +504,10 @@ func TestMinedGetOneInternal(t *testing.T) { ownerSigner := testingOwner() consumedUTXOs, tx := makeTxInitial(ownerSigner) - _, err = tx.Validate(nil, 1, consumedUTXOs) + msg := makeMockStorageGetter() + storage := makeStorage(msg) + + _, err = tx.Validate(nil, 1, consumedUTXOs, storage) if err != nil { t.Fatal(err) } @@ -560,7 +579,10 @@ func TestMinedAddOneInternal(t *testing.T) { ownerSigner := testingOwner() consumedUTXOs, tx := makeTxInitial(ownerSigner) - _, err = tx.Validate(nil, 1, consumedUTXOs) + msg := makeMockStorageGetter() + storage := makeStorage(msg) + + _, err = tx.Validate(nil, 1, consumedUTXOs, storage) if err != nil { t.Fatal(err) } diff --git a/application/minedtx/mockstorage_test.go b/application/minedtx/mockstorage_test.go new file mode 100644 index 00000000..35a02335 --- /dev/null +++ b/application/minedtx/mockstorage_test.go @@ -0,0 +1,145 @@ +package minedtx + +import ( + "math/big" + "time" + + "github.com/MadBase/MadNet/application/wrapper" + "github.com/MadBase/MadNet/dynamics" + "github.com/dgraph-io/badger/v2" +) + +func makeMockStorageGetter() *mockStorageGetter { + maxBytes := uint32(0) + dataStoreEpochFee := new(big.Int).SetInt64(0) + atomicSwapFee := new(big.Int).SetInt64(0) + valueStoreFee := new(big.Int).SetInt64(0) + minTxFee := new(big.Int).SetInt64(0) + + msg := &mockStorageGetter{ + maxBytes: maxBytes, + dataStoreEpochFee: dataStoreEpochFee, + valueStoreFee: valueStoreFee, + atomicSwapFee: atomicSwapFee, + minTxFee: minTxFee, + } + return msg +} + +func makeStorage(msg dynamics.StorageGetter) *wrapper.Storage { + storage := wrapper.NewStorage(msg) + return storage +} + +type mockStorageGetter struct { + maxBytes uint32 + dataStoreEpochFee *big.Int + valueStoreFee *big.Int + atomicSwapFee *big.Int + minTxFee *big.Int + maxTxVectorLength int +} + +func (msg *mockStorageGetter) GetMaxBytes() uint32 { + return msg.maxBytes +} + +func (msg *mockStorageGetter) SetMaxBytes(value uint32) { + msg.maxBytes = value +} + +func (msg *mockStorageGetter) GetMaxProposalSize() uint32 { + return msg.maxBytes +} + +func (msg *mockStorageGetter) GetProposalStepTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetPreVoteStepTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetPreCommitStepTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetDeadBlockRoundNextRoundTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetDownloadTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetSrvrMsgTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetMsgTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetMaxTxVectorLength() int { + return 128 +} + +func (msg *mockStorageGetter) UpdateStorage(txn *badger.Txn, update dynamics.Updater) error { + return nil +} +func (msg *mockStorageGetter) LoadStorage(txn *badger.Txn, epoch uint32) error { + return nil +} + +func (msg *mockStorageGetter) GetDataStoreEpochFee() *big.Int { + return msg.dataStoreEpochFee +} + +func (msg *mockStorageGetter) SetDataStoreEpochFee(value *big.Int) { + if value == nil { + panic("invalid value") + } + msg.dataStoreEpochFee.Set(value) +} + +func (msg *mockStorageGetter) GetDataStoreValidVersion() uint32 { + return 0 +} + +func (msg *mockStorageGetter) GetValueStoreFee() *big.Int { + return msg.valueStoreFee +} + +func (msg *mockStorageGetter) SetValueStoreFee(value *big.Int) { + if value == nil { + panic("invalid value") + } + msg.valueStoreFee.Set(value) +} + +func (msg *mockStorageGetter) GetValueStoreValidVersion() uint32 { + return 0 +} + +func (msg *mockStorageGetter) GetAtomicSwapFee() *big.Int { + return msg.atomicSwapFee +} + +func (msg *mockStorageGetter) SetAtomicSwapFee(value *big.Int) { + if value == nil { + panic("invalid value") + } + msg.atomicSwapFee.Set(value) +} + +func (msg *mockStorageGetter) GetAtomicSwapValidStopEpoch() uint32 { + return 0 +} + +func (msg *mockStorageGetter) GetMinTxFee() *big.Int { + return msg.minTxFee +} + +func (msg *mockStorageGetter) SetMinTxFee(value *big.Int) { + if value == nil { + panic("invalid value") + } + msg.minTxFee.Set(value) +} + +func (msg *mockStorageGetter) GetTxValidVersion() uint32 { + return 0 +} diff --git a/application/objs/as.go b/application/objs/as.go index bc854688..ffad1a52 100644 --- a/application/objs/as.go +++ b/application/objs/as.go @@ -4,6 +4,7 @@ import ( "github.com/MadBase/MadNet/application/objs/atomicswap" mdefs "github.com/MadBase/MadNet/application/objs/capn" "github.com/MadBase/MadNet/application/objs/uint256" + "github.com/MadBase/MadNet/application/wrapper" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" "github.com/MadBase/MadNet/errorz" @@ -142,12 +143,37 @@ func (b *AtomicSwap) SetTxHash(txHash []byte) error { // Value returns the Value of the object func (b *AtomicSwap) Value() (*uint256.Uint256, error) { - if b == nil || b.ASPreImage == nil || b.ASPreImage.Value == nil { + if b == nil || b.ASPreImage == nil || b.ASPreImage.Value == nil || b.ASPreImage.Value.IsZero() { return nil, errorz.ErrInvalid{}.New("not initialized") } return b.ASPreImage.Value.Clone(), nil } +// Fee returns the Fee of the object +func (b *AtomicSwap) Fee() (*uint256.Uint256, error) { + if b == nil || b.ASPreImage == nil || b.ASPreImage.Fee == nil { + return nil, errorz.ErrInvalid{}.New("not initialized") + } + return b.ASPreImage.Fee.Clone(), nil +} + +// ValuePlusFee returns the Value of the object with the associated fee +func (b *AtomicSwap) ValuePlusFee() (*uint256.Uint256, error) { + value, err := b.Value() + if err != nil { + return nil, err + } + fee, err := b.Fee() + if err != nil { + return nil, err + } + total, err := new(uint256.Uint256).Add(value, fee) + if err != nil { + return nil, err + } + return total, nil +} + // Owner returns the AtomicSwapOwner object of the AtomicSwap func (b *AtomicSwap) Owner() (*AtomicSwapOwner, error) { if b == nil || b.ASPreImage == nil { @@ -205,6 +231,22 @@ func (b *AtomicSwap) IsExpired(currentHeight uint32) (bool, error) { return b.ASPreImage.IsExpired(currentHeight) } +// ValidateFee validates the fee of the object at the time of creation +func (b *AtomicSwap) ValidateFee(storage *wrapper.Storage) error { + fee, err := b.Fee() + if err != nil { + return err + } + feeTrue, err := storage.GetAtomicSwapFee() + if err != nil { + return err + } + if fee.Cmp(feeTrue) != 0 { + return errorz.ErrInvalid{}.New("invalid fee") + } + return nil +} + // ValidateSignature validates the signature of the TXIn against the atomic swap func (b *AtomicSwap) ValidateSignature(currentHeight uint32, txIn *TXIn) error { if b == nil { diff --git a/application/objs/as_test.go b/application/objs/as_test.go index e2bdde56..f2db1edc 100644 --- a/application/objs/as_test.go +++ b/application/objs/as_test.go @@ -55,6 +55,7 @@ func TestAtomicSwapGood(t *testing.T) { Owner: owner, IssuedAt: iat, Exp: exp, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) as := &AtomicSwap{ @@ -128,6 +129,7 @@ func TestAtomicSwapBad1(t *testing.T) { Owner: owner, IssuedAt: iat, Exp: exp, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) as := &AtomicSwap{ @@ -191,6 +193,7 @@ func TestAtomicSwapBad2(t *testing.T) { Owner: owner, IssuedAt: iat, Exp: exp, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen+1) // Invalid TxHash as := &AtomicSwap{ @@ -751,3 +754,8 @@ func TestAtomicSwapMakeTxIn(t *testing.T) { t.Fatal(err) } } + +func TestAtomicSwapValidateFee(t *testing.T) { + return + panic("test not implemented") +} diff --git a/application/objs/asowner_test.go b/application/objs/asowner_test.go index 564ceed1..5cdc2579 100644 --- a/application/objs/asowner_test.go +++ b/application/objs/asowner_test.go @@ -54,6 +54,7 @@ func TestASOwnerSig(t *testing.T) { Owner: owner, IssuedAt: iat, Exp: exp, + Fee: new(uint256.Uint256).SetZero(), } asp2 := &ASPreImage{} aspBytes, err := asp.MarshalBinary() diff --git a/application/objs/aspi.go b/application/objs/aspi.go index 05cff942..0940709a 100644 --- a/application/objs/aspi.go +++ b/application/objs/aspi.go @@ -18,6 +18,7 @@ type ASPreImage struct { IssuedAt uint32 Exp uint32 Owner *AtomicSwapOwner + Fee *uint256.Uint256 // preHash []byte } @@ -54,6 +55,7 @@ func (b *ASPreImage) UnmarshalCapn(bc mdefs.ASPreImage) error { if err := owner.UnmarshalBinary(bc.Owner()); err != nil { return err } + b.Owner = owner b.ChainID = bc.ChainID() u32array := [8]uint32{} u32array[0] = bc.Value() @@ -70,10 +72,24 @@ func (b *ASPreImage) UnmarshalCapn(bc mdefs.ASPreImage) error { return err } b.Value = vObj + b.TXOutIdx = bc.TXOutIdx() - b.Owner = owner b.IssuedAt = bc.IssuedAt() b.Exp = bc.Exp() + fObj := &uint256.Uint256{} + u32array[0] = bc.Fee0() + u32array[1] = bc.Fee1() + u32array[2] = bc.Fee2() + u32array[3] = bc.Fee3() + u32array[4] = bc.Fee4() + u32array[5] = bc.Fee5() + u32array[6] = bc.Fee6() + u32array[7] = bc.Fee7() + err = fObj.FromUint32Array(u32array) + if err != nil { + return err + } + b.Fee = fObj return nil } @@ -120,6 +136,18 @@ func (b *ASPreImage) MarshalCapn(seg *capnp.Segment) (mdefs.ASPreImage, error) { bc.SetValue5(u32array[5]) bc.SetValue6(u32array[6]) bc.SetValue7(u32array[7]) + u32array, err = b.Fee.ToUint32Array() + if err != nil { + return bc, err + } + bc.SetFee0(u32array[0]) + bc.SetFee1(u32array[1]) + bc.SetFee2(u32array[2]) + bc.SetFee3(u32array[3]) + bc.SetFee4(u32array[4]) + bc.SetFee5(u32array[5]) + bc.SetFee6(u32array[6]) + bc.SetFee7(u32array[7]) bc.SetTXOutIdx(b.TXOutIdx) bc.SetIssuedAt(b.IssuedAt) bc.SetExp(b.Exp) diff --git a/application/objs/aspi_test.go b/application/objs/aspi_test.go index 5f0fa4e7..1de63145 100644 --- a/application/objs/aspi_test.go +++ b/application/objs/aspi_test.go @@ -52,6 +52,7 @@ func TestASPreImageGood(t *testing.T) { Owner: owner, IssuedAt: iat, Exp: exp, + Fee: new(uint256.Uint256).SetZero(), } asp2 := &ASPreImage{} aspBytes, err := asp.MarshalBinary() @@ -137,6 +138,7 @@ func TestASPreImageBad1(t *testing.T) { Owner: owner, IssuedAt: iat, Exp: exp, + Fee: new(uint256.Uint256).SetZero(), } asp2 := &ASPreImage{} aspBytes, err := asp.MarshalBinary() @@ -244,6 +246,7 @@ func TestASPreImageBad3(t *testing.T) { Owner: owner, IssuedAt: iat, Exp: exp, + Fee: new(uint256.Uint256).SetZero(), } asp2 := &ASPreImage{} aspBytes, err := asp.MarshalBinary() @@ -300,6 +303,7 @@ func TestASPreImageBad4(t *testing.T) { Owner: owner, IssuedAt: iat, Exp: exp, + Fee: new(uint256.Uint256).SetZero(), } asp2 := &ASPreImage{} aspBytes, err := asp.MarshalBinary() @@ -356,6 +360,7 @@ func TestASPreImageBad5(t *testing.T) { Owner: owner, IssuedAt: iat, Exp: exp, + Fee: new(uint256.Uint256).SetZero(), } asp2 := &ASPreImage{} aspBytes, err := asp.MarshalBinary() diff --git a/application/objs/capn/application.capnp b/application/objs/capn/application.capnp index 65af9219..66e6fe76 100644 --- a/application/objs/capn/application.capnp +++ b/application/objs/capn/application.capnp @@ -3,10 +3,11 @@ using Go = import "/go.capnp"; $Go.package("capn"); $Go.import("github.com/MadBase/MadNet/application/capn"); -const defaultDSPreImage :DSPreImage = (chainID = 0, index = 0x"00", issuedAt = 0, deposit = 0, rawData = 0x"00", owner = 0x"00"); +const defaultDSPreImage :DSPreImage = (chainID = 0, index = 0x"00", issuedAt = 0, deposit = 0, rawData = 0x"00", owner = 0x"00", deposit1 = 0, deposit2 = 0, deposit3 = 0, deposit4 = 0, deposit5 = 0, deposit6 = 0, deposit7 = 0, fee0 = 0, fee1 = 0, fee2 = 0, fee3 = 0, fee4 = 0, fee5 = 0, fee6 = 0, fee7 = 0); const defaultDSLinker :DSLinker = (txHash = 0x"00", dSPreImage = .defaultDSPreImage); -const defaultVSPreImage :VSPreImage = (chainID = 0, value = 0, owner = 0x"00"); -const defaultASPreImage :ASPreImage = (chainID = 0, value = 0, owner = 0x"00", issuedAt = 0, exp = 0); +const defaultVSPreImage :VSPreImage = (chainID = 0, value = 0, owner = 0x"00", value1 = 0, value2 = 0, value3 = 0, value4 = 0, value5 = 0, value6 = 0, value7 = 0, fee0 = 0, fee1 = 0, fee2 = 0, fee3 = 0, fee4 = 0, fee5 = 0, fee6 = 0, fee7 = 0); +const defaultASPreImage :ASPreImage = (chainID = 0, value = 0, owner = 0x"00", issuedAt = 0, exp = 0, value1 = 0, value2 = 0, value3 = 0, value4 = 0, value5 = 0, value6 = 0, value7 = 0, fee0 = 0, fee1 = 0, fee2 = 0, fee3 = 0, fee4 = 0, fee5 = 0, fee6 = 0, fee7 = 0); +const defaultTFPreImage :TFPreImage = (chainID = 0, fee0 = 0, fee1 = 0, fee2 = 0, fee3 = 0, fee4 = 0, fee5 = 0, fee6 = 0, fee7 = 0); const defaultTXInPreImage :TXInPreImage = (chainID = 0, consumedTxIdx = 0, consumedTxHash = 0x"00"); const defaultTXInLinker :TXInLinker = (tXInPreImage = .defaultTXInPreImage, txHash = 0x"00"); @@ -37,6 +38,17 @@ struct DSPreImage { deposit5 @11 :UInt32 = 0; deposit6 @12 :UInt32 = 0; deposit7 @13 :UInt32 = 0; + # Deposit stores the value of the DataStore + + fee0 @14 :UInt32 = 0; + fee1 @15 :UInt32 = 0; + fee2 @16 :UInt32 = 0; + fee3 @17 :UInt32 = 0; + fee4 @18 :UInt32 = 0; + fee5 @19 :UInt32 = 0; + fee6 @20 :UInt32 = 0; + fee7 @21 :UInt32 = 0; + # Fee stores the associated fee for a DataStore } struct DSLinker { @@ -75,6 +87,17 @@ struct VSPreImage { value5 @8 :UInt32 = 0; value6 @9 :UInt32 = 0; value7 @10 :UInt32 = 0; + # Value stores the value + + fee0 @11 :UInt32 = 0; + fee1 @12 :UInt32 = 0; + fee2 @13 :UInt32 = 0; + fee3 @14 :UInt32 = 0; + fee4 @15 :UInt32 = 0; + fee5 @16 :UInt32 = 0; + fee6 @17 :UInt32 = 0; + fee7 @18 :UInt32 = 0; + # Fee stores the associated fee for a ValueStore } struct ValueStore { @@ -114,6 +137,17 @@ struct ASPreImage { value5 @10 :UInt32 = 0; value6 @11 :UInt32 = 0; value7 @12 :UInt32 = 0; + # Value stores the value which is to be swapped + + fee0 @13 :UInt32 = 0; + fee1 @14 :UInt32 = 0; + fee2 @15 :UInt32 = 0; + fee3 @16 :UInt32 = 0; + fee4 @17 :UInt32 = 0; + fee5 @18 :UInt32 = 0; + fee6 @19 :UInt32 = 0; + fee7 @20 :UInt32 = 0; + # Fee stores the associated fee for an AtomicSwap } struct AtomicSwap { @@ -126,6 +160,34 @@ struct AtomicSwap { ################################################################################ +struct TFPreImage { + chainID @0 :UInt32 = 0; + # The chainID of this object. + + tXOutIdx @1 :UInt32 = 0; + # The index at which this element appears in the transaction output list. + + fee0 @2 :UInt32 = 0; + fee1 @3 :UInt32 = 0; + fee2 @4 :UInt32 = 0; + fee3 @5 :UInt32 = 0; + fee4 @6 :UInt32 = 0; + fee5 @7 :UInt32 = 0; + fee6 @8 :UInt32 = 0; + fee7 @9 :UInt32 = 0; + # Fee stores the fee +} + +struct TxFee { + tFPreImage @0 :TFPreImage = .defaultTFPreImage; + # The structure containing particular information for this object. + + txHash @1 :Data = 0x"00"; + # The hash of the transaction that created this object. +} + +################################################################################ + struct TXInPreImage { chainID @0 :UInt32 = 0; # Chain id on which this object was created. @@ -167,6 +229,10 @@ struct TXOut { # The output if it is a valuestore atomicSwap @2 :AtomicSwap; + # The output if it is an atomicswap + + txFee @3 :TxFee; + # The output if it is a txfee } } diff --git a/application/objs/capn/application.capnp.go b/application/objs/capn/application.capnp.go index 92043b0f..2f200131 100644 --- a/application/objs/capn/application.capnp.go +++ b/application/objs/capn/application.capnp.go @@ -11,12 +11,13 @@ import ( // Constants defined in application.capnp. var ( - DefaultDSPreImage = DSPreImage{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[0:112]).Struct()} - DefaultDSLinker = DSLinker{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[112:248]).Struct()} - DefaultVSPreImage = VSPreImage{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[248:320]).Struct()} - DefaultASPreImage = ASPreImage{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[320:400]).Struct()} - DefaultTXInPreImage = TXInPreImage{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[400:440]).Struct()} - DefaultTXInLinker = TXInLinker{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[440:504]).Struct()} + DefaultDSPreImage = DSPreImage{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[0:144]).Struct()} + DefaultDSLinker = DSLinker{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[144:312]).Struct()} + DefaultVSPreImage = VSPreImage{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[312:416]).Struct()} + DefaultASPreImage = ASPreImage{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[416:528]).Struct()} + DefaultTFPreImage = TFPreImage{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[528:584]).Struct()} + DefaultTXInPreImage = TXInPreImage{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[584:624]).Struct()} + DefaultTXInLinker = TXInLinker{Struct: capnp.MustUnmarshalRootPtr(x_b99093b7d2518300[624:688]).Struct()} ) func init() { @@ -25,6 +26,7 @@ func init() { DefaultDSLinker.Segment().Message().ReadLimiter().Reset((1 << 64) - 1) DefaultVSPreImage.Segment().Message().ReadLimiter().Reset((1 << 64) - 1) DefaultASPreImage.Segment().Message().ReadLimiter().Reset((1 << 64) - 1) + DefaultTFPreImage.Segment().Message().ReadLimiter().Reset((1 << 64) - 1) DefaultTXInPreImage.Segment().Message().ReadLimiter().Reset((1 << 64) - 1) DefaultTXInLinker.Segment().Message().ReadLimiter().Reset((1 << 64) - 1) } @@ -35,12 +37,12 @@ type DSPreImage struct{ capnp.Struct } const DSPreImage_TypeID = 0xd4eb3c212b8dbb26 func NewDSPreImage(s *capnp.Segment) (DSPreImage, error) { - st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 48, PointerCount: 3}) + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 80, PointerCount: 3}) return DSPreImage{st}, err } func NewRootDSPreImage(s *capnp.Segment) (DSPreImage, error) { - st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 48, PointerCount: 3}) + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 80, PointerCount: 3}) return DSPreImage{st}, err } @@ -190,12 +192,76 @@ func (s DSPreImage) SetDeposit7(v uint32) { s.Struct.SetUint32(40, v) } +func (s DSPreImage) Fee0() uint32 { + return s.Struct.Uint32(44) +} + +func (s DSPreImage) SetFee0(v uint32) { + s.Struct.SetUint32(44, v) +} + +func (s DSPreImage) Fee1() uint32 { + return s.Struct.Uint32(48) +} + +func (s DSPreImage) SetFee1(v uint32) { + s.Struct.SetUint32(48, v) +} + +func (s DSPreImage) Fee2() uint32 { + return s.Struct.Uint32(52) +} + +func (s DSPreImage) SetFee2(v uint32) { + s.Struct.SetUint32(52, v) +} + +func (s DSPreImage) Fee3() uint32 { + return s.Struct.Uint32(56) +} + +func (s DSPreImage) SetFee3(v uint32) { + s.Struct.SetUint32(56, v) +} + +func (s DSPreImage) Fee4() uint32 { + return s.Struct.Uint32(60) +} + +func (s DSPreImage) SetFee4(v uint32) { + s.Struct.SetUint32(60, v) +} + +func (s DSPreImage) Fee5() uint32 { + return s.Struct.Uint32(64) +} + +func (s DSPreImage) SetFee5(v uint32) { + s.Struct.SetUint32(64, v) +} + +func (s DSPreImage) Fee6() uint32 { + return s.Struct.Uint32(68) +} + +func (s DSPreImage) SetFee6(v uint32) { + s.Struct.SetUint32(68, v) +} + +func (s DSPreImage) Fee7() uint32 { + return s.Struct.Uint32(72) +} + +func (s DSPreImage) SetFee7(v uint32) { + s.Struct.SetUint32(72, v) +} + // DSPreImage_List is a list of DSPreImage. type DSPreImage_List struct{ capnp.List } // NewDSPreImage creates a new list of DSPreImage. func NewDSPreImage_List(s *capnp.Segment, sz int32) (DSPreImage_List, error) { - l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 48, PointerCount: 3}, sz) + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 80, PointerCount: 3}, sz) return DSPreImage_List{l}, err } @@ -246,7 +312,7 @@ func (s DSLinker) DSPreImage() DSPreImage { s.NewDSPreImage() } p, _ := s.Struct.Ptr(0) - ss, _ := p.StructDefault(x_b99093b7d2518300[504:616]) + ss, _ := p.StructDefault(x_b99093b7d2518300[688:832]) return DSPreImage{Struct: ss} } @@ -313,7 +379,7 @@ func (p DSLinker_Promise) Struct() (DSLinker, error) { } func (p DSLinker_Promise) DSPreImage() DSPreImage_Promise { - return DSPreImage_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[616:728])} + return DSPreImage_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[832:976])} } type DataStore struct{ capnp.Struct } @@ -346,7 +412,7 @@ func (s DataStore) DSLinker() DSLinker { s.NewDSLinker() } p, _ := s.Struct.Ptr(0) - ss, _ := p.StructDefault(x_b99093b7d2518300[728:864]) + ss, _ := p.StructDefault(x_b99093b7d2518300[976:1144]) return DSLinker{Struct: ss} } @@ -413,7 +479,7 @@ func (p DataStore_Promise) Struct() (DataStore, error) { } func (p DataStore_Promise) DSLinker() DSLinker_Promise { - return DSLinker_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[864:1000])} + return DSLinker_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[1144:1312])} } type VSPreImage struct{ capnp.Struct } @@ -422,12 +488,12 @@ type VSPreImage struct{ capnp.Struct } const VSPreImage_TypeID = 0xf8c203f305398e1b func NewVSPreImage(s *capnp.Segment) (VSPreImage, error) { - st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 40, PointerCount: 1}) + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 72, PointerCount: 1}) return VSPreImage{st}, err } func NewRootVSPreImage(s *capnp.Segment) (VSPreImage, error) { - st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 40, PointerCount: 1}) + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 72, PointerCount: 1}) return VSPreImage{st}, err } @@ -537,12 +603,76 @@ func (s VSPreImage) SetValue7(v uint32) { s.Struct.SetUint32(36, v) } +func (s VSPreImage) Fee0() uint32 { + return s.Struct.Uint32(40) +} + +func (s VSPreImage) SetFee0(v uint32) { + s.Struct.SetUint32(40, v) +} + +func (s VSPreImage) Fee1() uint32 { + return s.Struct.Uint32(44) +} + +func (s VSPreImage) SetFee1(v uint32) { + s.Struct.SetUint32(44, v) +} + +func (s VSPreImage) Fee2() uint32 { + return s.Struct.Uint32(48) +} + +func (s VSPreImage) SetFee2(v uint32) { + s.Struct.SetUint32(48, v) +} + +func (s VSPreImage) Fee3() uint32 { + return s.Struct.Uint32(52) +} + +func (s VSPreImage) SetFee3(v uint32) { + s.Struct.SetUint32(52, v) +} + +func (s VSPreImage) Fee4() uint32 { + return s.Struct.Uint32(56) +} + +func (s VSPreImage) SetFee4(v uint32) { + s.Struct.SetUint32(56, v) +} + +func (s VSPreImage) Fee5() uint32 { + return s.Struct.Uint32(60) +} + +func (s VSPreImage) SetFee5(v uint32) { + s.Struct.SetUint32(60, v) +} + +func (s VSPreImage) Fee6() uint32 { + return s.Struct.Uint32(64) +} + +func (s VSPreImage) SetFee6(v uint32) { + s.Struct.SetUint32(64, v) +} + +func (s VSPreImage) Fee7() uint32 { + return s.Struct.Uint32(68) +} + +func (s VSPreImage) SetFee7(v uint32) { + s.Struct.SetUint32(68, v) +} + // VSPreImage_List is a list of VSPreImage. type VSPreImage_List struct{ capnp.List } // NewVSPreImage creates a new list of VSPreImage. func NewVSPreImage_List(s *capnp.Segment, sz int32) (VSPreImage_List, error) { - l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 40, PointerCount: 1}, sz) + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 72, PointerCount: 1}, sz) return VSPreImage_List{l}, err } @@ -593,7 +723,7 @@ func (s ValueStore) VSPreImage() VSPreImage { s.NewVSPreImage() } p, _ := s.Struct.Ptr(0) - ss, _ := p.StructDefault(x_b99093b7d2518300[1000:1072]) + ss, _ := p.StructDefault(x_b99093b7d2518300[1312:1416]) return VSPreImage{Struct: ss} } @@ -660,7 +790,7 @@ func (p ValueStore_Promise) Struct() (ValueStore, error) { } func (p ValueStore_Promise) VSPreImage() VSPreImage_Promise { - return VSPreImage_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[1072:1144])} + return VSPreImage_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[1416:1520])} } type ASPreImage struct{ capnp.Struct } @@ -669,12 +799,12 @@ type ASPreImage struct{ capnp.Struct } const ASPreImage_TypeID = 0xa6bc62ab6b339789 func NewASPreImage(s *capnp.Segment) (ASPreImage, error) { - st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 48, PointerCount: 1}) + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 80, PointerCount: 1}) return ASPreImage{st}, err } func NewRootASPreImage(s *capnp.Segment) (ASPreImage, error) { - st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 48, PointerCount: 1}) + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 80, PointerCount: 1}) return ASPreImage{st}, err } @@ -800,12 +930,76 @@ func (s ASPreImage) SetValue7(v uint32) { s.Struct.SetUint32(44, v) } +func (s ASPreImage) Fee0() uint32 { + return s.Struct.Uint32(48) +} + +func (s ASPreImage) SetFee0(v uint32) { + s.Struct.SetUint32(48, v) +} + +func (s ASPreImage) Fee1() uint32 { + return s.Struct.Uint32(52) +} + +func (s ASPreImage) SetFee1(v uint32) { + s.Struct.SetUint32(52, v) +} + +func (s ASPreImage) Fee2() uint32 { + return s.Struct.Uint32(56) +} + +func (s ASPreImage) SetFee2(v uint32) { + s.Struct.SetUint32(56, v) +} + +func (s ASPreImage) Fee3() uint32 { + return s.Struct.Uint32(60) +} + +func (s ASPreImage) SetFee3(v uint32) { + s.Struct.SetUint32(60, v) +} + +func (s ASPreImage) Fee4() uint32 { + return s.Struct.Uint32(64) +} + +func (s ASPreImage) SetFee4(v uint32) { + s.Struct.SetUint32(64, v) +} + +func (s ASPreImage) Fee5() uint32 { + return s.Struct.Uint32(68) +} + +func (s ASPreImage) SetFee5(v uint32) { + s.Struct.SetUint32(68, v) +} + +func (s ASPreImage) Fee6() uint32 { + return s.Struct.Uint32(72) +} + +func (s ASPreImage) SetFee6(v uint32) { + s.Struct.SetUint32(72, v) +} + +func (s ASPreImage) Fee7() uint32 { + return s.Struct.Uint32(76) +} + +func (s ASPreImage) SetFee7(v uint32) { + s.Struct.SetUint32(76, v) +} + // ASPreImage_List is a list of ASPreImage. type ASPreImage_List struct{ capnp.List } // NewASPreImage creates a new list of ASPreImage. func NewASPreImage_List(s *capnp.Segment, sz int32) (ASPreImage_List, error) { - l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 48, PointerCount: 1}, sz) + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 80, PointerCount: 1}, sz) return ASPreImage_List{l}, err } @@ -856,7 +1050,7 @@ func (s AtomicSwap) ASPreImage() ASPreImage { s.NewASPreImage() } p, _ := s.Struct.Ptr(0) - ss, _ := p.StructDefault(x_b99093b7d2518300[1144:1224]) + ss, _ := p.StructDefault(x_b99093b7d2518300[1520:1632]) return ASPreImage{Struct: ss} } @@ -923,7 +1117,238 @@ func (p AtomicSwap_Promise) Struct() (AtomicSwap, error) { } func (p AtomicSwap_Promise) ASPreImage() ASPreImage_Promise { - return ASPreImage_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[1224:1304])} + return ASPreImage_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[1632:1744])} +} + +type TFPreImage struct{ capnp.Struct } + +// TFPreImage_TypeID is the unique identifier for the type TFPreImage. +const TFPreImage_TypeID = 0x828d564f51f7af4e + +func NewTFPreImage(s *capnp.Segment) (TFPreImage, error) { + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 40, PointerCount: 0}) + return TFPreImage{st}, err +} + +func NewRootTFPreImage(s *capnp.Segment) (TFPreImage, error) { + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 40, PointerCount: 0}) + return TFPreImage{st}, err +} + +func ReadRootTFPreImage(msg *capnp.Message) (TFPreImage, error) { + root, err := msg.RootPtr() + return TFPreImage{root.Struct()}, err +} + +func (s TFPreImage) String() string { + str, _ := text.Marshal(0x828d564f51f7af4e, s.Struct) + return str +} + +func (s TFPreImage) ChainID() uint32 { + return s.Struct.Uint32(0) +} + +func (s TFPreImage) SetChainID(v uint32) { + s.Struct.SetUint32(0, v) +} + +func (s TFPreImage) TXOutIdx() uint32 { + return s.Struct.Uint32(4) +} + +func (s TFPreImage) SetTXOutIdx(v uint32) { + s.Struct.SetUint32(4, v) +} + +func (s TFPreImage) Fee0() uint32 { + return s.Struct.Uint32(8) +} + +func (s TFPreImage) SetFee0(v uint32) { + s.Struct.SetUint32(8, v) +} + +func (s TFPreImage) Fee1() uint32 { + return s.Struct.Uint32(12) +} + +func (s TFPreImage) SetFee1(v uint32) { + s.Struct.SetUint32(12, v) +} + +func (s TFPreImage) Fee2() uint32 { + return s.Struct.Uint32(16) +} + +func (s TFPreImage) SetFee2(v uint32) { + s.Struct.SetUint32(16, v) +} + +func (s TFPreImage) Fee3() uint32 { + return s.Struct.Uint32(20) +} + +func (s TFPreImage) SetFee3(v uint32) { + s.Struct.SetUint32(20, v) +} + +func (s TFPreImage) Fee4() uint32 { + return s.Struct.Uint32(24) +} + +func (s TFPreImage) SetFee4(v uint32) { + s.Struct.SetUint32(24, v) +} + +func (s TFPreImage) Fee5() uint32 { + return s.Struct.Uint32(28) +} + +func (s TFPreImage) SetFee5(v uint32) { + s.Struct.SetUint32(28, v) +} + +func (s TFPreImage) Fee6() uint32 { + return s.Struct.Uint32(32) +} + +func (s TFPreImage) SetFee6(v uint32) { + s.Struct.SetUint32(32, v) +} + +func (s TFPreImage) Fee7() uint32 { + return s.Struct.Uint32(36) +} + +func (s TFPreImage) SetFee7(v uint32) { + s.Struct.SetUint32(36, v) +} + +// TFPreImage_List is a list of TFPreImage. +type TFPreImage_List struct{ capnp.List } + +// NewTFPreImage creates a new list of TFPreImage. +func NewTFPreImage_List(s *capnp.Segment, sz int32) (TFPreImage_List, error) { + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 40, PointerCount: 0}, sz) + return TFPreImage_List{l}, err +} + +func (s TFPreImage_List) At(i int) TFPreImage { return TFPreImage{s.List.Struct(i)} } + +func (s TFPreImage_List) Set(i int, v TFPreImage) error { return s.List.SetStruct(i, v.Struct) } + +func (s TFPreImage_List) String() string { + str, _ := text.MarshalList(0x828d564f51f7af4e, s.List) + return str +} + +// TFPreImage_Promise is a wrapper for a TFPreImage promised by a client call. +type TFPreImage_Promise struct{ *capnp.Pipeline } + +func (p TFPreImage_Promise) Struct() (TFPreImage, error) { + s, err := p.Pipeline.Struct() + return TFPreImage{s}, err +} + +type TxFee struct{ capnp.Struct } + +// TxFee_TypeID is the unique identifier for the type TxFee. +const TxFee_TypeID = 0x89c736f29fb5bda4 + +func NewTxFee(s *capnp.Segment) (TxFee, error) { + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}) + return TxFee{st}, err +} + +func NewRootTxFee(s *capnp.Segment) (TxFee, error) { + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}) + return TxFee{st}, err +} + +func ReadRootTxFee(msg *capnp.Message) (TxFee, error) { + root, err := msg.RootPtr() + return TxFee{root.Struct()}, err +} + +func (s TxFee) String() string { + str, _ := text.Marshal(0x89c736f29fb5bda4, s.Struct) + return str +} + +func (s TxFee) TFPreImage() TFPreImage { + if !s.HasTFPreImage() { + s.NewTFPreImage() + } + p, _ := s.Struct.Ptr(0) + ss, _ := p.StructDefault(x_b99093b7d2518300[1744:1800]) + return TFPreImage{Struct: ss} +} + +func (s TxFee) HasTFPreImage() bool { + p, err := s.Struct.Ptr(0) + return p.IsValid() || err != nil +} + +func (s TxFee) SetTFPreImage(v TFPreImage) error { + return s.Struct.SetPtr(0, v.Struct.ToPtr()) +} + +// NewTFPreImage sets the tFPreImage field to a newly +// allocated TFPreImage struct, preferring placement in s's segment. +func (s TxFee) NewTFPreImage() (TFPreImage, error) { + ss, err := NewTFPreImage(s.Struct.Segment()) + if err != nil { + return TFPreImage{}, err + } + err = s.Struct.SetPtr(0, ss.Struct.ToPtr()) + return ss, err +} +func (s TxFee) TxHash() []byte { + p, _ := s.Struct.Ptr(1) + return []byte(p.DataDefault([]byte{0x0})) +} + +func (s TxFee) HasTxHash() bool { + p, err := s.Struct.Ptr(1) + return p.IsValid() || err != nil +} + +func (s TxFee) SetTxHash(v []byte) error { + if v == nil { + v = []byte{} + } + return s.Struct.SetData(1, v) +} + +// TxFee_List is a list of TxFee. +type TxFee_List struct{ capnp.List } + +// NewTxFee creates a new list of TxFee. +func NewTxFee_List(s *capnp.Segment, sz int32) (TxFee_List, error) { + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}, sz) + return TxFee_List{l}, err +} + +func (s TxFee_List) At(i int) TxFee { return TxFee{s.List.Struct(i)} } + +func (s TxFee_List) Set(i int, v TxFee) error { return s.List.SetStruct(i, v.Struct) } + +func (s TxFee_List) String() string { + str, _ := text.MarshalList(0x89c736f29fb5bda4, s.List) + return str +} + +// TxFee_Promise is a wrapper for a TxFee promised by a client call. +type TxFee_Promise struct{ *capnp.Pipeline } + +func (p TxFee_Promise) Struct() (TxFee, error) { + s, err := p.Pipeline.Struct() + return TxFee{s}, err +} + +func (p TxFee_Promise) TFPreImage() TFPreImage_Promise { + return TFPreImage_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[1800:1856])} } type TXInPreImage struct{ capnp.Struct } @@ -1040,7 +1465,7 @@ func (s TXInLinker) TXInPreImage() TXInPreImage { s.NewTXInPreImage() } p, _ := s.Struct.Ptr(0) - ss, _ := p.StructDefault(x_b99093b7d2518300[1304:1344]) + ss, _ := p.StructDefault(x_b99093b7d2518300[1856:1896]) return TXInPreImage{Struct: ss} } @@ -1107,7 +1532,7 @@ func (p TXInLinker_Promise) Struct() (TXInLinker, error) { } func (p TXInLinker_Promise) TXInPreImage() TXInPreImage_Promise { - return TXInPreImage_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[1344:1384])} + return TXInPreImage_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[1896:1936])} } type TXIn struct{ capnp.Struct } @@ -1140,7 +1565,7 @@ func (s TXIn) TXInLinker() TXInLinker { s.NewTXInLinker() } p, _ := s.Struct.Ptr(0) - ss, _ := p.StructDefault(x_b99093b7d2518300[1384:1448]) + ss, _ := p.StructDefault(x_b99093b7d2518300[1936:2000]) return TXInLinker{Struct: ss} } @@ -1207,7 +1632,7 @@ func (p TXIn_Promise) Struct() (TXIn, error) { } func (p TXIn_Promise) TXInLinker() TXInLinker_Promise { - return TXInLinker_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[1448:1512])} + return TXInLinker_Promise{Pipeline: p.Pipeline.GetPipelineDefault(0, x_b99093b7d2518300[2000:2064])} } type TXOut struct{ capnp.Struct } @@ -1217,10 +1642,11 @@ const ( TXOut_Which_dataStore TXOut_Which = 0 TXOut_Which_valueStore TXOut_Which = 1 TXOut_Which_atomicSwap TXOut_Which = 2 + TXOut_Which_txFee TXOut_Which = 3 ) func (w TXOut_Which) String() string { - const s = "dataStorevalueStoreatomicSwap" + const s = "dataStorevalueStoreatomicSwaptxFee" switch w { case TXOut_Which_dataStore: return s[0:9] @@ -1228,6 +1654,8 @@ func (w TXOut_Which) String() string { return s[9:19] case TXOut_Which_atomicSwap: return s[19:29] + case TXOut_Which_txFee: + return s[29:34] } return "TXOut_Which(" + strconv.FormatUint(uint64(w), 10) + ")" @@ -1364,6 +1792,41 @@ func (s TXOut) NewAtomicSwap() (AtomicSwap, error) { err = s.Struct.SetPtr(0, ss.Struct.ToPtr()) return ss, err } +func (s TXOut) TxFee() (TxFee, error) { + if s.Struct.Uint16(0) != 3 { + panic("Which() != txFee") + } + p, err := s.Struct.Ptr(0) + if err != nil { + return TxFee{}, err + } + return TxFee{Struct: p.Struct()}, err +} + +func (s TXOut) HasTxFee() bool { + if s.Struct.Uint16(0) != 3 { + return false + } + p, err := s.Struct.Ptr(0) + return p.IsValid() || err != nil +} + +func (s TXOut) SetTxFee(v TxFee) error { + s.Struct.SetUint16(0, 3) + return s.Struct.SetPtr(0, v.Struct.ToPtr()) +} + +// NewTxFee sets the txFee field to a newly +// allocated TxFee struct, preferring placement in s's segment. +func (s TXOut) NewTxFee() (TxFee, error) { + s.Struct.SetUint16(0, 3) + ss, err := NewTxFee(s.Struct.Segment()) + if err != nil { + return TxFee{}, err + } + err = s.Struct.SetPtr(0, ss.Struct.ToPtr()) + return ss, err +} // TXOut_List is a list of TXOut. type TXOut_List struct{ capnp.List } @@ -1403,6 +1866,10 @@ func (p TXOut_Promise) AtomicSwap() AtomicSwap_Promise { return AtomicSwap_Promise{Pipeline: p.Pipeline.GetPipeline(0)} } +func (p TXOut_Promise) TxFee() TxFee_Promise { + return TxFee_Promise{Pipeline: p.Pipeline.GetPipeline(0)} +} + type Tx struct{ capnp.Struct } // Tx_TypeID is the unique identifier for the type Tx. @@ -1433,7 +1900,7 @@ func (s Tx) Vin() (TXIn_List, error) { if err != nil { return TXIn_List{}, err } - l, err := p.ListDefault(x_b99093b7d2518300[1512:1536]) + l, err := p.ListDefault(x_b99093b7d2518300[2064:2088]) return TXIn_List{List: l}, err } @@ -1462,7 +1929,7 @@ func (s Tx) Vout() (TXOut_List, error) { if err != nil { return TXOut_List{}, err } - l, err := p.ListDefault(x_b99093b7d2518300[1536:1560]) + l, err := p.ListDefault(x_b99093b7d2518300[2088:2112]) return TXOut_List{List: l}, err } @@ -1512,119 +1979,157 @@ func (p Tx_Promise) Struct() (Tx, error) { return Tx{s}, err } -const schema_b99093b7d2518300 = "x\xda\xbc\x97kl\x1cW\x15\xc7\xcf\x99;3\xeb]" + - "?w\xef\x0a\x90\x10Jk%\x08\x17\x83\xe2Wb\"" + - "\x1b\xdb\xedZ\xf2V\x8e\xb2\xd3\xd9\x16S\xb9R\x07\xef" + - "\x10o\x9d}\xb0\x9e\x8d7\x88\xaaAi\xa4VrB" + - "\xa2\xa6\xd4\x91\xd2\x92\xa8\xb4J\xa9x\x04Z\xd1\xb4A" + - "\xa2\x11\x05W*R\x83ZAQK\x05\x82\xa2\x1a!" + - "@|A@3\xe8\x9c\xd9\xd7\xec\xaec#!\xa4\xfd" + - "\xb0\xf7\xb7g\xef\xfd\xdfs\xcf\xfd\x9f\x99\xdds\xda\xa4" + - "2\xa0\xed\xd5\x01\x8c\x19MwK\x91\xdf<\xd9\xb77" + - "\x7f\x12\xc2\x11\xf5\xfa1\xe3\x17?|\xe4\xd4e\xc0!" + - "\x14\xbd(\xc3\"\x00`v\x08\x81\xe6G\x84\x82\x00\xee" + - "\xfa{K\xf7\x1a\xe7\xd6NC8\x82\x95hM\x09\x00" + - "\xc8>\xf1k9B\xf1r@\xac\x00\xba\xfd\x1f\x1b\xfd" + - "\xd2\xfa\xf0\xea\xa3\xcd\x91\xaf\x88\x0dy\x8d#\x7f\xce\x91" + - "\x9f\xbd\xef@?>\xe9>\xd6\x1c9\xae~KN\xab" + - "\xf4mJ\xa5\xc8\xd5\xb1\xf7\x1e\x1f~\xe4Ck\x0dZ" + - "_Q{Q\xbeIq\xe6\xeb\xaa@\xf3m\x95\xb5\xde" + - "\xb44\xfa\xb7\x17\xde\xbdm\xady\xde\x0f\xd4\xdf\xc9\xa0" + - "F\xdf4\x8d\xe6\xbd\xedg\xff\xfe\xfe\xd7\x06\x07\xce7" + - "G>\xa3m\xc8\xe79\xf2\x12G\xde\x92\xba\xfe\xec\xe4" + - "\xe7\x8e\x9doP0\xa0GPN\xe9\xa4`L\x17h" + - "\xce\xe8\xac\xe0\xcc\xf4\xf8/\x8f\xfdu\xf8\xe9\x86\xe8\xa2" + - "\xde\x8b\xf28G\x1f\xa5\xe8U/\xfa\xe1\xc7\x86\x96\x9e" + - "\xfd\xc2\x95\xa7\xc1\x88\xa0^\x95\x81\xb4\xf8Y}C>" + - "E\x7f\x18\xba\xa0\xbf\xab\x00\xba\xef\xf7\xedy\xe2\xa7'" + - "\x8f|\xa7a\xe6\xc7\x83\x83(\xbf\x1d\xa4\x99/\x06\x05" + - "\x9a\xcf\x05y\xe6s?\x88\xcd\xbf\xf3G\xedR\xf3\xfe" + - ".\x05_\x94\x97)^>\x1f\xa4\xfd}\xe3\xc5?|" + - "\xfd/\xbb\x94\x97H\x03\xfa5\x8c\x84\xfe)\xa7B|" + - ",\xa1\xef\x02\xba\xe6\x8f.\xec\xb9j\xbf\xfcj\xf3\xa4" + - "\xbf\x0dm\xc8?s\xe4\xfb!\x9a\xf4\xe3/\x9d\xf8\xe4" + - "\xcdc\x7fz\xc3\xbf1\xae\x80L\xfb\x86<\xd2N\x1b" + - "+\xb6\x7fX\x00\xba\x1f=\xf9\x19\xed\xef\xe2\xea?(" + - "V\xf3\x0b\xb8\xd6\xb5!\xdf\xe9\xa2\xd8\xb7\xbar\x94\x84" + - "\x9d\x85\xf1\xa7^\xdb\x15\xfb\x97O\xec4\x06\x04\x80\xfc" + - "|\xf8\xaa\xb4\xc2\xf4\xb7{\xc2\xa4\xf6\xe67\xaf\x9c\xda" + - "\xbf\xfe\x84\xdbX\xe6\x11*\xf3\x08\x97y\x84\xca<\xc2" + - "\x09\xb3\xf2\xf9C\xe9\x05\xcb\x11\xe9\\\xf6\xd3\x0bV>" + - "\x9b\xdf\x97\xb2\xbfh\x15\x0f9Sf\xa2`\xc73\x01" + - "\xeb\xa0\x9d@\xc4\x9e\xda\xa9\x01b\x0f@BG\xd0\xc2" + - "\x18\xaa\x9bD\xa9M\x123g\xd3\xd9%a\x17\x12\x88" + - "F\x9bP\x01TD\x0c\xf7\xdd\x0d`|B\xa01\xac" + - "`\x181\x8a\x04\xad}\x00\xc6\xbc@cQA7\xe5" + - "-k\x818hcO-\xa3\x95E\x05h\xe1`\xc8" + - "\xfb\x802\xe1\x94f\xac\xe5E\xec\x04\x05;\x016\x95" + - "\x93\x9c\x8bgg\xd3\xdd\xd9\xa5&A\xf7\xb5\x104N" + - "\x82F\x05\x1a1\x05]g.\x9e%I\xd0\x9d\xb1X" + - "R\xb5r\xca\x92\x10\xcb\x0b\xdfP\x0cV\xc4t\xe7\xf7" + - "%K\x0d\"z\x01\x8c\x9d\x02\x8d\xc9:\x11S\xb7\x00" + - "\x18c\x02\x8d9\x05\x03\x87\xd3Y\xec\x02L\x08:\x88" + - "j\x91\x03b\x17\xad\x13\x98T\xba\x0f\xe7\x8aN-\xa4" + - "Z0\xd5\x90\x04\xe2\x0d\x0e;\xd6x\xd8[\xe4\xbd\xe5" + - "\x91[\x8ee:\x81\\\xc1n\xd8\xdd\xed-R\x9c\xb9" + - "\x03\xc08$\xd0(\xf1\x99S\xb1\xd8\x05\x00\xc0\x9e\x9a" + - "\xf3zKO*\x86\xaa\x8b\xf0@\xa8^\x80p\x97\xd3" + - "\x07\xb3\x96S,\x00\xda[\x9e\xfe]\xd6\xa1\xa2mv" + - ";\xcd\xd2Z\x95\xe3~:\xfd\x19\x81FRA\xf7\xb0" + - "\xbf\x1c\xab\x97\xb6\x9c\x16\x0dA\xdd\xfa\xec[\xe5\x9bw" + - "\x8c\x05/\xdb\xdb\xdb\xf2\xe6\xd3\xdd\xd5x|\x9b\xeal" + - "\x95\x1e\xef\xa6sySz\x86+\xe9\x91\xf7\xe0\xad\x00" + - "\xe6\x1c\x0a4S\xa8`X\xf32$-\x1c\x040\xe7" + - "\x89/\x12G%\x8a\x0a\xa2\xb4\xf1v\x003E\xcc\xfcA\xe2\xa7\x88\x07\xdb\xa2" + - "\x18D\x94'\x98?D\xfc\x0c\xf1P0\x8a!Dy" + - "\x9a\xf9*\xf15\xe2\xed\xa1(\xb6#\xcaG\x99\x9f\"" + - "~\x8exG{\x14;\x10\xe5Y\xe6g\x88\x9fG\x05" + - "\x1fXX\xb4\xd2\xd9x\x0c\xdb@\xc16\xc0\x1d\x87\xa9" + - "\xec*#\xd7\x99;Pt\xe2\xa9\x12\x15y%\"\xb7" + - "\x92\xb5\x0b\xfe\x9aI//\x17\xed\xd4\x94S\x17\x16\xb0" + - "K\xf9\xca\xf7\x09\x9et\xc0?\x1c\xf4\x0f\x87\xfc\xc3a" + - "\xffp\xc4?\xdc\xe3\x1f\xee\xad\xea\xdd\xbc\xda\x92\x9e#" + - "\xee\x88g\xaa\xf5\xb6\xa9'6\xfb\xdf\x84\xe7\xc6\xdb\xb8" + - "\x88\xf1;\xea.\xa2\xc3\x0e\x9e]\x02a\x17\xb0\xa7\xf6" + - "\xd0U\xbb1\x88\xe1\xce\x90\xb7,\xfeW\xf6\xe0\xedg" + - "\xc2\xae\xec\xc7\xe8\xa8\xea\x9a\xbe\x15\xc0\x98\x14h\xcc\xd6" + - "\xeb*\xd4t\x95K?l|\x19\xc0H\x084\xe6\x9b" + - "\x0b\xc1]\xc8e\x97\x8b\x19;\x05;\x92\xa5x\xaa\xd4" + - "\xcc'\x92\xdbkcSN.\x93^\xe86W\xac\xfc" + - "6\xf2g\x90\x91\xcdz\x1d\xc4\xb5\xfcF\xb6i3\xdf" + - "nG\x8d\xf9Mc\xb4j\x1aG\xd84\xaa\xb7\xba," + - "F\xde\xcf&P\xbb\xd4\x0az\x9eq\x9cM\xa0vI" + - "\xf5\xb2i\x9c\xe0ij\x97T\xa0g\x1a\xa7\x99\xd7." + - "\xa9*<\xd38\xcb\xf3\xac\x11\xff&{\x95\xe2\x99\xc6" + - "\x05^\xf7\x1c\xf1\x8bl\x1a\xaag\x1a\xcfp\xfcE\xe2" + - "\xcf\xb1ih\x9ei\\b\xfe=\xe2W\xd84t\xcf" + - "4.3\x7f\x81\xf8O\xd84\x02\x9ei\xbc\xcc\xfc\xc7" + - "\xc4_c\xd3h\xf3L\xe3U\xe6\xeb\xc4\xdf`\xd3\x08" + - "z\xa6q\x8d\xf9\xeb\xc4\xdf&\xde\x19\x8ab'\xa2|" + - "\x8b\xf9\xaf\x88\xff\xbe\x95\x99\xa4\xb3)\xbb\xb4\xa5U<" + - "\x90\xb2\xf3\xb9\xe5\xb4S\x1d\x17\xac\x15\xea\xcb\xfe?n" + - "\xd3\x8a\xcas\x0d\xd4\x85U\xd8`\x0b6\xd4\x82\x0d\xb7" + - "`#-\xd8\x9e\x16lo=k\xd5\xd5\xfd\x15\xd8_" + - "\xad\xc0]\\\"7Q*\xfb+\xa5C\xbc\x8fKa" + - "'\xf1\xdd\xf5m\xebS\x9c\xfa~\xe2\xa3umk\x84" + - "\xc3w\x13\x1e\xabT\x1aU\xe08\xdb\xfe(\xf1\x18W" + - "Z\xb9mM1\x1f#>S\xdf\xb6\xa6\x99O\x12\x9f" + - "\xado[q\xe61\xe2\x89\xfa\xb6\xb5\x9f\xf9\x0c\xf1d" + - "}\xdb2\x98\xcf\x12\x9f\xabo[w2O\x10\x9f\xff" + - "_\xb5\xa1\xff\x7f\x9b\xf1\xf9\xf1\x81\xa2\x03e#\xeep" + - "]\x95\x9a\xc945\x83\x98@#\xa1`'^w\xa3" + - "\xdcb\xf6\xdf]\xb3\xb8N\xe5\x037\x8a\x0a@\xf8N" + - "\xa2I\x81\xc6\xbd\xf4p\xc9\x8f\xa59\xee\x04=\xb5W" + - "e\xf6=tY\x8d\xe9\xe4@\x14\xe8\xe7\xea\xfbq\xf9" + - "g\x8b\x0d\xd7\\\x01a\xe5\xb1\xa7\xf6&X\xf9\xf9\x86" + - "=r6\x9d\x0dx/\x1c[7\xac\xff\x04\x00\x00\xff" + - "\xffD\x0b\xa4\xe4" +const schema_b99093b7d2518300 = "x\xda\xdc\x98kl\x14\xd7\x15\xc7\xef\x99\xbb\xbb\xb3\xbb" + + "x\xbd\xb3\xbe\x83Ey\x08\xb0Le\xa8i\xb1\xbd6" + + "\x06\xe1\xdaNl\xc4F\xa6x2\x1bj*\x90\xd8\xb2" + + "\x13\xbc\xc1^/\xebYX*P\xa0\x04\x09$\xa0\xa0" + + "\x90\x86H$\x10\xe5\xa1\xa6i\x9b\xa6\x0f5\x0f\"\x15" + + "\x946 \x81\x14*P\xa0*mZ5\x89\xe2~H" + + "\x1f\x1f\xfa\xa2Lu\xce\xec\xee\xcc>\x00W\xaa\x14)" + + "\x92?\xec\xfd\xf9\xcc\x9ds\xe6\xde\xfb?\xff\x99e\x1f" + + "\xcb}R\x9b\xb7WfL\x8b{}\xd6W^\xf9\xbb" + + "\xb6n\xfd\x91o2\xad\x01\xbc\xb7\xf7k\xbf\xfa\xd9\xe3" + + "\xc7^\xf7\xc8\x8c\x89\x03|J\x1c\xe72c\x1dG\xf8" + + "r\x89\x81\xf5\xfc[?=\xfd\xd7\xaew\x0e\xb1H\x03" + + "\x14#\xbd\x12\x86^\xf6\x9e\x17\xd7\xbc\xf8\xeb\x8aw'" + + "\x03+\xdf\xf0\xdb\xe7\x16/\xcf\x1ce\x91\x06O1\x92" + + "AG\xcc\xd7\x04b\x83OfL\x8f\xfb8\xe8\x9b}" + + "\x120f]\xf8p\xdbf\xed\xd4\xc9\xe3\xd5\xf3\xee\xf3" + + "\xfdZ\x1c\xc1xq\xc8\x87\xf3\xb6\xce\xeb\xde~!z" + + "\xf8\x89\xea\xc8\xa5\xf2\x94X!\xe3\xafN\x19#\xbf\xfc" + + "\xc8\xbaVx\xcez\xb2:\xf2\x05\xf9\xbb\xe2\xfb\x14\xf9" + + "\x12E\x1e^\xf5\xe1\xd3\xd1\xc7\x1bOV\xe4\xba\xd4\xdf" + + "\x04\xa2\xc7\x8f\xb9v\xfb9\xe8\x03~\xcau\xc1\xb6\xee" + + "\xbf\xbc\xf6\xbb\xfbOV\xcf\x9b\xf3\xffA\xec\xc3x\xb1" + + "\xc7\x8f\xf3\xde\xff\xce\xad\x1f}\xab\xbd\xedLud[" + + "`J\xf4\x04\xf0\xd7\x8a\x00F.I\xde~\xb9\xef\xab" + + "\xfb\xcfTdp1\xd0\x00\xe2\x06\xc6\xe9W\x03\x1c\xf4" + + "\xf7\x03\x94\xc1\x89\xc1\x9e\xf7\xf6\xff9\xfabE\xf4\xbc" + + "`\x13\x88\xa5A\x8cn\x09r\xd0\xa3A\x8a>\xf4d" + + "\xc7\xb6\x97\xbf~\xf6E\\\xde`)\x0d\xc0\x9bk\xc1" + + ")\xb1\x09/\xe8\xd8\x10<\xeda`}\xbc\xb8\xeb\x99" + + "_\x1e\xdd\xf5\x83\x8a\x99;\xc3\xed \x06\xc38s_" + + "\x98\x83>\x14\xa6\x99O\xfdx`\xe3\xcd\x8f\xbc\xafV" + + "\xd7\x17\x0b\xbf!4\x8c\x17k\xc3X\xdf\xe97>\xf8" + + "\xf6'\x8b\xa471\x07(\xcf\xe1r\xf8_\xe2\x06\x85" + + "^\x0b\xbf\xc2\xc0\xd2\xdfz\xb6\xeb\xbcq\xeeb\xf5\xa4" + + "\xbb\x94)q@\xa1M\xa1\xe0\xa4\x9f\x7f\xf3\xc8\x17\x16" + + "\xae\xfa\xd3\xd5\xf2\xc2p\xbb\x8a\x99\x91)\xb10\x82\x85" + + "\xcd\x8b\xbc\x87\x85\xdd\x8a|\xf2\xcf\xa7\x07z\xaeU\x14" + + "\xb6\xa1\xb1\x09D\xaa\x11\x0bK6r\xd03\x8dT\xd8" + + "\x9c\xa3+\xbc\x7f\xe3\xe7\xff\x813\x07\xca\xd3\xcd5N" + + "\x89}xA\xc7\x9e\xc6/\xe1\xcc\xcd\xd9\x9e\x17.-" + + "\x1a\xf8wYi\x83 {\x18\x137g\x9f\x17\x1f\xcc" + + "\xc6\xcb~?\xfb#\x06\xd6\xc2kg\x8f\xad\xbd\xf0\x8c" + + "U\x91\xc5\xb99M \xae\xcc\xc1,.\xcd\xe1\xa0_" + + "\x9fCY$2\x99\xb1\xd4\x96\x84)\xa5&\xd2_\xdc" + + "\x92\xc8\xa43+\xe3\xab\x87\xb3F,<\x9e\xd8j\x0c" + + "\x03h-\xdc\xc3\x98\x07\x00D\x00\xeecL\xf7\x00\x07" + + "]\x01\x09\"\x00* \x0f\xc1\x03\x8c\xe9u\xc8g!" + + "\x97$\x15$\x001\x13\x960\xa6+\xc8\xe7\"\xe7\\" + + "\x05\x0e >G\x1c\x7f\xeb\x0b\x90{<*x\x00\xc4" + + "<\xe2\xb3\x907#\xf7zU\xf0\x02\x88\x85\xc4\xe7\"" + + "oA\xee\xf3\xa9\xe0\x03\x10\x8b\x88/@\xde\x8a\\\x96" + + "U\x90\x01\xc4b\xe2\xcd\xc8\x97!\xf7\xfbU\xf0\x03\x88" + + "\xa5\xc4[\x90G\x91\x07\x02*\x04\x00D\x1b\xf1V\xe4" + + "\xdd \xc1\xa3[F\x13\xa9tl\x00\xfcL\x02?\x03" + + "\xcb\x1cY\x973c\xc9\xf9\xd5\x86\xc1pi\xfc\xc5" + + "\xa5\x89,\xfe\x1acZ\x0b\x07-\xea\xacKdp%" + + "cZ\x1f\x07mH\x02\xcb\xa4U\x1dO0\xbe\xd5\x00" + + "\xc5\xd1c\x06\xa00\xa6x\x99\xa7\xd7\xcc\xafIL\x8e" + + "B\x88I\x10b,\x02A\xd7\xfe\xe0\xce\xfd\x93\xc6\xc3" + + "\x89\xdc\x98\xd9\xaf\xd3\x84\xb2\xbdM@q4\xc0\x9er" + + "8\x08,P>\x89\xab\x88\x01}(\x95\xde\xc6\x8d\xec" + + "4\xea\xd8\x8eu\x8cq\xd0\xf2\x12XI\xbd\xac\x8e\xd2" + + "\xf9,\xde\x94\xb3@$\x10\xb4\xff\x98t\xd7\x9a\xdc\xcf" + + "t$\x96\x1eJ\x85\xd3\xdb\xaa\x12z\xa4FB=\x98" + + "P7\x07m\x00\x1f\xecH,\x8d)1:1\xa08" + + ":TH\x09\xa0p\xe3\xbb&\x03\xc5d\xc2\xb8\xc2\x15" + + "I41\xa65s\xd0\xfa\\I\xf4/aL[\xc5" + + "A\x1b\x91@\xde\x91JC=\x83a\x8e\x0bQ\x92L" + + "\x06P\x8f\xf7\x91\xfb\xa4\xf0\x8e\x89\x9c\xe9\x84\x94\x04\xa5" + + "\x142\x0cp\x97\xc5\x1e\xa8\\\xec{<\xf7\x9aK\x9e" + + "0\x13\xba)Od\x8d\x8a\xea\x1e\xa8\xf1\x88\xf7<\xc8" + + "\x98\xb6\x9b\x83v\x90\xd6\x1c7\x8b\x91\xc5\x03\xa88}" + + "\xdc\xbeu\x9f\xa4y\x82<\xd2\x1ft'\xc0\xad\xc9\xd4" + + "\xd6t\xc2\xcce\x19\x18\xf7\\\xfd\xf5\x89\xb1\x9c\xa1\x87" + + "\xcd\xea\xd4jm\xc7M\xb8\xfa#\x1c\xb4\xa4\x04\xd6\x8e" + + "\xf2\xedX\x12\xf5\xc2c\x09\x00\xf3\xdf{\xedk=o" + + "\xaa\x18\xb2\xf6\xd3\x9e^\xc9w\x9en}\xe5\xf2\xdd1" + + "\xcfZ\x8f\xc7>\xe9\xa5\x86\x10/5\x84\x1e\x09\x1bB" + + "\xb7\x84\xbeEB\xc1.4\x84~\xa9\x9d1}\x15\xf2" + + "5\xc8\xa1\xd0\x10\x06%l\x14\x03\xc8\x87%\x09\x10c" + + "?XK\xe1k\x10\xc7\xa5B\x9f\xc0~\xf0\x10\x85\xc7" + + "\x91o\x96\x0a}\x02\xfb\xc1&\xa9\x891}\x04y\x12" + + "\xb9\xcfk\xf7\x83\x84\xb4\x921}#\xf2Q\xe4\xb2\xcf" + + "\xee\x07\x06\xf1\xcd\xc8\xc7\x90\xfbe\xbb\x1f\xa4\x88'\x91" + + "g\x90\x07\xfcv?\x18'>\x8a\xdcD\x1e\x0c\xa8\x10" + + "\x04\x10\xdb\x89\x8f!\xcf#\x9f\x11Ta\x06\x80\xc8\x11" + + "\xcf \xdf\x8d\xbcn\x86\x0au\x00b\x17q\x13\xf9^" + + "\xe4\xa1:\x15B\x00b\x8f\x84\xfd&\x8f\xfc1\xe4\xf5" + + "!\x15\xea\x01\xc4>\xe2\xbb\x91\x1fD\x1e\xaeW!\x0c" + + " \x0e\x10\xdf\x8b\xfc0r%\xac\x82\x02 \x0e\x11\x7f" + + "\x0c\xf91\xe4\x11E\xc5]*\x8e\x10?\x88\xfc\x04\xf2" + + "\x86\x88\x0a\x0d\x00\xe28\xf1\xc3\xc8O\"\x17\x0d*\x08" + + "\x00\xf1\x04\xf1c\xc8O!W\x85\x8a\x0b)\x9e\"~" + + "\x02\xf9\x19\xa9\xba/\xce\xdf\x81\xc7\xe6n]r\xfe\xc4" + + "\xce\xb4\x91-\xdf\xf3\xa9\xc9\xc9\x9c\x91\xec7]a\xb2" + + "\x91\xcf\x14\x7f\xf7\xd2\xa4m\xe5\xc3\xf6\xf2aG\xf90" + + "Z>\xec,\x1fv\x95\x0f\x97\x7fj\x1d\xbc\xfaT\xc6" + + "\xed\xce1?6^:\x97w\xec\x1d\xd5}\xa2\xd7\xee" + + "Z\xd3\x10\xac\x18j\xe9\x1a\x0eZ\xbc\xd0\xaeP[\x18" + + "7\xb2\xa08\xaf:\x8e\xb2\x00DBA\xfb\xb6\xf0?" + + "\xc9\xa8]O\xafQ\xacG\xab+\xe55x\x9fcE" + + "\x9c\xbc\xb2N^\x05\x89\x88h\xdf`L\x1b\xe6\xa0m" + + "\xaca\xc4\xb6L\xa4's\xe3F\x92\xcd\x8f\xe7c\xc9" + + "|5\xef\x8dO\xaf\xdd\xf7\x9b\x13\xe3\xa9-a}g" + + "\"3\x8d\xe7\x97@\xc1\xdf\xc8A\x1b\x95\xc0J\x94\x0b" + + "\xfe\x1dM\xcft\x9d\xc7@\xb9\xb8\x8e\x94\xc4u\x03\x89" + + "kI\xfd\x8af{\x13\x89\xa5#~\x12\xd8\xdaj\x90" + + "X:b\xe6+\x88\xeb8M\xe3\x88\x19\x07[\\\xb7" + + "\x13w\xc4\xcc\xc3mq\xddE\xf38\xe2\xe4\x95lq" + + "\xddG\xf7u\xc4I\xf6\xd8\xe2z\x88\xe2\x1d\xb1\xf1{" + + "mq=N\xdc\x11\x95\x80\xcf\x16\xd7\xa7\x88\x9fD\xfe" + + "<\x89\xabl\x8b\xeb\xb3\xc4\xcf \xff\x1e\x89\xab\xdf\x16" + + "\xd7\x97\x88\x7f\x07\xf9OH\\\x03\xb6\xb8\xbeJ\xfc\x87" + + "\xc8\xcf\x92\xb8\x06mq}\x9d\xf8k\xc8\xdf&q\x9d" + + "a\x8b\xeb9\x12\xb3\xb3\xc8/\x90\xb8\xd6\xd9\xe2\xfa\x0b" + + "\xe2?G~\x89\xc45d\x8b\xebE\xe2o#\x7f\x97" + + "\xc4\xb5\xde\x16\xd7\xcb\xc4/ \xbfJ\xe2\x1a\xb6\xc5\xf5" + + "\x0a\xf1K\xc8\xaf\x93\xb8*\xb6\xb8^#\xfe.\xf2\xdf" + + "\x90\xb8Flq\xbdA\xfc*\xf2\xf7\x91\xcflPa" + + "&\x80\xb8I\xfc:\xf2?\xd6\x12\xddT:i\xe4\xef" + + ")\xa9\x8f&\x8d\xcc\xc4d\xca,\x8d\xb3\x89\x9d\xe8\xbf" + + "\xca/\x9c\xa6d\x17\xe6js\x85\x15Y{\x0d\xd6Q" + + "\x83Ek\xb0\xce\x1a\xac\xab\x06[\xfei\xbew\xd5P" + + "\xed\xd5\x15^\xaa\xc6\xabTM\x93Y~\xd0\x87J\x07" + + "\xdd\x8b'\xf1A\\\xf0\xba\xe2\x01\xa5\xb7m:p\x1e" + + "\xe4\x8a\xdbD\x85h\x83\xd7!\x9f\xe52Q3)\\" + + "A<\xb7x\x9e\xe9\xa5\x9aL\xc8,\xe4\xcdt\x9e\x0b" + + "&j!\xf1\xb9\xc8[\xdc&j\x11\xf1\x05\xc8[\xdd" + + "&j1\xf1f\xe4\xcb\xdc&j)\xf1\x16\xe4Q\xb7" + + "\x89j#\xde\x8a\xbc\xdbm\xa2:\x89/C\xbe\xcam" + + "\xa2V\xd0\xbe\x8f\"\xefs\x9b\xa8\x1e\xe2\x8e\xc7,\x9a" + + "\xa8~\xe2\x8e\xc7,\x9a\xa8A\xe2}\xc8\x87\xdc&*" + + "F\xbc\xe4=K&j-q\xc7|\x16M\x94F|" + + "\x08\xf9\x88\xdbD=D|\x18\xf9\xc6\xff\x97)\xfa\xec" + + "\x9a\x9e2w\xb0.g\xd2g\x0b\x85{\xea,\xcb\x83" + + "\xd6&\x81\xd6d3\x07mL\x82\x10\xdc\xb6T2<" + + ")\xec\xc2\xa3\x1c4S\x82\x90\xf4\x1fK\x05\x89\xb1\xc8" + + "v\xa4\x19\x0e\xdan\x09B\xfc\x96\xa5\x02g,\xb2\xab" + + "\x9d1\xcd\xe4\xa0\xed\xc5\x17Ez\xc5\x9c \xb7\xa28" + + "\x1fQ\xe9`\x82E\xcfB7'\x18\xcf\xe2\xbfK_" + + "N\x0b\xffN\x90)\xd0w2\x9e\xc8\x80\xe2|#\xb4" + + "\xff=\xdf\xcc\xaf6\xf0\xb2\xd2\xe7\xe9\xe2ew\xf5w" + + "C\xa9\xb4l\x7fT\xb8\xb7\xd9\xfao\x00\x00\x00\xff\xff" + + "Z\xbe\x9cC" func init() { schemas.Register(schema_b99093b7d2518300, + 0x828d564f51f7af4e, + 0x89c736f29fb5bda4, 0x8e703729a3de1278, 0x91989c51606be6c8, 0x958c34c871381d2c, @@ -1640,14 +2145,19 @@ func init() { 0xbb0225ef96e5ba9f, 0xc9c165c236a1bd53, 0xd4eb3c212b8dbb26, + 0xd53d449df9ef11fc, 0xf8c203f305398e1b, 0xfb4425cca53d7224, 0xff9ec84d90bcd521) } var x_b99093b7d2518300 = []byte{ - 0, 0, 0, 0, 13, 0, 0, 0, - 0, 0, 0, 0, 6, 0, 3, 0, + 0, 0, 0, 0, 17, 0, 0, 0, + 0, 0, 0, 0, 10, 0, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1660,10 +2170,14 @@ var x_b99093b7d2518300 = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, - 4, 0, 0, 0, 6, 0, 3, 0, - 49, 0, 0, 0, 10, 0, 0, 0, + 4, 0, 0, 0, 10, 0, 3, 0, + 65, 0, 0, 0, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1677,8 +2191,12 @@ var x_b99093b7d2518300 = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 8, 0, 0, 0, - 0, 0, 0, 0, 5, 0, 1, 0, + 0, 0, 0, 0, 12, 0, 0, 0, + 0, 0, 0, 0, 9, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1686,8 +2204,12 @@ var x_b99093b7d2518300 = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 9, 0, 0, 0, - 0, 0, 0, 0, 6, 0, 1, 0, + 0, 0, 0, 0, 13, 0, 0, 0, + 0, 0, 0, 0, 10, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1696,6 +2218,13 @@ var x_b99093b7d2518300 = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1709,8 +2238,12 @@ var x_b99093b7d2518300 = []byte{ 1, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 13, 0, 0, 0, - 0, 0, 0, 0, 6, 0, 3, 0, + 0, 0, 0, 0, 17, 0, 0, 0, + 0, 0, 0, 0, 10, 0, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1723,8 +2256,12 @@ var x_b99093b7d2518300 = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 13, 0, 0, 0, - 0, 0, 0, 0, 6, 0, 3, 0, + 0, 0, 0, 0, 17, 0, 0, 0, + 0, 0, 0, 0, 10, 0, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1737,10 +2274,14 @@ var x_b99093b7d2518300 = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, - 4, 0, 0, 0, 6, 0, 3, 0, - 49, 0, 0, 0, 10, 0, 0, 0, + 4, 0, 0, 0, 10, 0, 3, 0, + 65, 0, 0, 0, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1754,10 +2295,14 @@ var x_b99093b7d2518300 = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, - 4, 0, 0, 0, 6, 0, 3, 0, - 49, 0, 0, 0, 10, 0, 0, 0, + 4, 0, 0, 0, 10, 0, 3, 0, + 65, 0, 0, 0, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1771,8 +2316,12 @@ var x_b99093b7d2518300 = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 8, 0, 0, 0, - 0, 0, 0, 0, 5, 0, 1, 0, + 0, 0, 0, 0, 12, 0, 0, 0, + 0, 0, 0, 0, 9, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1780,8 +2329,12 @@ var x_b99093b7d2518300 = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 8, 0, 0, 0, - 0, 0, 0, 0, 5, 0, 1, 0, + 0, 0, 0, 0, 12, 0, 0, 0, + 0, 0, 0, 0, 9, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1789,8 +2342,12 @@ var x_b99093b7d2518300 = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 9, 0, 0, 0, - 0, 0, 0, 0, 6, 0, 1, 0, + 0, 0, 0, 0, 13, 0, 0, 0, + 0, 0, 0, 0, 10, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1799,8 +2356,12 @@ var x_b99093b7d2518300 = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 9, 0, 0, 0, - 0, 0, 0, 0, 6, 0, 1, 0, + 0, 0, 0, 0, 13, 0, 0, 0, + 0, 0, 0, 0, 10, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1809,6 +2370,20 @@ var x_b99093b7d2518300 = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/application/objs/capn/gen.go b/application/objs/capn/gen.go index 4e6fc27c..914155d9 100644 --- a/application/objs/capn/gen.go +++ b/application/objs/capn/gen.go @@ -7,4 +7,4 @@ import ( // Import check to ensure capnp is installed. var _ = capnp.Tag -//go:generate capnp compile -I /home/et3p/go/src/zombiezen.com/go/capnproto2/std -ogo application.capnp +//go:generate capnp compile -I $GOPATH/src/zombiezen.com/go/capnproto2/std -ogo application.capnp diff --git a/application/objs/constants.go b/application/objs/constants.go index deead08a..63e39031 100644 --- a/application/objs/constants.go +++ b/application/objs/constants.go @@ -18,6 +18,8 @@ const ( DataStoreSVA ) +// SignerRole is the defined type utilized for designation for signers +// in AtomicSwap objects type SignerRole uint8 const ( diff --git a/application/objs/ds.go b/application/objs/ds.go index 8d5d0d32..a5690e08 100644 --- a/application/objs/ds.go +++ b/application/objs/ds.go @@ -4,6 +4,7 @@ import ( mdefs "github.com/MadBase/MadNet/application/objs/capn" "github.com/MadBase/MadNet/application/objs/datastore" "github.com/MadBase/MadNet/application/objs/uint256" + "github.com/MadBase/MadNet/application/wrapper" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/utils" @@ -207,6 +208,14 @@ func (b *DataStore) GenericOwner() (*Owner, error) { return onr, nil } +// IsExpired returns true if the datastore is free for garbage collection +func (b *DataStore) IsExpired(currentHeight uint32) (bool, error) { + if b == nil { + return false, errorz.ErrInvalid{}.New("not initialized") + } + return b.DSLinker.IsExpired(currentHeight) +} + // EpochOfExpiration returns the epoch in which the datastore may be garbage // collected func (b *DataStore) EpochOfExpiration() (uint32, error) { @@ -232,6 +241,86 @@ func (b *DataStore) Value() (*uint256.Uint256, error) { return b.DSLinker.Value() } +// Fee returns the fee stored in the object at the time of creation +func (b *DataStore) Fee() (*uint256.Uint256, error) { + if b == nil { + return nil, errorz.ErrInvalid{}.New("not initialized") + } + return b.DSLinker.Fee() +} + +// ValuePlusFee returns the Value of the object with the associated fee +// +// Given a DataStore with data of size dataSize bytes which are to be stored +// for of numEpochs, the value is +// +// value := (dataSize + BaseDatasizeConst) * (numEpochs + 2) +// +// Here, BaseDatasizeConst is a constant which includes the base cost of the +// actual storage object. Furthermore, we include 2 additional epochs into +// the standard cost for initial burning as well as miner reward. +// +// Given the base cost, we also want to be able to include additional fees. +// These fees would be on a per-epoch basis. Thus, we have the form +// +// valuePlusFee := (dataSize + BaseDatasizeConst + perEpochFee) * (numEpochs + 2) +// = (dataSize + BaseDatasizeConst) * (numEpochs + 2) +// + perEpochFee * (numEpochs + 2) +// = value + fee +// +// with +// +// fee := perEpochFee * (numEpochs + 2) +// +// The fee is burned at creation. +func (b *DataStore) ValuePlusFee() (*uint256.Uint256, error) { + value, err := b.Value() + if err != nil { + return nil, err + } + fee, err := b.Fee() + if err != nil { + return nil, err + } + total, err := new(uint256.Uint256).Add(value, fee) + if err != nil { + return nil, err + } + return total, nil +} + +// ValidateFee validates the fee of the datastore at the time of creation +func (b *DataStore) ValidateFee(storage *wrapper.Storage) error { + // Get Fee + fee, err := b.Fee() + if err != nil { + return err + } + // Compute correct fee value + value, err := b.Value() + if err != nil { + return err + } + perEpochFee, err := storage.GetDataStoreEpochFee() + if err != nil { + return err + } + dataSize := uint32(len(b.DSLinker.DSPreImage.RawData)) + numEpochs32, err := NumEpochsEquation(dataSize, value) + if err != nil { + return err + } + totalEpochs, _ := new(uint256.Uint256).FromUint64(uint64(numEpochs32) + 2) + feeTrue, err := new(uint256.Uint256).Mul(perEpochFee, totalEpochs) + if err != nil { + return err + } + if fee.Cmp(feeTrue) != 0 { + return errorz.ErrInvalid{}.New("invalid fee") + } + return nil +} + // ValidatePreSignature validates the signature of the datastore at the time of // creation func (b *DataStore) ValidatePreSignature() error { diff --git a/application/objs/ds_test.go b/application/objs/ds_test.go index 2cf17450..05e67873 100644 --- a/application/objs/ds_test.go +++ b/application/objs/ds_test.go @@ -2,6 +2,7 @@ package objs import ( "bytes" + "math/big" "testing" "github.com/MadBase/MadNet/application/objs/uint256" @@ -50,6 +51,7 @@ func makeDataStoreGood(secpPrivk []byte) *DataStore { RawData: rawdata, TXOutIdx: txOutIdx, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } dsl := &DSLinker{ DSPreImage: dsp, @@ -93,6 +95,7 @@ func TestDataStoreGood(t *testing.T) { RawData: rawdata, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) dsl := &DSLinker{ @@ -206,6 +209,7 @@ func TestDataStoreBad2(t *testing.T) { RawData: rawdata, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) dsl := &DSLinker{ @@ -257,6 +261,7 @@ func TestOwnerSig(t *testing.T) { RawData: rawdata, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) dsl := &DSLinker{ @@ -319,7 +324,10 @@ func TestOwnerSig(t *testing.T) { { bnsigner := &crypto.BNSigner{} - bnsigner.SetPrivk(crypto.Hasher([]byte("d"))) + err := bnsigner.SetPrivk(crypto.Hasher([]byte("d"))) + if err != nil { + t.Fatal(err) + } txIn, err := ds2.MakeTxIn() if err != nil { t.Fatal(err) @@ -349,7 +357,10 @@ func TestOwnerSig2(t *testing.T) { } ownerSigner := &crypto.BNSigner{} - ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))) + err = ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))) + if err != nil { + t.Fatal(err) + } ownerPubk, err := ownerSigner.Pubkey() if err != nil { @@ -367,6 +378,7 @@ func TestOwnerSig2(t *testing.T) { RawData: rawdata, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) dsl := &DSLinker{ @@ -429,7 +441,10 @@ func TestOwnerSig2(t *testing.T) { { bnsigner := &crypto.BNSigner{} - bnsigner.SetPrivk(crypto.Hasher([]byte("d"))) + err := bnsigner.SetPrivk(crypto.Hasher([]byte("d"))) + if err != nil { + t.Fatal(err) + } txIn, err := ds2.MakeTxIn() if err != nil { t.Fatal(err) @@ -460,7 +475,10 @@ func TestDeposit(t *testing.T) { } ownerSigner := &crypto.BNSigner{} - ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))) + err = ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))) + if err != nil { + t.Fatal(err) + } ownerPubk, err := ownerSigner.Pubkey() if err != nil { @@ -478,6 +496,7 @@ func TestDeposit(t *testing.T) { RawData: rawdata, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) dsl := &DSLinker{ @@ -820,6 +839,147 @@ func TestDSValueCall(t *testing.T) { if err == nil { t.Fatal("Should have raised an error (2)") } + + // Prep DataStore + numEpochs := uint32(3) + dataSize := uint32(1) + data := make([]byte, int(dataSize)) + ds.DSLinker = &DSLinker{} + ds.DSLinker.DSPreImage = &DSPreImage{} + ds.DSLinker.DSPreImage.RawData = data + deposit32 := (constants.BaseDatasizeConst + dataSize) * (numEpochs + 2) + deposit, err := new(uint256.Uint256).FromUint64(uint64(deposit32)) + if err != nil { + t.Fatal(err) + } + ds.DSLinker.DSPreImage.Deposit = deposit + + // Check value and deposit agree + value, err := ds.Value() + if err != nil { + t.Fatal(err) + } + if value.Cmp(deposit) != 0 { + t.Fatal("true value and deposit do not agree") + } +} + +func TestDSValuePlusFeeCallGood(t *testing.T) { + // Test for failures due to not being initialized + utxo := &TXOut{} + _, err := utxo.dataStore.ValuePlusFee() + if err == nil { + t.Fatal("Should have raised an error (1)") + } + ds := &DataStore{} + _, err = ds.ValuePlusFee() + if err == nil { + t.Fatal("Should have raised an error (2)") + } + + // Prep DataStore + numEpochs := uint32(3) + dataSize := uint32(1) + data := make([]byte, int(dataSize)) + ds.DSLinker = &DSLinker{} + ds.DSLinker.DSPreImage = &DSPreImage{} + ds.DSLinker.DSPreImage.RawData = data + deposit32 := (constants.BaseDatasizeConst + dataSize) * (numEpochs + 2) + deposit, err := new(uint256.Uint256).FromUint64(uint64(deposit32)) + if err != nil { + t.Fatal(err) + } + ds.DSLinker.DSPreImage.Deposit = deposit + ds.DSLinker.DSPreImage.Fee = new(uint256.Uint256) + + // Check value and valuePlusFee agree with fee == 0 + value, err := ds.Value() + if err != nil { + t.Fatal(err) + } + valuePlusFee, err := ds.ValuePlusFee() + if err != nil { + t.Fatal(err) + } + if value.Cmp(valuePlusFee) != 0 { + t.Fatal("true value and valuePlusFee do not agree (1)") + } + + // Prep for 1000 fee + perEpochFee := uint32(1000) + + fee32 := perEpochFee * (numEpochs + 2) + fee, err := new(uint256.Uint256).FromUint64(uint64(fee32)) + if err != nil { + t.Fatal(err) + } + ds.DSLinker.DSPreImage.Fee = fee.Clone() + + // Check value and valuePlusFee agree with fee != 0 + valuePlusFee, err = ds.ValuePlusFee() + if err != nil { + t.Fatal(err) + } + vpfTrue, err := new(uint256.Uint256).Add(value, fee) + if err != nil { + t.Fatal(err) + } + if vpfTrue.Cmp(valuePlusFee) != 0 { + t.Fatal("true value and valuePlusFee do not agree (2)") + } + if value.Eq(valuePlusFee) { + t.Fatal("value and valuePlusFee should not be equal") + } +} + +func TestDSValuePlusFeeCallBad1(t *testing.T) { + // Test for failures due to not being initialized + utxo := &TXOut{} + _, err := utxo.dataStore.ValuePlusFee() + if err == nil { + t.Fatal("Should have raised an error (1)") + } + ds := &DataStore{} + _, err = ds.ValuePlusFee() + if err == nil { + t.Fatal("Should have raised an error (2)") + } +} + +func TestDSValuePlusFeeCallBad2(t *testing.T) { + // Test for failure due to large of values + dataSize32 := constants.MaxDataStoreSize + numEpochs32 := constants.MaxUint32 + deposit64 := (uint64(constants.BaseDatasizeConst) + uint64(dataSize32)) * (2 + uint64(numEpochs32)) + deposit, err := new(uint256.Uint256).FromUint64(deposit64) + if err != nil { + t.Fatal(err) + } + ds := &DataStore{} + ds.DSLinker = &DSLinker{} + ds.DSLinker.DSPreImage = &DSPreImage{} + ds.DSLinker.DSPreImage.RawData = make([]byte, dataSize32) + ds.DSLinker.DSPreImage.Deposit = deposit + + _, err = ds.ValuePlusFee() + if err == nil { + t.Fatal("Should have raised an error") + } +} + +func TestDSValuePlusFeeCallBad3(t *testing.T) { + // Test for failure due to invalid Deposit + dataSize32 := uint32(1) + ds := &DataStore{} + ds.DSLinker = &DSLinker{} + ds.DSLinker.DSPreImage = &DSPreImage{} + ds.DSLinker.DSPreImage.RawData = make([]byte, dataSize32) + ds.DSLinker.DSPreImage.Deposit = new(uint256.Uint256).SetOne() + + _, err := ds.ValuePlusFee() + if err == nil { + t.Fatal("Should have raised an error") + } } func TestDSValidatePreSignature(t *testing.T) { @@ -943,3 +1103,80 @@ func TestDSMakeTxIn(t *testing.T) { t.Fatal("Should have raised an error (4)") } } + +func TestDSIsExpired(t *testing.T) { + currentHeight := uint32(1) + utxo := &TXOut{} + _, err := utxo.dataStore.IsExpired(currentHeight) + if err == nil { + t.Fatal("Should have raised an error (1)") + } + + ds := &DataStore{} + _, err = ds.IsExpired(currentHeight) + if err == nil { + t.Fatal("Should have raised an error (2)") + } +} + +func TestDSValidateFee(t *testing.T) { + msg := makeMockStorageGetter() + storage := makeStorage(msg) + + utxo := &TXOut{} + err := utxo.dataStore.ValidateFee(storage) + if err == nil { + t.Fatal("Should have raised an error (1)") + } + + ds := &DataStore{} + err = ds.ValidateFee(storage) + if err == nil { + t.Fatal("Should have raised an error (2)") + } + + ds.DSLinker = &DSLinker{} + ds.DSLinker.DSPreImage = &DSPreImage{} + ds.DSLinker.DSPreImage.RawData = make([]byte, 0) + ds.DSLinker.DSPreImage.Fee = new(uint256.Uint256).SetZero() + err = ds.ValidateFee(storage) + if err == nil { + t.Fatal("Should have raised an error (3)") + } + + // Store 1 byte for 1 epoch + rawData := make([]byte, 1) + numEpochs := uint32(1) + ds.DSLinker.DSPreImage.RawData = rawData + deposit32 := (constants.BaseDatasizeConst + uint32(len(rawData))) * (numEpochs + 2) + deposit, err := new(uint256.Uint256).FromUint64(uint64(deposit32)) + if err != nil { + t.Fatal(err) + } + ds.DSLinker.DSPreImage.Deposit = deposit + err = ds.ValidateFee(storage) + if err != nil { + t.Fatal(err) + } + + // Set perEpochFee to 1, raising an error + perEpochFee32 := uint32(1) + msg.SetDataStoreEpochFee(big.NewInt(int64(perEpochFee32))) + storage = makeStorage(msg) + err = ds.ValidateFee(storage) + if err == nil { + t.Fatal("Should have raised an error (4)") + } + + // Correct Fee value + fee32 := (perEpochFee32) * (numEpochs + 2) + fee, err := new(uint256.Uint256).FromUint64(uint64(fee32)) + if err != nil { + t.Fatal(err) + } + ds.DSLinker.DSPreImage.Fee = fee + err = ds.ValidateFee(storage) + if err != nil { + t.Fatal(err) + } +} diff --git a/application/objs/dsl.go b/application/objs/dsl.go index ac5ec7c9..2f4771ad 100644 --- a/application/objs/dsl.go +++ b/application/objs/dsl.go @@ -169,6 +169,14 @@ func (b *DSLinker) SetTXOutIdx(idx uint32) error { return nil } +// IsExpired returns true if the datastore is free for garbage collection +func (b *DSLinker) IsExpired(currentHeight uint32) (bool, error) { + if b == nil { + return false, errorz.ErrInvalid{}.New("not initialized") + } + return b.DSPreImage.IsExpired(currentHeight) +} + // EpochOfExpiration returns the epoch in which the datastore may be garbage // collected func (b *DSLinker) EpochOfExpiration() (uint32, error) { @@ -194,6 +202,14 @@ func (b *DSLinker) Value() (*uint256.Uint256, error) { return b.DSPreImage.Value() } +// Fee returns the fee stored in the object at the time of creation +func (b *DSLinker) Fee() (*uint256.Uint256, error) { + if b == nil || b.DSPreImage == nil || b.DSPreImage.Fee == nil { + return nil, errorz.ErrInvalid{}.New("not initialized") + } + return b.DSPreImage.Fee.Clone(), nil +} + // ValidateSignature validates the signature of the datastore at the time of // consumption func (b *DSLinker) ValidateSignature(currentHeight uint32, msg []byte, sig *DataStoreSignature) error { diff --git a/application/objs/dsl_test.go b/application/objs/dsl_test.go index c6dc4898..64d24aaf 100644 --- a/application/objs/dsl_test.go +++ b/application/objs/dsl_test.go @@ -42,6 +42,7 @@ func TestDSLinkerGood(t *testing.T) { RawData: rawdata, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) dsl := &DSLinker{ @@ -145,6 +146,7 @@ func TestDSLinkerBad2(t *testing.T) { RawData: rawdata, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, 31) // Invalid TxHash dsl := &DSLinker{ @@ -397,6 +399,20 @@ func TestDSLinkerSetTXOutIdx(t *testing.T) { } } +func TestDSLinkerIsExpired(t *testing.T) { + ds := &DataStore{} + currentHeight := uint32(1) + _, err := ds.DSLinker.IsExpired(currentHeight) + if err == nil { + t.Fatal("Should have raised error (1)") + } + dsl := &DSLinker{} + _, err = dsl.IsExpired(currentHeight) + if err == nil { + t.Fatal("Should have raised error (2)") + } +} + func TestDSLinkerEpochOfExpiration(t *testing.T) { ds := &DataStore{} _, err := ds.DSLinker.EpochOfExpiration() diff --git a/application/objs/dsowner_test.go b/application/objs/dsowner_test.go index 6eff09c8..73a4d827 100644 --- a/application/objs/dsowner_test.go +++ b/application/objs/dsowner_test.go @@ -264,7 +264,10 @@ func TestDSOwnerValidateSignatureBN(t *testing.T) { privk := make([]byte, 32) privk[0] = 1 privk[31] = 1 - signer.SetPrivk(privk) + err = signer.SetPrivk(privk) + if err != nil { + t.Fatal(err) + } pk, err := signer.Pubkey() if err != nil { t.Fatal(err) diff --git a/application/objs/dspi.go b/application/objs/dspi.go index 633a122f..c511fb6d 100644 --- a/application/objs/dspi.go +++ b/application/objs/dspi.go @@ -20,6 +20,7 @@ type DSPreImage struct { RawData []byte TXOutIdx uint32 Owner *DataStoreOwner + Fee *uint256.Uint256 } // UnmarshalBinary takes a byte slice and returns the corresponding @@ -75,6 +76,20 @@ func (b *DSPreImage) UnmarshalCapn(bc mdefs.DSPreImage) error { return err } b.Owner = owner + fObj := &uint256.Uint256{} + u32array[0] = bc.Fee0() + u32array[1] = bc.Fee1() + u32array[2] = bc.Fee2() + u32array[3] = bc.Fee3() + u32array[4] = bc.Fee4() + u32array[5] = bc.Fee5() + u32array[6] = bc.Fee6() + u32array[7] = bc.Fee7() + err = fObj.FromUint32Array(u32array) + if err != nil { + return err + } + b.Fee = fObj // protects against zero bytes errors in equations return b.ValidateDeposit() } @@ -132,6 +147,18 @@ func (b *DSPreImage) MarshalCapn(seg *capnp.Segment) (mdefs.DSPreImage, error) { bc.SetDeposit5(u32array[5]) bc.SetDeposit6(u32array[6]) bc.SetDeposit7(u32array[7]) + u32array, err = b.Fee.ToUint32Array() + if err != nil { + return bc, err + } + bc.SetFee0(u32array[0]) + bc.SetFee1(u32array[1]) + bc.SetFee2(u32array[2]) + bc.SetFee3(u32array[3]) + bc.SetFee4(u32array[4]) + bc.SetFee5(u32array[5]) + bc.SetFee6(u32array[6]) + bc.SetFee7(u32array[7]) bc.SetTXOutIdx(b.TXOutIdx) return bc, nil } @@ -165,12 +192,12 @@ func (b *DSPreImage) RemainingValue(currentHeight uint32) (*uint256.Uint256, err if err != nil { return nil, err } - return result.Clone(), nil + return result, nil } // Value returns the value stored in the object at the time of creation func (b *DSPreImage) Value() (*uint256.Uint256, error) { - if b == nil || b.Deposit == nil || b.Deposit.Eq(uint256.Zero()) { + if b == nil || b.Deposit == nil || b.Deposit.IsZero() { return nil, errorz.ErrInvalid{}.New("not initialized") } return b.Deposit.Clone(), nil @@ -216,7 +243,7 @@ func (b *DSPreImage) IsExpired(currentHeight uint32) (bool, error) { // EpochOfExpiration returns the epoch in which the datastore may be garbage // collected func (b *DSPreImage) EpochOfExpiration() (uint32, error) { - if b == nil || b.Deposit == nil || b.Deposit.Eq(uint256.Zero()) || len(b.RawData) == 0 { + if b == nil || b.Deposit == nil || b.Deposit.IsZero() || len(b.RawData) == 0 { return 0, errorz.ErrInvalid{}.New("not initialized") } dataSize := uint32(len(b.RawData)) @@ -230,7 +257,7 @@ func (b *DSPreImage) EpochOfExpiration() (uint32, error) { // ValidateDeposit validates the deposit func (b *DSPreImage) ValidateDeposit() error { - if b == nil || b.Deposit == nil { + if b == nil || b.Deposit == nil || b.Deposit.IsZero() || b.Fee == nil { return errorz.ErrInvalid{}.New("not initialized") } if b.ChainID == 0 { @@ -282,14 +309,8 @@ func RewardDepositEquation(depositOrig *uint256.Uint256, dataSize32 uint32, epoc // This ensures // epochDiff == epochFinal - epochInitial >= 0 epochDiff := epochFinal - epochInitial - dataSize, err := new(uint256.Uint256).FromUint64(uint64(dataSize32)) - if err != nil { - return nil, err - } - epochCost, err := new(uint256.Uint256).Add(uint256.BaseDatasizeConst(), dataSize) - if err != nil { - return nil, err - } + dataSize, _ := new(uint256.Uint256).FromUint64(uint64(dataSize32)) + epochCost, _ := new(uint256.Uint256).Add(uint256.BaseDatasizeConst(), dataSize) numEpochs, err := NumEpochsEquation(dataSize32, depositClone) if err != nil { return nil, err @@ -306,10 +327,7 @@ func RewardDepositEquation(depositOrig *uint256.Uint256, dataSize32 uint32, epoc if err != nil { return nil, err } - tmp3, err := new(uint256.Uint256).Mul(uint256.Two(), epochCost) - if err != nil { - return nil, err - } + tmp3, _ := new(uint256.Uint256).Mul(uint256.Two(), epochCost) remainder, err := new(uint256.Uint256).Add(tmp, tmp3) if err != nil { return nil, err @@ -361,37 +379,16 @@ func BaseDepositEquation(dataSize32 uint32, numEpochs32 uint32) (*uint256.Uint25 // dataSize is too large so we do not perform any checks return nil, errorz.ErrInvalid{}.New("Error in BaseDepositEquation: dataSize is too large") } - dataSize, err := new(uint256.Uint256).FromUint64(uint64(dataSize32)) - if err != nil { - return nil, err - } + dataSize, _ := new(uint256.Uint256).FromUint64(uint64(dataSize32)) // tmp1 < 2^31 - epochCost, err := new(uint256.Uint256).Add(dataSize, uint256.BaseDatasizeConst()) - if err != nil { - return nil, err - } + epochCost, _ := new(uint256.Uint256).Add(dataSize, uint256.BaseDatasizeConst()) // We have // tmp2 < 2^33 - numEpochs, err := new(uint256.Uint256).FromUint64(uint64(numEpochs32)) - if err != nil { - return nil, err - } - totalEpochs, err := new(uint256.Uint256).Add(uint256.Two(), numEpochs) - if err != nil { - return nil, err - } + numEpochs, _ := new(uint256.Uint256).FromUint64(uint64(numEpochs32)) + totalEpochs, _ := new(uint256.Uint256).Add(uint256.Two(), numEpochs) // The above ensures no overflow occurs in the following multiplication, as // deposit == tmp1*tmp2 < 2^64 - depositUint256, err := new(uint256.Uint256).Mul(epochCost, totalEpochs) - if err != nil { - return nil, err - } - //if depositUint256.Gt(constants.Uint256MaxUint32()) { - // // deposit cannot be represented by uint32 value - // return nil, errorz.ErrInvalid{}.New("Error in BaseDepositEquation: required deposit is too large to be uint32") - //} - // The above check ensures this conversion succeeds - // deposit := uint32(depositUint64) + depositUint256, _ := new(uint256.Uint256).Mul(epochCost, totalEpochs) return depositUint256, nil } @@ -402,8 +399,7 @@ func BaseDepositEquation(dataSize32 uint32, numEpochs32 uint32) (*uint256.Uint25 // numEpochs = (deposit / (dataSize + BaseDatasizeConst)) - 2 // // We have additional checks to ensure there is no integer overflow. -func NumEpochsEquation(dataSize32 uint32, depositOrig *uint256.Uint256) (uint32, error) { - depositClone := depositOrig.Clone() +func NumEpochsEquation(dataSize32 uint32, deposit *uint256.Uint256) (uint32, error) { if dataSize32 > constants.MaxDataStoreSize { return 0, errorz.ErrInvalid{}.New("Error in NumEpochsEquation: dataSize is too large") } @@ -416,15 +412,9 @@ func NumEpochsEquation(dataSize32 uint32, depositOrig *uint256.Uint256) (uint32, // Unsigned integer arithmetic ensures // // tmp >= 0 - dataSize, err := new(uint256.Uint256).FromUint64(uint64(dataSize32)) - if err != nil { - return 0, err - } - totalDataSize, err := new(uint256.Uint256).Add(dataSize, uint256.BaseDatasizeConst()) - if err != nil { - return 0, err - } - tmp, err := new(uint256.Uint256).Div(depositClone, totalDataSize) + dataSize, _ := new(uint256.Uint256).FromUint64(uint64(dataSize32)) + totalDataSize, _ := new(uint256.Uint256).Add(dataSize, uint256.BaseDatasizeConst()) + tmp, err := new(uint256.Uint256).Div(deposit, totalDataSize) if err != nil { return 0, err } @@ -432,10 +422,7 @@ func NumEpochsEquation(dataSize32 uint32, depositOrig *uint256.Uint256) (uint32, return 0, errorz.ErrInvalid{}.New("Error in NumEpochsEquation: invalid dataSize and deposit causing integer overflow") } // The above check ensures there is no integer overflow in this subtraction - numEpochs, err := new(uint256.Uint256).Sub(tmp, uint256.Two()) - if err != nil { - return 0, err - } + numEpochs, _ := new(uint256.Uint256).Sub(tmp, uint256.Two()) numEpochs32, err := numEpochs.ToUint32() if err != nil { return 0, err diff --git a/application/objs/dspi_test.go b/application/objs/dspi_test.go index f4f7c956..4f970d90 100644 --- a/application/objs/dspi_test.go +++ b/application/objs/dspi_test.go @@ -41,6 +41,7 @@ func TestDSPreImageGood(t *testing.T) { RawData: rawdata, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } dsp2 := &DSPreImage{} dspBytes, err := dsp.MarshalBinary() @@ -310,6 +311,7 @@ func TestDSOwnerSig(t *testing.T) { RawData: rawdata, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } _, err = dsp.MarshalBinary() if err != nil { @@ -374,6 +376,7 @@ func TestDSPreImagePreHash(t *testing.T) { dsp.RawData = rawData dsp.TXOutIdx = txOutIdx dsp.Owner = dso + dsp.Fee = new(uint256.Uint256).SetZero() _, err = dsp.PreHash() if err != nil { @@ -792,10 +795,17 @@ func TestDSPreImageValidateDeposit(t *testing.T) { dsp.Deposit = deposit err = dsp.ValidateDeposit() if err == nil { - // Fails because dsp.Owner == nil + // Fails because dsp.Fee == nil t.Fatal("Should raise an error (7)") } + dsp.Fee = new(uint256.Uint256) + err = dsp.ValidateDeposit() + if err == nil { + // Fails because dsp.Owner == nil + t.Fatal("Should raise an error (8)") + } + dso := &DataStoreOwner{} curveSpec := constants.CurveSecp256k1 acct := make([]byte, constants.OwnerLen) diff --git a/application/objs/mockstorage_test.go b/application/objs/mockstorage_test.go new file mode 100644 index 00000000..1178ffb6 --- /dev/null +++ b/application/objs/mockstorage_test.go @@ -0,0 +1,145 @@ +package objs + +import ( + "math/big" + "time" + + "github.com/MadBase/MadNet/application/wrapper" + "github.com/MadBase/MadNet/dynamics" + "github.com/dgraph-io/badger/v2" +) + +func makeMockStorageGetter() *mockStorageGetter { + maxBytes := uint32(0) + dataStoreEpochFee := new(big.Int) + atomicSwapFee := new(big.Int) + valueStoreFee := new(big.Int) + minTxFee := new(big.Int) + + msg := &mockStorageGetter{ + maxBytes: maxBytes, + dataStoreEpochFee: dataStoreEpochFee, + valueStoreFee: valueStoreFee, + atomicSwapFee: atomicSwapFee, + minTxFee: minTxFee, + } + return msg +} + +func makeStorage(msg dynamics.StorageGetter) *wrapper.Storage { + storage := wrapper.NewStorage(msg) + return storage +} + +type mockStorageGetter struct { + maxBytes uint32 + dataStoreEpochFee *big.Int + valueStoreFee *big.Int + atomicSwapFee *big.Int + minTxFee *big.Int + maxTxVectorLength int +} + +func (msg *mockStorageGetter) GetMaxBytes() uint32 { + return msg.maxBytes +} + +func (msg *mockStorageGetter) SetMaxBytes(value uint32) { + msg.maxBytes = value +} + +func (msg *mockStorageGetter) GetMaxProposalSize() uint32 { + return msg.maxBytes +} + +func (msg *mockStorageGetter) GetProposalStepTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetPreVoteStepTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetPreCommitStepTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetDeadBlockRoundNextRoundTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetDownloadTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetSrvrMsgTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetMsgTimeout() time.Duration { + return time.Duration(0) +} +func (msg *mockStorageGetter) GetMaxTxVectorLength() int { + return 128 +} + +func (msg *mockStorageGetter) UpdateStorage(txn *badger.Txn, update dynamics.Updater) error { + return nil +} +func (msg *mockStorageGetter) LoadStorage(txn *badger.Txn, epoch uint32) error { + return nil +} + +func (msg *mockStorageGetter) GetDataStoreEpochFee() *big.Int { + return msg.dataStoreEpochFee +} + +func (msg *mockStorageGetter) SetDataStoreEpochFee(value *big.Int) { + if value == nil { + panic("invalid value") + } + msg.dataStoreEpochFee.Set(value) +} + +func (msg *mockStorageGetter) GetDataStoreValidVersion() uint32 { + return 0 +} + +func (msg *mockStorageGetter) GetValueStoreFee() *big.Int { + return msg.valueStoreFee +} + +func (msg *mockStorageGetter) SetValueStoreFee(value *big.Int) { + if value == nil { + panic("invalid value") + } + msg.valueStoreFee.Set(value) +} + +func (msg *mockStorageGetter) GetValueStoreValidVersion() uint32 { + return 0 +} + +func (msg *mockStorageGetter) GetAtomicSwapFee() *big.Int { + return msg.atomicSwapFee +} + +func (msg *mockStorageGetter) SetAtomicSwapFee(value *big.Int) { + if value == nil { + panic("invalid value") + } + msg.atomicSwapFee.Set(value) +} + +func (msg *mockStorageGetter) GetAtomicSwapValidStopEpoch() uint32 { + return 0 +} + +func (msg *mockStorageGetter) GetMinTxFee() *big.Int { + return msg.minTxFee +} + +func (msg *mockStorageGetter) SetMinTxFee(value *big.Int) { + if value == nil { + panic("invalid value") + } + msg.minTxFee.Set(value) +} + +func (msg *mockStorageGetter) GetTxValidVersion() uint32 { + return 0 +} diff --git a/application/objs/tf.go b/application/objs/tf.go new file mode 100644 index 00000000..d8695432 --- /dev/null +++ b/application/objs/tf.go @@ -0,0 +1,176 @@ +package objs + +import ( + mdefs "github.com/MadBase/MadNet/application/objs/capn" + "github.com/MadBase/MadNet/application/objs/txfee" + "github.com/MadBase/MadNet/application/objs/uint256" + "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/errorz" + "github.com/MadBase/MadNet/utils" + capnp "zombiezen.com/go/capnproto2" +) + +// TxFee stores the transaction fee in a Tx +type TxFee struct { + TFPreImage *TFPreImage + TxHash []byte + // + utxoID []byte +} + +// New creates a new TxFee; fees must always +func (b *TxFee) New(chainID uint32, fee *uint256.Uint256) error { + if chainID == 0 { + return errorz.ErrInvalid{}.New("not initialized") + } + if fee == nil || fee.IsZero() { + return errorz.ErrInvalid{}.New("Error in TxFee.New: fee is nil") + } + tfp := &TFPreImage{ + ChainID: chainID, + Fee: fee.Clone(), + } + b.TFPreImage = tfp + return nil +} + +// UnmarshalBinary takes a byte slice and returns the corresponding +// TxFee object +func (b *TxFee) UnmarshalBinary(data []byte) error { + if b == nil { + return errorz.ErrInvalid{}.New("not initialized") + } + bc, err := txfee.Unmarshal(data) + if err != nil { + return err + } + return b.UnmarshalCapn(bc) +} + +// MarshalBinary takes the ValueStore object and returns the canonical +// byte slice +func (b *TxFee) MarshalBinary() ([]byte, error) { + if b == nil { + return nil, errorz.ErrInvalid{}.New("not initialized") + } + bc, err := b.MarshalCapn(nil) + if err != nil { + return nil, err + } + return txfee.Marshal(bc) +} + +// UnmarshalCapn unmarshals the capnproto definition of the object +func (b *TxFee) UnmarshalCapn(bc mdefs.TxFee) error { + if err := txfee.Validate(bc); err != nil { + return err + } + b.TFPreImage = &TFPreImage{} + if err := b.TFPreImage.UnmarshalCapn(bc.TFPreImage()); err != nil { + return err + } + b.TxHash = utils.CopySlice(bc.TxHash()) + return nil +} + +// MarshalCapn marshals the object into its capnproto definition +func (b *TxFee) MarshalCapn(seg *capnp.Segment) (mdefs.TxFee, error) { + if b == nil { + return mdefs.TxFee{}, errorz.ErrInvalid{}.New("not initialized") + } + var bc mdefs.TxFee + if seg == nil { + _, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) + if err != nil { + return bc, err + } + tmp, err := mdefs.NewRootTxFee(seg) + if err != nil { + return bc, err + } + bc = tmp + } else { + tmp, err := mdefs.NewTxFee(seg) + if err != nil { + return bc, err + } + bc = tmp + } + seg = bc.Struct.Segment() + bt, err := b.TFPreImage.MarshalCapn(seg) + if err != nil { + return bc, err + } + if err := bc.SetTFPreImage(bt); err != nil { + return bc, err + } + if err := bc.SetTxHash(utils.CopySlice(b.TxHash)); err != nil { + return bc, err + } + return bc, nil +} + +// PreHash calculates the PreHash of the object +func (b *TxFee) PreHash() ([]byte, error) { + if b == nil { + return nil, errorz.ErrInvalid{}.New("not initialized") + } + return b.TFPreImage.PreHash() +} + +// UTXOID calculates the UTXOID of the object +func (b *TxFee) UTXOID() ([]byte, error) { + if b == nil || b.TFPreImage == nil || len(b.TxHash) != constants.HashLen { + return nil, errorz.ErrInvalid{}.New("not initialized") + } + if b.utxoID != nil { + return utils.CopySlice(b.utxoID), nil + } + b.utxoID = MakeUTXOID(b.TxHash, b.TFPreImage.TXOutIdx) + return utils.CopySlice(b.utxoID), nil +} + +// TXOutIdx returns the TXOutIdx of the object +func (b *TxFee) TXOutIdx() (uint32, error) { + if b == nil || b.TFPreImage == nil { + return 0, errorz.ErrInvalid{}.New("not initialized") + } + return b.TFPreImage.TXOutIdx, nil +} + +// SetTXOutIdx sets the TXOutIdx of the object +func (b *TxFee) SetTXOutIdx(idx uint32) error { + if b == nil || b.TFPreImage == nil { + return errorz.ErrInvalid{}.New("not initialized") + } + b.TFPreImage.TXOutIdx = idx + return nil +} + +// SetTxHash sets the TxHash of the object +func (b *TxFee) SetTxHash(txHash []byte) error { + if b == nil || b.TFPreImage == nil { + return errorz.ErrInvalid{}.New("not initialized") + } + if len(txHash) != constants.HashLen { + return errorz.ErrInvalid{}.New("Invalid hash length") + } + b.TxHash = utils.CopySlice(txHash) + return nil +} + +// ChainID returns the ChainID of the object +func (b *TxFee) ChainID() (uint32, error) { + if b == nil || b.TFPreImage == nil || b.TFPreImage.ChainID == 0 { + return 0, errorz.ErrInvalid{}.New("not initialized") + } + return b.TFPreImage.ChainID, nil +} + +// Fee returns the Fee of the object; Fee should always be nonzero +func (b *TxFee) Fee() (*uint256.Uint256, error) { + if b == nil || b.TFPreImage == nil || b.TFPreImage.Fee == nil || b.TFPreImage.Fee.IsZero() { + return nil, errorz.ErrInvalid{}.New("not initialized") + } + return b.TFPreImage.Fee.Clone(), nil +} diff --git a/application/objs/tf_test.go b/application/objs/tf_test.go new file mode 100644 index 00000000..0452aaf6 --- /dev/null +++ b/application/objs/tf_test.go @@ -0,0 +1,397 @@ +package objs + +import ( + "bytes" + "testing" + + "github.com/MadBase/MadNet/application/objs/uint256" + "github.com/MadBase/MadNet/constants" +) + +func TestTxFeeGood(t *testing.T) { + cid := uint32(2) + fee, err := new(uint256.Uint256).FromUint64(65537) + if err != nil { + t.Fatal(err) + } + txoid := uint32(17) + + tfp := &TFPreImage{ + ChainID: cid, + Fee: fee, + TXOutIdx: txoid, + } + txHash := make([]byte, constants.HashLen) + tf := &TxFee{ + TFPreImage: tfp, + TxHash: txHash, + } + tf2 := &TxFee{} + tfBytes, err := tf.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = tf2.UnmarshalBinary(tfBytes) + if err != nil { + t.Fatal(err) + } + tfEqual(t, tf, tf2) +} + +func tfEqual(t *testing.T, tf1, tf2 *TxFee) { + tfpi1 := tf1.TFPreImage + tfpi2 := tf2.TFPreImage + tfpiEqual(t, tfpi1, tfpi2) + if !bytes.Equal(tf1.TxHash, tf2.TxHash) { + t.Fatal("Do not agree on TxHash!") + } +} + +func TestTxFeeBad1(t *testing.T) { + cid := uint32(0) // Invalid ChainID + fee, err := new(uint256.Uint256).FromUint64(65537) + if err != nil { + t.Fatal(err) + } + txoid := uint32(17) + + tfp := &TFPreImage{ + ChainID: cid, + Fee: fee, + TXOutIdx: txoid, + } + txHash := make([]byte, constants.HashLen) + tf := &TxFee{ + TFPreImage: tfp, + TxHash: txHash, + } + tf2 := &TxFee{} + tfBytes, err := tf.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = tf2.UnmarshalBinary(tfBytes) + if err == nil { + t.Fatal("Should raise error for invalid TFPreImage!") + } +} + +func TestTxFeeBad2(t *testing.T) { + cid := uint32(2) + fee, err := new(uint256.Uint256).FromUint64(65537) + if err != nil { + t.Fatal(err) + } + txoid := uint32(17) + + tfp := &TFPreImage{ + ChainID: cid, + Fee: fee, + TXOutIdx: txoid, + } + txHash := make([]byte, constants.HashLen+1) // Invalid TxHash + tf := &TxFee{ + TFPreImage: tfp, + TxHash: txHash, + } + tf2 := &TxFee{} + tfBytes, err := tf.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = tf2.UnmarshalBinary(tfBytes) + if err == nil { + t.Fatal("Should raise error for invalid TxHash: incorrect byte length!") + } +} + +func TestTxFeeNew(t *testing.T) { + utxo := &TXOut{} + chainID := uint32(0) + fee, err := new(uint256.Uint256).FromUint64(65537) + if err != nil { + t.Fatal(err) + } + err = utxo.txFee.New(chainID, nil) + if err == nil { + t.Fatal("Should raise an error (1)") + } + + tf := &TxFee{} + err = tf.New(chainID, nil) + if err == nil { + t.Fatal("Should raise an error (2)") + } + + chainID = 1 + err = tf.New(chainID, nil) + if err == nil { + t.Fatal("Should raise an error (3)") + } + + err = tf.New(chainID, fee) + if err != nil { + t.Fatal(err) + } +} + +func TestTxFeeMarshalBinary(t *testing.T) { + utxo := &TXOut{} + _, err := utxo.txFee.MarshalBinary() + if err == nil { + t.Fatal("Should raise an error (1)") + } + _, err = utxo.txFee.MarshalCapn(nil) + if err == nil { + t.Fatal("Should raise an error (2)") + } + tf := &TxFee{} + _, err = tf.MarshalBinary() + if err == nil { + t.Fatal("Should raise an error (3)") + } + _, err = tf.MarshalCapn(nil) + if err == nil { + t.Fatal("Should raise an error (4)") + } + + cid := uint32(2) + fee, err := new(uint256.Uint256).FromUint64(65537) + if err != nil { + t.Fatal(err) + } + txoid := uint32(17) + + tfp := &TFPreImage{ + ChainID: cid, + Fee: fee, + TXOutIdx: txoid, + } + txHash := make([]byte, constants.HashLen) + tf = &TxFee{ + TFPreImage: tfp, + TxHash: txHash, + } + tfBytes, err := tf.MarshalBinary() + if err != nil { + t.Fatal(err) + } + tf2 := &TxFee{} + err = tf2.UnmarshalBinary(tfBytes) + if err != nil { + t.Fatal(err) + } + tfEqual(t, tf, tf2) +} + +func TestTxFeeUnmarshalBinary(t *testing.T) { + data := make([]byte, 0) + utxo := &TXOut{} + err := utxo.txFee.UnmarshalBinary(data) + if err == nil { + t.Fatal("Should raise an error (1)") + } + tf := &TxFee{} + err = tf.UnmarshalBinary(data) + if err == nil { + t.Fatal("Should raise an error (2)") + } + + cid := uint32(2) + fee, err := new(uint256.Uint256).FromUint64(65537) + if err != nil { + t.Fatal(err) + } + txoid := uint32(17) + + tfp := &TFPreImage{ + ChainID: cid, + Fee: fee, + TXOutIdx: txoid, + } + txHash := make([]byte, constants.HashLen) + tf = &TxFee{ + TFPreImage: tfp, + TxHash: txHash, + } + tfBytes, err := tf.MarshalBinary() + if err != nil { + t.Fatal(err) + } + tf2 := &TxFee{} + err = tf2.UnmarshalBinary(tfBytes) + if err != nil { + t.Fatal(err) + } + tfEqual(t, tf, tf2) +} + +func TestTxFeePreHash(t *testing.T) { + utxo := &TXOut{} + _, err := utxo.txFee.PreHash() + if err == nil { + t.Fatal("Should raise an error (1)") + } + tf := &TxFee{} + _, err = tf.PreHash() + if err == nil { + t.Fatal("Should raise an error (2)") + } +} + +func TestTxFeeTXOutIdx(t *testing.T) { + utxo := &TXOut{} + _, err := utxo.txFee.TXOutIdx() + if err == nil { + t.Fatal("Should raise an error (1)") + } + tf := &TxFee{} + _, err = tf.TXOutIdx() + if err == nil { + t.Fatal("Should raise an error (2)") + } + tf.TFPreImage = &TFPreImage{} + txOutIdx := uint32(17) + tf.TFPreImage.TXOutIdx = txOutIdx + out, err := tf.TXOutIdx() + if err != nil { + t.Fatal(err) + } + if out != txOutIdx { + t.Fatal("TXOutIdxes do not match") + } +} + +func TestTxFeeUTXOID(t *testing.T) { + utxo := &TXOut{} + _, err := utxo.txFee.UTXOID() + if err == nil { + t.Fatal("Should raise an error (1)") + } + tf := &TxFee{} + _, err = tf.UTXOID() + if err == nil { + t.Fatal("Should raise an error (2)") + } + + cid := uint32(2) + fee, err := new(uint256.Uint256).FromUint64(65537) + if err != nil { + t.Fatal(err) + } + txoid := uint32(17) + + tfp := &TFPreImage{ + ChainID: cid, + Fee: fee, + TXOutIdx: txoid, + } + tf = &TxFee{ + TFPreImage: tfp, + TxHash: nil, + } + _, err = tf.UTXOID() + if err == nil { + t.Fatal("Should raise an error (3)") + } + + txHash := make([]byte, constants.HashLen) + tf.TxHash = txHash + utxoID, err := tf.UTXOID() + if err != nil { + t.Fatal(err) + } + out, err := tf.UTXOID() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(out, utxoID) { + t.Fatal("utxoIDs do not match") + } +} + +func TestTxFeeSetTXOutIdx(t *testing.T) { + idx := uint32(0) + utxo := &TXOut{} + err := utxo.txFee.SetTXOutIdx(idx) + if err == nil { + t.Fatal("Should raise an error (1)") + } + tf := &TxFee{} + err = tf.SetTXOutIdx(idx) + if err == nil { + t.Fatal("Should raise an error (2)") + } + tf.TFPreImage = &TFPreImage{} + err = tf.SetTXOutIdx(idx) + if err != nil { + t.Fatal(err) + } + out, err := tf.TXOutIdx() + if err != nil { + t.Fatal(err) + } + if out != idx { + t.Fatal("TXOutIdxes do not match") + } +} + +func TestTxFeeSetTxHash(t *testing.T) { + tf := &TxFee{} + txHash := make([]byte, 0) + err := tf.SetTxHash(txHash) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + tf.TFPreImage = &TFPreImage{} + err = tf.SetTxHash(txHash) + if err == nil { + t.Fatal("Should have raised error (2)") + } + + txHash = make([]byte, constants.HashLen) + err = tf.SetTxHash(txHash) + if err != nil { + t.Fatal("Should pass") + } +} + +func TestTxFeeChainID(t *testing.T) { + tf := &TxFee{} + _, err := tf.ChainID() + if err == nil { + t.Fatal("Should raise an error") + } + tf.TFPreImage = &TFPreImage{} + cid := uint32(17) + tf.TFPreImage.ChainID = cid + chainID, err := tf.ChainID() + if err != nil { + t.Fatal(err) + } + if cid != chainID { + t.Fatal("ChainIDs do not match") + } +} + +func TestTxFeeFee(t *testing.T) { + tf := &TxFee{} + _, err := tf.Fee() + if err == nil { + t.Fatal("Should raise an error") + } + tf.TFPreImage = &TFPreImage{} + feeTrue, err := new(uint256.Uint256).FromUint64(1234567890) + if err != nil { + t.Fatal(err) + } + tf.TFPreImage.Fee = feeTrue.Clone() + fee, err := tf.Fee() + if err != nil { + t.Fatal(err) + } + if !fee.Eq(feeTrue) { + t.Fatal("Fees do not match") + } +} diff --git a/application/objs/tfpi.go b/application/objs/tfpi.go new file mode 100644 index 00000000..182af5b4 --- /dev/null +++ b/application/objs/tfpi.go @@ -0,0 +1,128 @@ +package objs + +import ( + mdefs "github.com/MadBase/MadNet/application/objs/capn" + "github.com/MadBase/MadNet/application/objs/tfpreimage" + "github.com/MadBase/MadNet/application/objs/uint256" + "github.com/MadBase/MadNet/crypto" + "github.com/MadBase/MadNet/errorz" + "github.com/MadBase/MadNet/utils" + capnp "zombiezen.com/go/capnproto2" +) + +// TFPreImage is a txfee preimage +type TFPreImage struct { + ChainID uint32 + TXOutIdx uint32 + Fee *uint256.Uint256 + // + preHash []byte +} + +// UnmarshalBinary takes a byte slice and returns the corresponding +// TFPreImage object +func (b *TFPreImage) UnmarshalBinary(data []byte) error { + if b == nil { + return errorz.ErrInvalid{}.New("not initialized") + } + bc, err := tfpreimage.Unmarshal(data) + if err != nil { + return err + } + return b.UnmarshalCapn(bc) +} + +// MarshalBinary takes the TFPreImage object and returns the canonical +// byte slice +func (b *TFPreImage) MarshalBinary() ([]byte, error) { + if b == nil { + return nil, errorz.ErrInvalid{}.New("not initialized") + } + bc, err := b.MarshalCapn(nil) + if err != nil { + return nil, err + } + return tfpreimage.Marshal(bc) +} + +// UnmarshalCapn unmarshals the capnproto definition of the object +func (b *TFPreImage) UnmarshalCapn(bc mdefs.TFPreImage) error { + if err := tfpreimage.Validate(bc); err != nil { + return err + } + b.ChainID = bc.ChainID() + u32array := [8]uint32{} + u32array[0] = bc.Fee0() + u32array[1] = bc.Fee1() + u32array[2] = bc.Fee2() + u32array[3] = bc.Fee3() + u32array[4] = bc.Fee4() + u32array[5] = bc.Fee5() + u32array[6] = bc.Fee6() + u32array[7] = bc.Fee7() + fObj := &uint256.Uint256{} + err := fObj.FromUint32Array(u32array) + if err != nil { + return err + } + b.Fee = fObj + b.TXOutIdx = bc.TXOutIdx() + return nil +} + +// MarshalCapn marshals the object into its capnproto definition +func (b *TFPreImage) MarshalCapn(seg *capnp.Segment) (mdefs.TFPreImage, error) { + if b == nil { + return mdefs.TFPreImage{}, errorz.ErrInvalid{}.New("not initialized") + } + var bc mdefs.TFPreImage + if seg == nil { + _, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) + if err != nil { + return bc, err + } + tmp, err := mdefs.NewRootTFPreImage(seg) + if err != nil { + return bc, err + } + bc = tmp + } else { + tmp, err := mdefs.NewTFPreImage(seg) + if err != nil { + return bc, err + } + bc = tmp + } + bc.SetChainID(b.ChainID) + u32array, err := b.Fee.ToUint32Array() + if err != nil { + return bc, err + } + bc.SetFee0(u32array[0]) + bc.SetFee1(u32array[1]) + bc.SetFee2(u32array[2]) + bc.SetFee3(u32array[3]) + bc.SetFee4(u32array[4]) + bc.SetFee5(u32array[5]) + bc.SetFee6(u32array[6]) + bc.SetFee7(u32array[7]) + bc.SetTXOutIdx(b.TXOutIdx) + return bc, nil +} + +// PreHash calculates the PreHash of the object +func (b *TFPreImage) PreHash() ([]byte, error) { + if b == nil { + return nil, errorz.ErrInvalid{}.New("not initialized") + } + if b.preHash != nil { + return utils.CopySlice(b.preHash), nil + } + msg, err := b.MarshalBinary() + if err != nil { + return nil, err + } + hsh := crypto.Hasher(msg) + b.preHash = hsh + return utils.CopySlice(b.preHash), nil +} diff --git a/application/objs/tfpi_test.go b/application/objs/tfpi_test.go new file mode 100644 index 00000000..1225b7ed --- /dev/null +++ b/application/objs/tfpi_test.go @@ -0,0 +1,144 @@ +package objs + +import ( + "bytes" + "testing" + + "github.com/MadBase/MadNet/application/objs/uint256" +) + +func TestTFPreImageGood(t *testing.T) { + cid := uint32(2) + fee, err := new(uint256.Uint256).FromUint64(65537) + if err != nil { + t.Fatal(err) + } + txoid := uint32(17) + + tfp := &TFPreImage{ + ChainID: cid, + Fee: fee, + TXOutIdx: txoid, + } + tfp2 := &TFPreImage{} + tfpBytes, err := tfp.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = tfp2.UnmarshalBinary(tfpBytes) + if err != nil { + t.Fatal(err) + } + tfpiEqual(t, tfp, tfp2) +} + +func tfpiEqual(t *testing.T, tfpi1, tfpi2 *TFPreImage) { + if tfpi1.ChainID != tfpi2.ChainID { + t.Fatal("Do not agree on ChainID!") + } + if !tfpi1.Fee.Eq(tfpi2.Fee) { + t.Fatal("Do not agree on Next!") + } + if tfpi1.TXOutIdx != tfpi2.TXOutIdx { + t.Fatal("Do not agree on TXOutIdx!") + } +} + +func TestTFPreImageBad1(t *testing.T) { + cid := uint32(0) // Invalid ChainID + fee, err := new(uint256.Uint256).FromUint64(65537) + if err != nil { + t.Fatal(err) + } + txoid := uint32(17) + + tfp := &TFPreImage{ + ChainID: cid, + Fee: fee, + TXOutIdx: txoid, + } + tfp2 := &TFPreImage{} + tfpBytes, err := tfp.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = tfp2.UnmarshalBinary(tfpBytes) + if err == nil { + t.Fatal("Should raise error for invalid ChainID!") + } +} + +func TestTFPreImageMarshalBinary(t *testing.T) { + tf := &TxFee{} + _, err := tf.TFPreImage.MarshalBinary() + if err == nil { + t.Fatal("Should raise an error (1)") + } + + tfp := &TFPreImage{} + _, err = tfp.MarshalBinary() + if err == nil { + t.Fatal("Should raise an error (2)") + } +} + +func TestTFPreImageUnmarshalBinary(t *testing.T) { + data := make([]byte, 0) + tf := &TxFee{} + err := tf.TFPreImage.UnmarshalBinary(data) + if err == nil { + t.Fatal("Should raise an error (1)") + } + + tfp := &TFPreImage{} + err = tfp.UnmarshalBinary(data) + if err == nil { + t.Fatal("Should raise an error (2)") + } +} + +func TestTFPreImagePreHash(t *testing.T) { + tf := &TxFee{} + _, err := tf.TFPreImage.PreHash() + if err == nil { + t.Fatal("Should raise an error (1)") + } + + tfp := &TFPreImage{} + _, err = tfp.PreHash() + if err == nil { + t.Fatal("Should raise an error (2)") + } + + // preHash is present; should not fail + tfpGoodPH := &TFPreImage{} + tfpGoodPH.preHash = make([]byte, 32) + tfpGoodPHOut, err := tfpGoodPH.PreHash() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(tfpGoodPHOut, tfpGoodPH.preHash) { + t.Fatal("PreHashes do not match (1)") + } + + // Make new + cid := uint32(2) + fee, err := new(uint256.Uint256).FromUint64(65537) + if err != nil { + t.Fatal(err) + } + txoid := uint32(17) + + tfp = &TFPreImage{ + ChainID: cid, + Fee: fee, + TXOutIdx: txoid, + } + out, err := tfp.PreHash() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(out, tfp.preHash) { + t.Fatal("PreHashes do not match (2)") + } +} diff --git a/application/objs/tfpreimage/tfpreimage.go b/application/objs/tfpreimage/tfpreimage.go new file mode 100644 index 00000000..e60d4e9e --- /dev/null +++ b/application/objs/tfpreimage/tfpreimage.go @@ -0,0 +1,52 @@ +package tfpreimage + +import ( + mdefs "github.com/MadBase/MadNet/application/objs/capn" + "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/errorz" + "github.com/MadBase/MadNet/utils" + capnp "zombiezen.com/go/capnproto2" +) + +// Marshal will marshal the TFPreImage object. +func Marshal(v mdefs.TFPreImage) ([]byte, error) { + raw, err := capnp.Canonicalize(v.Struct) + if err != nil { + return nil, err + } + out := utils.CopySlice(raw) + return out, nil +} + +// Unmarshal will unmarshal the TFPreImage object. +func Unmarshal(data []byte) (mdefs.TFPreImage, error) { + var err error + fn := func() (mdefs.TFPreImage, error) { + defer func() { + if r := recover(); r != nil { + err = errorz.ErrInvalid{}.New("bad serialization") + } + }() + dataCopy := utils.CopySlice(data) + msg := &capnp.Message{Arena: capnp.SingleSegment(dataCopy)} + obj, tmp := mdefs.ReadRootTFPreImage(msg) + err = tmp + return obj, err + } + obj, err := fn() + if err != nil { + return mdefs.TFPreImage{}, err + } + return obj, nil +} + +// Validate will validate the TFPreImage object +func Validate(v mdefs.TFPreImage) error { + if v.ChainID() < 1 { + return errorz.ErrInvalid{}.New("tfpreimage capn obj is not valid; invalid ChainID") + } + if int(v.TXOutIdx()) >= constants.MaxTxVectorLength { + return errorz.ErrInvalid{}.New("tfpreimage capn obj is not valid: output index is too large") + } + return nil +} diff --git a/application/objs/tx.go b/application/objs/tx.go index 1f69a6f7..f61541ef 100644 --- a/application/objs/tx.go +++ b/application/objs/tx.go @@ -8,6 +8,7 @@ import ( mdefs "github.com/MadBase/MadNet/application/objs/capn" "github.com/MadBase/MadNet/application/objs/tx" + "github.com/MadBase/MadNet/application/wrapper" trie "github.com/MadBase/MadNet/badgerTrie" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" @@ -42,6 +43,12 @@ func (b *Tx) MarshalBinary() ([]byte, error) { if b == nil { return nil, errorz.ErrInvalid{}.New("not initialized") } + if len(b.Vin) > constants.MaxTxVectorLength { + return nil, errorz.ErrInvalid{}.New("invalid tx: len(vin) > MaxTxVectorLength") + } + if len(b.Vout) > constants.MaxTxVectorLength { + return nil, errorz.ErrInvalid{}.New("invalid tx: len(vout) > MaxTxVectorLength") + } bc, err := b.MarshalCapn(nil) if err != nil { return nil, err @@ -202,8 +209,8 @@ func (b *Tx) ValidateDataStoreIndexes(opset map[string]bool) (map[string]bool, e return nil, err } tmp := []byte{} - tmp = append(tmp, utils.CopySlice(ownerBytes)...) - tmp = append(tmp, utils.CopySlice(index)...) + tmp = append(tmp, ownerBytes...) + tmp = append(tmp, index...) hsh := crypto.Hasher(tmp) if !opset[string(hsh)] { opset[string(hsh)] = true @@ -352,7 +359,7 @@ func (b *Tx) GeneratedUTXOID() ([][]byte, error) { // GeneratedPreHash returns the list of PreHashs from Vout func (b *Tx) GeneratedPreHash() ([][]byte, error) { - if b == nil || len(b.Vout) == 0 || len(b.Vin) == 0 { + if b == nil || len(b.Vout) == 0 { return nil, errorz.ErrInvalid{}.New("not initialized") } return b.Vout.PreHash() @@ -360,7 +367,7 @@ func (b *Tx) GeneratedPreHash() ([][]byte, error) { // ValidateSignature validates the signatures of the objects func (b *Tx) ValidateSignature(currentHeight uint32, refUTXOs Vout) error { - if b == nil || len(b.Vout) == 0 || len(b.Vin) == 0 { + if b == nil || len(b.Vin) == 0 { return errorz.ErrInvalid{}.New("not initialized") } return refUTXOs.ValidateSignature(currentHeight, b.Vin) @@ -368,20 +375,39 @@ func (b *Tx) ValidateSignature(currentHeight uint32, refUTXOs Vout) error { // ValidatePreSignature validates the presignatures of the objects func (b *Tx) ValidatePreSignature() error { - if b == nil || len(b.Vout) == 0 || len(b.Vin) == 0 { + if b == nil || len(b.Vout) == 0 { return errorz.ErrInvalid{}.New("not initialized") } return b.Vout.ValidatePreSignature() } +// ValidateFees validates the fees of the object. +// currentHeight and refUTXOs are needed to verify if we have a cleanup tx. +func (b *Tx) ValidateFees(currentHeight uint32, refUTXOs Vout, storage *wrapper.Storage) error { + if b == nil || len(b.Vout) == 0 || len(b.Vin) == 0 { + return errorz.ErrInvalid{}.New("not initialized") + } + if b.IsCleanupTx(currentHeight, refUTXOs) { + // Tx is a valid Cleanup Tx, so we do not worry about fees + return nil + } + if err := b.Vout.ValidateFees(storage); err != nil { + return err + } + if err := b.Vout.ValidateTxFee(storage); err != nil { + return err + } + return nil +} + // ValidateEqualVinVout checks the following // calc sum on inputs from utxos and currentHeight // sum inputs must equal sum outputs -func (b *Tx) ValidateEqualVinVout(refUTXOs Vout, currentHeight uint32) error { +func (b *Tx) ValidateEqualVinVout(currentHeight uint32, refUTXOs Vout) error { if b == nil || len(b.Vout) == 0 || len(b.Vin) == 0 { return errorz.ErrInvalid{}.New("not initialized") } - valueOut, err := b.Vout.Value() + valueOut, err := b.Vout.ValuePlusFee() if err != nil { return err } @@ -399,7 +425,7 @@ func (b *Tx) ValidateEqualVinVout(refUTXOs Vout, currentHeight uint32) error { if err != nil { return err } - if valueOut.Clone().Cmp(valueIn.Clone()) == 0 { + if valueOut.Cmp(valueIn) == 0 { return nil } return errorz.ErrInvalid{}.New(fmt.Sprintf("input value does not match output value: IN:%v vs OUT:%v", valueIn, valueOut)) @@ -407,14 +433,11 @@ func (b *Tx) ValidateEqualVinVout(refUTXOs Vout, currentHeight uint32) error { // ValidateChainID validates that all elements have the correct ChainID func (b *Tx) ValidateChainID(chainID uint32) error { - if b == nil { - return errorz.ErrInvalid{}.New("not initialized a") - } - if len(b.Vout) == 0 { - return errorz.ErrInvalid{}.New("not initialized b") + if b == nil || len(b.Vout) == 0 || len(b.Vin) == 0 { + return errorz.ErrInvalid{}.New("not initialized") } - if len(b.Vin) == 0 { - return errorz.ErrInvalid{}.New("not initialized c") + if chainID == 0 { + return errorz.ErrInvalid{}.New("chainID invalid: cannot be 0") } for _, inp := range b.Vin { inpCid, err := inp.ChainID() @@ -439,7 +462,7 @@ func (b *Tx) ValidateChainID(chainID uint32) error { // CannotBeMinedUntil ... func (b *Tx) CannotBeMinedUntil() (uint32, error) { - if b == nil || len(b.Vout) == 0 || len(b.Vin) == 0 { + if b == nil || len(b.Vout) == 0 { return 0, errorz.ErrInvalid{}.New("not initialized") } maxBH := uint32(1) @@ -457,7 +480,7 @@ func (b *Tx) CannotBeMinedUntil() (uint32, error) { // ValidateIssuedAtForMining ... func (b *Tx) ValidateIssuedAtForMining(currentHeight uint32) error { - if b == nil || len(b.Vout) == 0 || len(b.Vin) == 0 { + if b == nil || len(b.Vout) == 0 { return errorz.ErrInvalid{}.New("not initialized") } hmap := make(map[uint32]bool) @@ -489,7 +512,7 @@ func (b *Tx) ValidateIssuedAtForMining(currentHeight uint32) error { // EpochOfExpirationForMining ... func (b *Tx) EpochOfExpirationForMining() (uint32, error) { - if b == nil || len(b.Vout) == 0 || len(b.Vin) == 0 { + if b == nil || len(b.Vout) == 0 { return 0, errorz.ErrInvalid{}.New("not initialized") } hmap := make(map[uint32]bool) @@ -517,7 +540,7 @@ func (b *Tx) EpochOfExpirationForMining() (uint32, error) { } // Validate ... -func (b *Tx) Validate(set map[string]bool, currentHeight uint32, consumedUTXOs Vout) (map[string]bool, error) { +func (b *Tx) Validate(set map[string]bool, currentHeight uint32, consumedUTXOs Vout, storage *wrapper.Storage) (map[string]bool, error) { if b == nil || len(b.Vin) == 0 || len(b.Vout) == 0 { return nil, errorz.ErrInvalid{}.New("empty input or output vector in tx") } @@ -532,7 +555,7 @@ func (b *Tx) Validate(set map[string]bool, currentHeight uint32, consumedUTXOs V if err != nil { return nil, err } - err = b.ValidateEqualVinVout(consumedUTXOs, currentHeight) + err = b.ValidateEqualVinVout(currentHeight, consumedUTXOs) if err != nil { return nil, err } @@ -540,6 +563,10 @@ func (b *Tx) Validate(set map[string]bool, currentHeight uint32, consumedUTXOs V if err != nil { return nil, err } + err = b.ValidateFees(currentHeight, consumedUTXOs, storage) + if err != nil { + return nil, err + } return set, nil } @@ -572,7 +599,7 @@ func (b *Tx) PostValidatePending(currentHeight uint32, consumedUTXOs Vout) error if b == nil { return errorz.ErrInvalid{}.New("not initialized") } - err := b.ValidateEqualVinVout(consumedUTXOs, currentHeight) + err := b.ValidateEqualVinVout(currentHeight, consumedUTXOs) if err != nil { return err } @@ -583,5 +610,26 @@ func (b *Tx) PostValidatePending(currentHeight uint32, consumedUTXOs Vout) error return nil } +// IsCleanupTx checks if Tx is a cleanup transaction. +// Cleanup transactions are unique in that there is no associated TxFee +// or ValueStoreFee. The CleaupTx must consist of *expired* DataStores +// with value equal to that in the only ValueStore in Vout. +func (b *Tx) IsCleanupTx(currentHeight uint32, refUTXOs Vout) bool { + if b == nil { + return false + } + // Confirm Vin + cleanupVin := b.Vin.IsCleanupVin(currentHeight, refUTXOs) + if !cleanupVin { + return false + } + // Confirm Vout + cleanupVout := b.Vout.IsCleanupVout() + if !cleanupVout { + return false + } + return true +} + // XXXIsTx allows compile time type checking for transaction interfaces func (b *Tx) XXXIsTx() {} diff --git a/application/objs/tx/tx.go b/application/objs/tx/tx.go index aca9dff7..ef9372b3 100644 --- a/application/objs/tx/tx.go +++ b/application/objs/tx/tx.go @@ -2,6 +2,7 @@ package tx import ( mdefs "github.com/MadBase/MadNet/application/objs/capn" + "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" @@ -51,6 +52,9 @@ func Validate(v mdefs.Tx) error { if vin.Len() == 0 { return errorz.ErrInvalid{}.New("tx capn obj is not valid; invalid Vin; zero length object") } + if vin.Len() > constants.MaxTxVectorLength { + return errorz.ErrInvalid{}.New("tx capn obj is not valid; invalid Vin; length object too large") + } if !v.HasVout() { return errorz.ErrInvalid{}.New("tx capn obj does not have Vout") } @@ -61,5 +65,8 @@ func Validate(v mdefs.Tx) error { if vout.Len() == 0 { return errorz.ErrInvalid{}.New("tx capn obj is not valid; invalid Vout; zero length object") } + if vout.Len() > constants.MaxTxVectorLength { + return errorz.ErrInvalid{}.New("tx capn obj is not valid; invalid Vout; length object too large") + } return nil } diff --git a/application/objs/tx_test.go b/application/objs/tx_test.go index 53a44ef1..ee66b4df 100644 --- a/application/objs/tx_test.go +++ b/application/objs/tx_test.go @@ -3,6 +3,7 @@ package objs import ( "bytes" "encoding/hex" + "math/big" "strconv" "testing" @@ -23,10 +24,12 @@ func makeVS(t *testing.T, ownerSigner Signer, i int) *TXOut { owner := &ValueStoreOwner{} owner.New(ownerAcct, constants.CurveSecp256k1) + fee := new(uint256.Uint256) vsp := &VSPreImage{ ChainID: cid, Value: val, Owner: owner, + Fee: fee.Clone(), } var txHash []byte if i == 0 { @@ -56,7 +59,130 @@ func makeVS(t *testing.T, ownerSigner Signer, i int) *TXOut { return utxInputs } +func makeVSWithValueFee(t *testing.T, ownerSigner Signer, i int, value, fee *uint256.Uint256) *TXOut { + if value == nil || fee == nil { + panic("invalid value or fee") + } + cid := uint32(2) + + ownerPubk, err := ownerSigner.Pubkey() + if err != nil { + t.Fatal(err) + } + ownerAcct := crypto.GetAccount(ownerPubk) + owner := &ValueStoreOwner{} + owner.New(ownerAcct, constants.CurveSecp256k1) + + vsp := &VSPreImage{ + ChainID: cid, + Value: value, + Owner: owner, + Fee: fee.Clone(), + } + var txHash []byte + if i == 0 { + txHash = make([]byte, constants.HashLen) + } else { + txHash = crypto.Hasher([]byte(strconv.Itoa(i))) + } + vs := &ValueStore{ + VSPreImage: vsp, + TxHash: txHash, + } + vs2 := &ValueStore{} + vsBytes, err := vs.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = vs2.UnmarshalBinary(vsBytes) + if err != nil { + t.Fatal(err) + } + vsEqual(t, vs, vs2) + utxInputs := &TXOut{} + err = utxInputs.NewValueStore(vs) + if err != nil { + t.Fatal(err) + } + return utxInputs +} + +func makeDSWithValueFee(t *testing.T, ownerSigner Signer, i int, rawData []byte, index []byte, startEpoch uint32, numEpochs uint32, fee *uint256.Uint256) *TXOut { + if fee == nil || len(rawData) == 0 { + panic("invalid fee or rawData") + } + cid := uint32(2) + + ownerPubk, err := ownerSigner.Pubkey() + if err != nil { + t.Fatal(err) + } + ownerAcct := crypto.GetAccount(ownerPubk) + owner := &DataStoreOwner{} + owner.New(ownerAcct, constants.CurveSecp256k1) + + dataSize32 := uint32(len(rawData)) + deposit, err := BaseDepositEquation(dataSize32, numEpochs) + if err != nil { + t.Fatal(err) + } + + dsp := &DSPreImage{ + ChainID: cid, + Index: index, + IssuedAt: startEpoch, + Deposit: deposit.Clone(), + RawData: rawData, + Owner: owner, + Fee: fee.Clone(), + } + err = dsp.ValidateDeposit() + if err != nil { + t.Fatal(err) + } + var txHash []byte + if i == 0 { + txHash = make([]byte, constants.HashLen) + } else { + txHash = crypto.Hasher([]byte(strconv.Itoa(i))) + } + dsl := &DSLinker{ + DSPreImage: dsp, + TxHash: txHash, + } + ds := &DataStore{ + DSLinker: dsl, + } + err = ds.PreSign(ownerSigner) + if err != nil { + t.Fatal(err) + } + err = ds.ValidatePreSignature() + if err != nil { + t.Fatal(err) + } + + ds2 := &DataStore{} + dsBytes, err := ds.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = ds2.UnmarshalBinary(dsBytes) + if err != nil { + t.Fatal(err) + } + dsEqual(t, ds, ds2) + utxInputs := &TXOut{} + err = utxInputs.NewDataStore(ds) + if err != nil { + t.Fatal(err) + } + return utxInputs +} + func TestTx(t *testing.T) { + msg := makeMockStorageGetter() + storage := makeStorage(msg) ownerSigner := &crypto.Secp256k1Signer{} if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { @@ -153,7 +279,7 @@ func TestTx(t *testing.T) { if err != nil { t.Fatal(err) } - err = tx.ValidateEqualVinVout(consumedUTXOs, 1) + err = tx.ValidateEqualVinVout(1, consumedUTXOs) if err != nil { t.Fatal(err) } @@ -169,13 +295,13 @@ func TestTx(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = tx2.Validate(nil, 1, consumedUTXOs) + _, err = tx2.Validate(nil, 1, consumedUTXOs, storage) if err != nil { t.Fatal(err) } txVec := TxVec([]*Tx{tx}) - err = txVec.Validate(1, consumedUTXOs) + err = txVec.Validate(1, consumedUTXOs, storage) if err != nil { t.Fatal(err) } @@ -232,7 +358,10 @@ func TestTx(t *testing.T) { } signer := &crypto.BNSigner{} - signer.SetPrivk(privk) + err = signer.SetPrivk(privk) + if err != nil { + t.Fatal(err) + } pubk, _ := signer.Pubkey() account := crypto.GetAccount(pubk) @@ -251,6 +380,7 @@ func TestTx(t *testing.T) { t.Fatal(err) } + fee := new(uint256.Uint256) val, err := new(uint256.Uint256).FromUint64(300) if err != nil { t.Fatal(err) @@ -265,6 +395,7 @@ func TestTx(t *testing.T) { CurveSpec: constants.CurveBN256Eth, Account: account, }, + Fee: fee.Clone(), }, } @@ -322,10 +453,1282 @@ func TestTx(t *testing.T) { } -func TestTxConsumedPreHash(t *testing.T) { +func TestTxMarshalGood(t *testing.T) { tx := &Tx{} - _, err := tx.ConsumedPreHash() + + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + err = utxo1.valueStore.Sign(txin1, ownerSigner) + if err != nil { + t.Fatal(err) + } + + utxo2 := makeVSWithValueFee(t, ownerSigner, 2, value, fee) + tx.Vin = []*TXIn{txin1} + tx.Vout = []*TXOut{utxo2} + + err = tx.SetTxHash() + if err != nil { + t.Fatal(err) + } + + txb, err := tx.MarshalBinary() + if err != nil { + t.Fatal(err) + } + tx2 := &Tx{} + err = tx2.UnmarshalBinary(txb) + if err != nil { + t.Fatal(err) + } +} + +func TestTxMarshalBad1(t *testing.T) { + tx := &Tx{} + txb, err := tx.MarshalBinary() + if err != nil { + t.Fatal(err) + } + tx2 := &Tx{} + err = tx2.UnmarshalBinary(txb) if err == nil { - t.Fatal("Should raise an error") + t.Fatal("Should have raised error") + } +} + +func TestTxMarshalBad2(t *testing.T) { + txin := &TXIn{} + vin := Vin{} + vout := Vout{} + for i := 0; i < constants.MaxTxVectorLength+1; i++ { + vin = append(vin, txin) + } + tx := &Tx{ + Vin: vin, + Vout: vout, + } + _, err := tx.MarshalBinary() + if err == nil { + t.Fatal("Should have raised error (1)") + } + + utxo := &TXOut{} + vin = Vin{} + vout = Vout{} + for i := 0; i < constants.MaxTxVectorLength+1; i++ { + vout = append(vout, utxo) + } + tx = &Tx{ + Vin: vin, + Vout: vout, + } + _, err = tx.MarshalBinary() + if err == nil { + t.Fatal("Should have raised error (2)") + } +} + +func TestTxValidateChainIDBad1(t *testing.T) { + chainID := uint32(1) + tx := &Tx{} + err := tx.ValidateChainID(chainID) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateChainIDBad2(t *testing.T) { + chainID := uint32(0) + tx := &Tx{ + Vin: Vin{&TXIn{}}, + Vout: Vout{&TXOut{}}, + } + err := tx.ValidateChainID(chainID) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxCannotBeMinedUntilBad(t *testing.T) { + tx := &Tx{} + _, err := tx.CannotBeMinedUntil() + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateIssuedAtForMiningGood(t *testing.T) { + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + err = utxo1.valueStore.Sign(txin1, ownerSigner) + if err != nil { + t.Fatal(err) + } + + tx := &Tx{} + utxo2 := makeVSWithValueFee(t, ownerSigner, 2, value, fee) + tx.Vin = []*TXIn{txin1} + tx.Vout = []*TXOut{utxo2} + + currentHeight := uint32(1) + err = tx.ValidateIssuedAtForMining(currentHeight) + if err != nil { + t.Fatal(err) + } +} + +func TestTxEpochOfExpirationForMiningBad(t *testing.T) { + tx := &Tx{} + _, err := tx.EpochOfExpirationForMining() + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxEpochOfExpirationForMiningGood(t *testing.T) { + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + err = utxo1.valueStore.Sign(txin1, ownerSigner) + if err != nil { + t.Fatal(err) + } + + tx := &Tx{} + utxo2 := makeVSWithValueFee(t, ownerSigner, 2, value, fee) + tx.Vin = []*TXIn{txin1} + tx.Vout = []*TXOut{utxo2} + + height, err := tx.EpochOfExpirationForMining() + if err != nil { + t.Fatal(err) + } + if height != constants.MaxUint32 { + t.Fatal("Incorrect height") + } +} + +func TestTxValidateIssuedAtForMiningBad(t *testing.T) { + currentHeight := uint32(1) + tx := &Tx{} + err := tx.ValidateIssuedAtForMining(currentHeight) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateUniqueGood(t *testing.T) { + tx := &Tx{} + + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + err = utxo1.valueStore.Sign(txin1, ownerSigner) + if err != nil { + t.Fatal(err) + } + + utxo2 := makeVSWithValueFee(t, ownerSigner, 2, value, fee) + tx.Vin = []*TXIn{txin1} + tx.Vout = []*TXOut{utxo2} + + _, err = tx.ValidateUnique(nil) + if err != nil { + t.Fatal(err) + } +} + +func TestTxValidateUniqueBad1(t *testing.T) { + tx := &Tx{} + + txin1 := &TXIn{} + tx.Vin = []*TXIn{txin1} + + _, err := tx.ValidateUnique(nil) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateUniqueBad2(t *testing.T) { + tx := &Tx{} + + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + err = utxo1.valueStore.Sign(txin1, ownerSigner) + if err != nil { + t.Fatal(err) + } + + utxo2 := &TXOut{} + tx.Vin = []*TXIn{txin1} + tx.Vout = []*TXOut{utxo2} + + _, err = tx.ValidateUnique(nil) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateUniqueBad3(t *testing.T) { + tx := &Tx{} + + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + err = utxo1.valueStore.Sign(txin1, ownerSigner) + if err != nil { + t.Fatal(err) + } + + utxo2 := makeVSWithValueFee(t, ownerSigner, 2, value, fee) + tx.Vin = []*TXIn{txin1, txin1} + tx.Vout = []*TXOut{utxo2} + + _, err = tx.ValidateUnique(nil) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateUniqueBad4(t *testing.T) { + tx := &Tx{} + + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + err = utxo1.valueStore.Sign(txin1, ownerSigner) + if err != nil { + t.Fatal(err) + } + + utxo2 := makeVSWithValueFee(t, ownerSigner, 2, value, fee) + tx.Vin = []*TXIn{txin1} + tx.Vout = []*TXOut{utxo2, utxo2} + + _, err = tx.ValidateUnique(nil) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateDataStoreIndexesGood1(t *testing.T) { + tx := &Tx{} + _, err := tx.ValidateDataStoreIndexes(nil) + if err != nil { + t.Fatal(err) + } +} + +func TestTxValidateDataStoreIndexesGood2(t *testing.T) { + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + index := make([]byte, constants.HashLen) + index[0] = 1 + + fee := uint256.Zero() + rawData := make([]byte, 1) + iat := uint32(1) + numEpochs := uint32(1) + utxo1 := makeDSWithValueFee(t, ownerSigner, 0, rawData, index, iat, numEpochs, fee) + + tx := &Tx{ + Vout: Vout{utxo1}, + } + _, err := tx.ValidateDataStoreIndexes(nil) + if err != nil { + t.Fatal(err) + } +} + +func TestTxValidateDataStoreIndexesBad1(t *testing.T) { + utxo := &TXOut{} + utxo.hasDataStore = true + tx := &Tx{ + Vout: Vout{utxo}, + } + _, err := tx.ValidateDataStoreIndexes(nil) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateDataStoreIndexesBad2(t *testing.T) { + ds := &DataStore{} + utxo := &TXOut{} + utxo.NewDataStore(ds) + tx := &Tx{ + Vout: Vout{utxo}, + } + _, err := tx.ValidateDataStoreIndexes(nil) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateDataStoreIndexesBad3(t *testing.T) { + ds := &DataStore{} + ds.DSLinker = &DSLinker{} + ds.DSLinker.DSPreImage = &DSPreImage{} + ds.DSLinker.DSPreImage.Index = make([]byte, constants.HashLen) + utxo := &TXOut{} + utxo.NewDataStore(ds) + tx := &Tx{ + Vout: Vout{utxo}, + } + _, err := tx.ValidateDataStoreIndexes(nil) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateDataStoreIndexesBad4(t *testing.T) { + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + index := make([]byte, constants.HashLen) + index[0] = 1 + + fee := uint256.Zero() + rawData := make([]byte, 1) + iat := uint32(1) + numEpochs := uint32(1) + utxo1 := makeDSWithValueFee(t, ownerSigner, 0, rawData, index, iat, numEpochs, fee) + + tx := &Tx{ + Vout: Vout{utxo1, utxo1}, + } + _, err := tx.ValidateDataStoreIndexes(nil) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxConsumedPreHash(t *testing.T) { + tx := &Tx{} + _, err := tx.ConsumedPreHash() + if err == nil { + t.Fatal("Should raise an error") + } +} + +func TestTxConsumedUTXOID(t *testing.T) { + tx := &Tx{} + _, err := tx.ConsumedUTXOID() + if err == nil { + t.Fatal("Should raise an error") + } +} + +func TestTxGeneratedUTXOID(t *testing.T) { + tx := &Tx{} + _, err := tx.GeneratedUTXOID() + if err == nil { + t.Fatal("Should raise an error") + } +} + +func TestTxGeneratedPreHash(t *testing.T) { + tx := &Tx{} + _, err := tx.GeneratedPreHash() + if err == nil { + t.Fatal("Should raise an error") + } +} + +func TestTxValidateSignature(t *testing.T) { + currentHeight := uint32(1) + refUTXOs := Vout{} + tx := &Tx{} + err := tx.ValidateSignature(currentHeight, refUTXOs) + if err == nil { + t.Fatal("Should raise an error") + } +} + +func TestTxValidatePreSignature(t *testing.T) { + tx := &Tx{} + err := tx.ValidatePreSignature() + if err == nil { + t.Fatal("Should raise an error") + } +} + +func TestTxCallTxHash(t *testing.T) { + tx := &Tx{} + hashTrue := crypto.Hasher([][]byte{}...) + hash, err := tx.TxHash() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(hash, hashTrue) { + t.Fatal("txhashes do not match") + } +} + +func TestTxCallTxHashBad1(t *testing.T) { + txin := &TXIn{} + tx := &Tx{ + Vin: Vin{txin}, + } + _, err := tx.TxHash() + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxCallTxHashBad2(t *testing.T) { + utxo := &TXOut{} + tx := &Tx{ + Vout: Vout{utxo}, + } + _, err := tx.TxHash() + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateFeesGood1(t *testing.T) { + msg := makeMockStorageGetter() + storage := makeStorage(msg) + + tx := &Tx{} + + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + utxo2 := makeVSWithValueFee(t, ownerSigner, 2, value, fee) + tx.Vin = []*TXIn{txin1} + tx.Vout = []*TXOut{utxo2} + + err = tx.ValidateFees(0, nil, storage) + if err != nil { + t.Fatal(err) + } +} + +func TestTxValidateFeesGood2(t *testing.T) { + tx := &Tx{} + + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + utxo2 := makeVSWithValueFee(t, ownerSigner, 2, value, fee) + tx.Vin = []*TXIn{txin1} + tx.Vout = []*TXOut{utxo2} + + msg := makeMockStorageGetter() + minTxFee := big.NewInt(1) + msg.SetMinTxFee(minTxFee) + storage := makeStorage(msg) + err = tx.ValidateFees(0, nil, storage) + if err != nil { + t.Fatal(err) + } +} + +func TestTxValidateFeesBad1(t *testing.T) { + msg := makeMockStorageGetter() + storage := makeStorage(msg) + + tx := &Tx{} + err := tx.ValidateFees(0, nil, storage) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateFeesBad2(t *testing.T) { + txin := &TXIn{} + utxo := &TXOut{} + tx := &Tx{ + Vin: []*TXIn{txin}, + Vout: []*TXOut{utxo}, + } + err := tx.ValidateFees(0, nil, nil) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateFeesBad3(t *testing.T) { + tx := &Tx{} + + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + txf1 := &TxFee{} + chainID := uint32(1) + txf1.New(chainID, value) + utxo1.NewTxFee(txf1) + txf2 := &TxFee{} + txf2.New(chainID, fee) + utxo2 := &TXOut{} + utxo2.NewTxFee(txf2) + + tx.Vin = []*TXIn{txin1} + tx.Vout = []*TXOut{utxo1, utxo2} + + msg := makeMockStorageGetter() + storage := makeStorage(msg) + err = tx.ValidateFees(0, nil, storage) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateFeesBad4(t *testing.T) { + tx := &Tx{} + + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + txf1 := &TxFee{} + chainID := uint32(1) + zero := new(uint256.Uint256) + txf1.New(chainID, zero) + utxo1.NewTxFee(txf1) + + tx.Vin = []*TXIn{txin1} + tx.Vout = []*TXOut{utxo1} + + msg := makeMockStorageGetter() + storage := makeStorage(msg) + err = tx.ValidateFees(0, nil, storage) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateFeesBad5(t *testing.T) { + tx := &Tx{} + + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + txf1 := &TxFee{} + chainID := uint32(1) + one := uint256.One() + txf1.New(chainID, one) + utxo1.NewTxFee(txf1) + + tx.Vin = []*TXIn{txin1} + tx.Vout = []*TXOut{utxo1} + + msg := makeMockStorageGetter() + minFeeBig := big.NewInt(2) + msg.SetMinTxFee(minFeeBig) + storage := makeStorage(msg) + err = tx.ValidateFees(0, nil, storage) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxIsCleanupTxBad1(t *testing.T) { + // Invalid Tx + tx := &Tx{} + currentHeight := uint32(0) + refUTXOs := Vout{} + cleanup := tx.IsCleanupTx(currentHeight, refUTXOs) + if cleanup { + t.Fatal("Should not be CleanupTx") + } +} + +func TestTxIsCleanupTxBad2(t *testing.T) { + // Invalid Vin; no DataStore + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + value64 := uint64(10000) + value, err := new(uint256.Uint256).FromUint64(value64) + if err != nil { + t.Fatal(err) + } + fee64 := uint64(0) + fee, err := new(uint256.Uint256).FromUint64(fee64) + if err != nil { + t.Fatal(err) + } + utxo1 := makeVSWithValueFee(t, ownerSigner, 1, value, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + // Invalid Vin; not DataStore + vin := []*TXIn{txin1} + refUTXOs := []*TXOut{utxo1} + tx := &Tx{ + Vin: vin, + Vout: Vout{}, + } + currentHeight := uint32(1) + cleanup := tx.IsCleanupTx(currentHeight, refUTXOs) + if cleanup { + t.Fatal("Should not be CleanupTx") + } +} + +func TestTxIsCleanupTxBad3(t *testing.T) { + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + index := make([]byte, constants.HashLen) + index[0] = 1 + + fee := uint256.Zero() + rawData := make([]byte, 1) + iat := uint32(1) + numEpochs := uint32(1) + utxo1 := makeDSWithValueFee(t, ownerSigner, 0, rawData, index, iat, numEpochs, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + // Invalid Vin; DataStore not expired + vin := []*TXIn{txin1} + refUTXOs := []*TXOut{utxo1} + tx := &Tx{ + Vin: vin, + Vout: Vout{}, + } + currentHeight := uint32(1) + cleanup := tx.IsCleanupTx(currentHeight, refUTXOs) + if cleanup { + t.Fatal("Should not be CleanupTx") + } +} + +func TestTxIsCleanupTxBad4(t *testing.T) { + // Must have valid Vin and invalid Vout (not present) + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + index := make([]byte, constants.HashLen) + index[0] = 1 + + fee := uint256.Zero() + rawData := make([]byte, 1) + iat := uint32(1) + numEpochs := uint32(1) + utxo1 := makeDSWithValueFee(t, ownerSigner, 0, rawData, index, iat, numEpochs, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + vin := []*TXIn{txin1} + refUTXOs := []*TXOut{utxo1} + tx := &Tx{ + Vin: vin, + Vout: Vout{}, + } + currentHeight := constants.EpochLength*(iat+numEpochs) + 1 + cleanup := tx.IsCleanupTx(currentHeight, refUTXOs) + if cleanup { + t.Fatal("Should not be CleanupTx") + } +} + +func TestTxIsCleanupTxBad5(t *testing.T) { + // Must have valid Vin and invalid Vout (incorrect utxo type) + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + index := make([]byte, constants.HashLen) + index[0] = 1 + + fee := uint256.Zero() + rawData := make([]byte, 1) + iat := uint32(1) + numEpochs := uint32(1) + utxo1 := makeDSWithValueFee(t, ownerSigner, 0, rawData, index, iat, numEpochs, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + utxo2 := &TXOut{} + ds := &DataStore{} + utxo2.NewDataStore(ds) + + vin := []*TXIn{txin1} + refUTXOs := []*TXOut{utxo1} + vout := Vout{utxo2} + tx := &Tx{ + Vin: vin, + Vout: vout, + } + currentHeight := constants.EpochLength*(iat+numEpochs) + 1 + cleanup := tx.IsCleanupTx(currentHeight, refUTXOs) + if cleanup { + t.Fatal("Should not be CleanupTx") + } +} + +func TestTxIsCleanupTxBad6(t *testing.T) { + // Must have valid Vin and invalid Vout (bad ValueStore) + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + index := make([]byte, constants.HashLen) + index[0] = 1 + + fee := uint256.Zero() + rawData := make([]byte, 1) + iat := uint32(1) + numEpochs := uint32(1) + utxo1 := makeDSWithValueFee(t, ownerSigner, 0, rawData, index, iat, numEpochs, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + utxo2 := &TXOut{} + vs := &ValueStore{} + utxo2.NewValueStore(vs) + + vin := []*TXIn{txin1} + refUTXOs := []*TXOut{utxo1} + vout := Vout{utxo2} + tx := &Tx{ + Vin: vin, + Vout: vout, + } + currentHeight := constants.EpochLength*(iat+numEpochs) + 1 + cleanup := tx.IsCleanupTx(currentHeight, refUTXOs) + if cleanup { + t.Fatal("Should not be CleanupTx") + } +} + +func TestTxIsCleanupTxBad7(t *testing.T) { + // Must have valid Vin and invalid Vout (bad ValueStore) + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + index := make([]byte, constants.HashLen) + index[0] = 1 + + fee := uint256.Zero() + rawData := make([]byte, 1) + iat := uint32(1) + numEpochs := uint32(1) + utxo1 := makeDSWithValueFee(t, ownerSigner, 0, rawData, index, iat, numEpochs, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + value := uint256.One() + vsFee := uint256.One() + utxo2 := makeVSWithValueFee(t, ownerSigner, 0, value, vsFee) + + vin := []*TXIn{txin1} + refUTXOs := []*TXOut{utxo1} + vout := Vout{utxo2} + tx := &Tx{ + Vin: vin, + Vout: vout, + } + currentHeight := constants.EpochLength*(iat+numEpochs) + 1 + cleanup := tx.IsCleanupTx(currentHeight, refUTXOs) + if cleanup { + t.Fatal("Should not be CleanupTx") + } +} + +func TestTxIsCleanupTxGood1(t *testing.T) { + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + fee := uint256.Zero() + index := make([]byte, constants.HashLen) + index[0] = 1 + rawData := make([]byte, 1) + iat := uint32(1) + numEpochs := uint32(1) + utxo1 := makeDSWithValueFee(t, ownerSigner, 0, rawData, index, iat, numEpochs, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + // Compute remainingValue to have correct ValueStore + currentHeight := constants.EpochLength*(iat+numEpochs) + 1 + remainingValue, err := utxo1.RemainingValue(currentHeight) + if err != nil { + t.Fatal(err) + } + utxo2 := makeVSWithValueFee(t, ownerSigner, 1, remainingValue, fee) + + vin := []*TXIn{txin1} + refUTXOs := []*TXOut{utxo1} + vout := []*TXOut{utxo2} + tx := &Tx{ + Vin: vin, + Vout: vout, + } + cleanup := tx.IsCleanupTx(currentHeight, refUTXOs) + if !cleanup { + t.Fatal("Should be valid CleanupTx") + } +} + +func TestTxIsCleanupTxGood2(t *testing.T) { + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + fee := uint256.Zero() + index1 := make([]byte, constants.HashLen) + index1[0] = 1 + rawData1 := make([]byte, 1) + iat := uint32(1) + numEpochs := uint32(1) + utxo1 := makeDSWithValueFee(t, ownerSigner, 0, rawData1, index1, iat, numEpochs, fee) + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + index2 := make([]byte, constants.HashLen) + index2[0] = 2 + rawData2 := make([]byte, 100) + utxo2 := makeDSWithValueFee(t, ownerSigner, 0, rawData2, index2, iat, numEpochs, fee) + txin2, err := utxo2.MakeTxIn() + if err != nil { + t.Fatal(err) + } + + // Compute remainingValue to have correct ValueStore + currentHeight := constants.EpochLength*(iat+numEpochs) + 1 + vin := []*TXIn{txin1, txin2} + refUTXOs := Vout{utxo1, utxo2} + remainingValue, err := refUTXOs.RemainingValue(currentHeight) + if err != nil { + t.Fatal(err) + } + utxo3 := makeVSWithValueFee(t, ownerSigner, 1, remainingValue, fee) + + vout := []*TXOut{utxo3} + tx := &Tx{ + Vin: vin, + Vout: vout, + } + cleanup := tx.IsCleanupTx(currentHeight, refUTXOs) + if !cleanup { + t.Fatal("Should be valid CleanupTx") + } +} + +func TestTxIsCleanupTxGood3(t *testing.T) { + // This does a full test of validation logic; + // these fees should not affect the validity of the cleanup transaction + // because no fees apply in this case. + msg := makeMockStorageGetter() + dsFeeBig := big.NewInt(100) + msg.SetDataStoreEpochFee(dsFeeBig) + vsFeeBig := big.NewInt(1000) + msg.SetValueStoreFee(vsFeeBig) + tfFeeBig := big.NewInt(10000) + msg.SetMinTxFee(tfFeeBig) + msg.SetDataStoreEpochFee(dsFeeBig) + storage := makeStorage(msg) + + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + dsFee, err := new(uint256.Uint256).FromBigInt(dsFeeBig) + if err != nil { + t.Fatal(err) + } + index1 := make([]byte, constants.HashLen) + index1[0] = 1 + rawData1 := make([]byte, 1) + iat := uint32(1) + numEpochs := uint32(3) + utxo1 := makeDSWithValueFee(t, ownerSigner, 0, rawData1, index1, iat, numEpochs, dsFee) + idx := uint32(0) + err = utxo1.SetTXOutIdx(idx) + if err != nil { + t.Fatal(err) + } + txin1, err := utxo1.MakeTxIn() + if err != nil { + t.Fatal(err) + } + err = utxo1.dataStore.Sign(txin1, ownerSigner) + if err != nil { + t.Fatal(err) + } + + index2 := make([]byte, constants.HashLen) + index2[0] = 2 + rawData2 := make([]byte, 2) + utxo2 := makeDSWithValueFee(t, ownerSigner, 0, rawData2, index2, iat, numEpochs, dsFee) + idx = uint32(1) + err = utxo2.SetTXOutIdx(idx) + if err != nil { + t.Fatal(err) + } + txin2, err := utxo2.MakeTxIn() + if err != nil { + t.Fatal(err) + } + err = utxo2.dataStore.Sign(txin2, ownerSigner) + if err != nil { + t.Fatal(err) + } + + index3 := make([]byte, constants.HashLen) + index3[0] = 3 + rawData3 := make([]byte, 3) + utxo3 := makeDSWithValueFee(t, ownerSigner, 0, rawData3, index3, iat, numEpochs, dsFee) + idx = uint32(2) + err = utxo3.SetTXOutIdx(idx) + if err != nil { + t.Fatal(err) + } + txin3, err := utxo3.MakeTxIn() + if err != nil { + t.Fatal(err) + } + err = utxo3.dataStore.Sign(txin3, ownerSigner) + if err != nil { + t.Fatal(err) + } + + // Compute remainingValue to have correct ValueStore + cleanupFee := uint256.Zero() + currentHeight := constants.EpochLength*(iat+numEpochs) + 1 + vin := []*TXIn{txin1, txin2, txin3} + refUTXOs := Vout{utxo1, utxo2, utxo3} + remainingValue, err := refUTXOs.RemainingValue(currentHeight) + if err != nil { + t.Fatal(err) + } + utxo4 := makeVSWithValueFee(t, ownerSigner, 1, remainingValue, cleanupFee) + + vout := []*TXOut{utxo4} + tx := &Tx{ + Vin: vin, + Vout: vout, + } + err = tx.SetTxHash() + if err != nil { + t.Fatal(err) + } + + chainID := uint32(2) + err = tx.PreValidatePending(chainID) + if err != nil { + t.Fatal(err) + } + _, err = tx.Validate(nil, currentHeight, refUTXOs, storage) + if err != nil { + t.Fatal(err) + } + err = tx.PostValidatePending(currentHeight, refUTXOs) + if err != nil { + t.Fatal(err) + } +} + +func TestTxValidateEqualVinVoutBad1(t *testing.T) { + currentHeight := uint32(0) + refUTXOs := Vout{} + tx := &Tx{} + err := tx.ValidateEqualVinVout(currentHeight, refUTXOs) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateEqualVinVoutBad2(t *testing.T) { + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + txin := &TXIn{} + utxo := &TXOut{} + + currentHeight := uint32(1) + refUTXOs := Vout{} + tx := &Tx{ + Vin: Vin{txin}, + Vout: Vout{utxo}, + } + err := tx.ValidateEqualVinVout(currentHeight, refUTXOs) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestTxValidateEqualVinVoutBad3(t *testing.T) { + ownerSigner := &crypto.Secp256k1Signer{} + if err := ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))); err != nil { + t.Fatal(err) + } + + txin := &TXIn{} + utxo := &TXOut{} + vs := &ValueStore{} + zero := uint256.Zero() + one := uint256.One() + vs.VSPreImage = &VSPreImage{} + vs.VSPreImage.Value = one.Clone() + vs.VSPreImage.Fee = zero.Clone() + err := utxo.NewValueStore(vs) + if err != nil { + t.Fatal(err) + } + + currentHeight := uint32(1) + tx := &Tx{ + Vin: Vin{txin}, + Vout: Vout{utxo}, + } + err = tx.ValidateEqualVinVout(currentHeight, nil) + if err == nil { + t.Fatal("Should have raised error") } } diff --git a/application/objs/txfee/txfee.go b/application/objs/txfee/txfee.go new file mode 100644 index 00000000..8df6bade --- /dev/null +++ b/application/objs/txfee/txfee.go @@ -0,0 +1,55 @@ +package txfee + +import ( + mdefs "github.com/MadBase/MadNet/application/objs/capn" + "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/errorz" + "github.com/MadBase/MadNet/utils" + capnp "zombiezen.com/go/capnproto2" +) + +// Marshal will marshal the TxFee object. +func Marshal(v mdefs.TxFee) ([]byte, error) { + raw, err := capnp.Canonicalize(v.Struct) + if err != nil { + return nil, err + } + out := utils.CopySlice(raw) + return out, nil +} + +// Unmarshal will unmarshal the TxFee object. +func Unmarshal(data []byte) (mdefs.TxFee, error) { + var err error + fn := func() (mdefs.TxFee, error) { + defer func() { + if r := recover(); r != nil { + err = errorz.ErrInvalid{}.New("bad serialization") + } + }() + dataCopy := utils.CopySlice(data) + msg := &capnp.Message{Arena: capnp.SingleSegment(dataCopy)} + obj, tmp := mdefs.ReadRootTxFee(msg) + err = tmp + return obj, err + } + obj, err := fn() + if err != nil { + return mdefs.TxFee{}, err + } + return obj, nil +} + +// Validate will validate the TxFee object +func Validate(v mdefs.TxFee) error { + if !v.HasTFPreImage() { + return errorz.ErrInvalid{}.New("txfee capn obj does not have TFPreImage") + } + if !v.HasTxHash() { + return errorz.ErrInvalid{}.New("txfee capn obj does not have TxHash") + } + if len(v.TxHash()) != constants.HashLen { + return errorz.ErrInvalid{}.New("txfee capn obj is not valid: invalid TxHash; incorrect byte length") + } + return nil +} diff --git a/application/objs/txin.go b/application/objs/txin.go index 3ddb552c..49c0cc23 100644 --- a/application/objs/txin.go +++ b/application/objs/txin.go @@ -117,7 +117,7 @@ func (b *TXIn) TxHash() ([]byte, error) { if b == nil || b.TXInLinker == nil || len(b.TXInLinker.TxHash) != constants.HashLen { return nil, errorz.ErrInvalid{}.New("not initialized") } - return b.TXInLinker.TxHash, nil + return utils.CopySlice(b.TXInLinker.TxHash), nil } // SetTxHash sets the TxHash of the TXIn object diff --git a/application/objs/txin_test.go b/application/objs/txin_test.go index 213c3a40..ade81894 100644 --- a/application/objs/txin_test.go +++ b/application/objs/txin_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/utils" ) func TestTXInGood(t *testing.T) { @@ -160,6 +161,16 @@ func TestTXInTxHash(t *testing.T) { if err == nil { t.Fatal("Should raise an error (2)") } + + txHashTrue := make([]byte, constants.HashLen) + txIn.TXInLinker.TxHash = utils.CopySlice(txHashTrue) + txHash, err := txIn.TxHash() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(txHash, txHashTrue) { + t.Fatal("TxHashes do not match") + } } func TestTXInSetTxHash(t *testing.T) { @@ -170,17 +181,22 @@ func TestTXInSetTxHash(t *testing.T) { t.Fatal("Should raise an error (1)") } - txHash := make([]byte, constants.HashLen) - err = txIn.SetTxHash(txHash) + txIn.TXInLinker = &TXInLinker{} + err = txIn.SetTxHash(txHashBad) if err == nil { t.Fatal("Should raise an error (2)") } - txIn.TXInLinker = &TXInLinker{} + txHash := make([]byte, constants.HashLen) err = txIn.SetTxHash(txHash) if err == nil { t.Fatal("Should raise an error (3)") } + + err = txIn.SetTxHash(txHash) + if err == nil { + t.Fatal("Should raise an error (4)") + } } func TestTXInChainID(t *testing.T) { diff --git a/application/objs/txinl.go b/application/objs/txinl.go index 1d080a13..b35cff7b 100644 --- a/application/objs/txinl.go +++ b/application/objs/txinl.go @@ -145,5 +145,5 @@ func (b *TXInLinker) ConsumedTxHash() ([]byte, error) { if b == nil || b.TXInPreImage == nil || len(b.TXInPreImage.ConsumedTxHash) != constants.HashLen { return nil, errorz.ErrInvalid{}.New("not initialized") } - return b.TXInPreImage.ConsumedTxHash, nil + return utils.CopySlice(b.TXInPreImage.ConsumedTxHash), nil } diff --git a/application/objs/txinl_test.go b/application/objs/txinl_test.go index a47c1777..3f7bc794 100644 --- a/application/objs/txinl_test.go +++ b/application/objs/txinl_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/utils" ) func TestTXInLinkerGood(t *testing.T) { @@ -175,6 +176,22 @@ func TestTXInLinkerChainID(t *testing.T) { if err == nil { t.Fatal("Should raise an error (2)") } + + txinl.TXInPreImage = &TXInPreImage{} + _, err = txinl.ChainID() + if err == nil { + t.Fatal("Should raise an error (3)") + } + + chainID := uint32(17) + txinl.TXInPreImage.ChainID = chainID + retChainID, err := txinl.ChainID() + if err != nil { + t.Fatal(err) + } + if retChainID != chainID { + t.Fatal("ChainIDs do not match") + } } func TestTXInLinkerSetTxHash(t *testing.T) { @@ -191,18 +208,18 @@ func TestTXInLinkerSetTxHash(t *testing.T) { t.Fatal("Should raise an error (2)") } - txHash := make([]byte, constants.HashLen) - err = txin.TXInLinker.SetTxHash(txHash) + txinl.TXInPreImage = &TXInPreImage{} + err = txinl.SetTxHash(txHashBad) if err == nil { t.Fatal("Should raise an error (3)") } - err = txinl.SetTxHash(txHash) + txHash := make([]byte, constants.HashLen) + err = txin.TXInLinker.SetTxHash(txHash) if err == nil { t.Fatal("Should raise an error (4)") } - txinl.TXInPreImage = &TXInPreImage{} err = txinl.SetTxHash(txHash) if err != nil { t.Fatal("Should not raise an error") @@ -221,6 +238,17 @@ func TestTXInLinkerConsumedTxIdx(t *testing.T) { if err == nil { t.Fatal("Should raise an error (2)") } + + consumedTxIdx := uint32(25519) + txinl.TXInPreImage = &TXInPreImage{} + txinl.TXInPreImage.ConsumedTxIdx = consumedTxIdx + txIdx, err := txinl.ConsumedTxIdx() + if err != nil { + t.Fatal(err) + } + if txIdx != consumedTxIdx { + t.Fatal("ConsumedTxIdxes do not match") + } } func TestTXInLinkerConsumedTxHash(t *testing.T) { @@ -235,4 +263,15 @@ func TestTXInLinkerConsumedTxHash(t *testing.T) { if err == nil { t.Fatal("Should raise an error (2)") } + + txhashTrue := make([]byte, constants.HashLen) + txinl.TXInPreImage = &TXInPreImage{} + txinl.TXInPreImage.ConsumedTxHash = utils.CopySlice(txhashTrue) + txhash, err := txinl.ConsumedTxHash() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(txhash, txhashTrue) { + t.Fatal("ConsumedTxHashes do not match") + } } diff --git a/application/objs/txvec.go b/application/objs/txvec.go index 071e1d59..12160020 100644 --- a/application/objs/txvec.go +++ b/application/objs/txvec.go @@ -1,5 +1,10 @@ package objs +import ( + "github.com/MadBase/MadNet/application/wrapper" + "github.com/MadBase/MadNet/constants" +) + // TxVec is a vector of transactions Tx type TxVec []*Tx @@ -67,10 +72,10 @@ func (txv TxVec) PreValidateApplyState(chainID uint32) error { } // Validate ... -func (txv TxVec) Validate(currentHeight uint32, consumedUTXOs Vout) error { +func (txv TxVec) Validate(currentHeight uint32, consumedUTXOs Vout, storage *wrapper.Storage) error { set := make(map[string]bool) - voutMap := make(map[[32]byte]*TXOut) - var key [32]byte + voutMap := make(map[[constants.HashLen]byte]*TXOut) + var key [constants.HashLen]byte for i := 0; i < len(consumedUTXOs); i++ { utxoID, err := consumedUTXOs[i].UTXOID() if err != nil { @@ -89,7 +94,7 @@ func (txv TxVec) Validate(currentHeight uint32, consumedUTXOs Vout) error { copy(key[:], utxoIDs[j]) utxoSet[j] = voutMap[key] } - if set, err = txv[i].Validate(set, currentHeight, utxoSet); err != nil { + if set, err = txv[i].Validate(set, currentHeight, utxoSet, storage); err != nil { return err } } diff --git a/application/objs/uint256/uint256.go b/application/objs/uint256/uint256.go index edbbf7df..9939cd83 100644 --- a/application/objs/uint256/uint256.go +++ b/application/objs/uint256/uint256.go @@ -146,6 +146,16 @@ func (u *Uint256) FromBigInt(a *big.Int) (*Uint256, error) { return u.Clone(), nil } +// ToBigInt converts Uint256 into big.Int +func (u *Uint256) ToBigInt() (*big.Int, error) { + buf, err := u.MarshalBinary() + if err != nil { + return nil, err + } + a := new(big.Int).SetBytes(buf) + return a, nil +} + // FromUint64 converts a uint64 into Uint256 func (u *Uint256) FromUint64(a uint64) (*Uint256, error) { buf := utils.MarshalUint64(a) @@ -386,6 +396,18 @@ func (u *Uint256) SetZero() *Uint256 { return u.Clone() } +// IsZero determines if u is 0 +func (u *Uint256) IsZero() bool { + if u.val == nil { + u.val = &uint256.Int{} + } + z := new(Uint256).SetZero() + if u.Eq(z) { + return true + } + return false +} + // DSPIMinDeposit returns constants.DSPIMinDeposit as Uint256 func DSPIMinDeposit() *Uint256 { u, _ := new(Uint256).FromUint64(uint64(constants.DSPIMinDeposit)) diff --git a/application/objs/uint256/uint256_test.go b/application/objs/uint256/uint256_test.go index be2840f4..3d0eef81 100644 --- a/application/objs/uint256/uint256_test.go +++ b/application/objs/uint256/uint256_test.go @@ -469,6 +469,46 @@ func TestUint256FromBigInt(t *testing.T) { } } +func TestUint256ToBigInt(t *testing.T) { + u := &Uint256{} + _, err := u.ToBigInt() + if err == nil { + t.Fatal("Should have raised error (1)") + } + + u.SetZero() + b, err := u.ToBigInt() + if err != nil { + t.Fatal(err) + } + if b.Sign() != 0 { + t.Fatal("b should be 0") + } + + bigOne := new(big.Int).SetUint64(1) + u.SetOne() + b, err = u.ToBigInt() + if err != nil { + t.Fatal(err) + } + if b.Cmp(bigOne) != 0 { + t.Fatal("b should be 1") + } + + big25519 := new(big.Int).SetUint64(25519) + _, err = u.FromUint64(25519) + if err != nil { + t.Fatal(err) + } + b, err = u.ToBigInt() + if err != nil { + t.Fatal(err) + } + if b.Cmp(big25519) != 0 { + t.Fatal("b should be 25519") + } +} + func TestUint256FromUint64(t *testing.T) { obj := &testObj{} _, err := obj.Value.FromUint64(0) @@ -2482,6 +2522,18 @@ func TestUint256Zero(t *testing.T) { } } +func TestUint256IsZero(t *testing.T) { + u := Zero() + if !u.IsZero() { + t.Fatal("IsZero() should return true") + } + + u = One() + if u.IsZero() { + t.Fatal("IsZero() should return false") + } +} + func TestUint256One(t *testing.T) { u := One() uTrue := new(Uint256).SetOne() diff --git a/application/objs/utxo.go b/application/objs/utxo.go index 52591f6e..35c50307 100644 --- a/application/objs/utxo.go +++ b/application/objs/utxo.go @@ -4,6 +4,7 @@ import ( mdefs "github.com/MadBase/MadNet/application/objs/capn" "github.com/MadBase/MadNet/application/objs/uint256" "github.com/MadBase/MadNet/application/objs/utxo" + "github.com/MadBase/MadNet/application/wrapper" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/utils" @@ -15,16 +16,18 @@ type TXOut struct { dataStore *DataStore valueStore *ValueStore atomicSwap *AtomicSwap + txFee *TxFee // not part of serialized object below this line hasDataStore bool hasValueStore bool hasAtomicSwap bool + hasTxFee bool } // CreateValueStore makes a new ValueStore -func (b *TXOut) CreateValueStore(chainID uint32, value *uint256.Uint256, acct []byte, curveSpec constants.CurveSpec, txHash []byte) error { +func (b *TXOut) CreateValueStore(chainID uint32, value *uint256.Uint256, fee *uint256.Uint256, acct []byte, curveSpec constants.CurveSpec, txHash []byte) error { vs := &ValueStore{} - err := vs.New(chainID, value, acct, curveSpec, txHash) + err := vs.New(chainID, value, fee, acct, curveSpec, txHash) if err != nil { return err } @@ -46,9 +49,11 @@ func (b *TXOut) NewDataStore(v *DataStore) error { b.hasDataStore = true b.hasValueStore = false b.hasAtomicSwap = false + b.hasTxFee = false b.dataStore = v b.atomicSwap = nil b.valueStore = nil + b.txFee = nil return nil } @@ -57,9 +62,11 @@ func (b *TXOut) NewValueStore(v *ValueStore) error { b.hasDataStore = false b.hasValueStore = true b.hasAtomicSwap = false + b.hasTxFee = false b.dataStore = nil b.valueStore = v b.atomicSwap = nil + b.txFee = nil return nil } @@ -68,9 +75,24 @@ func (b *TXOut) NewAtomicSwap(v *AtomicSwap) error { b.hasDataStore = false b.hasValueStore = false b.hasAtomicSwap = true + b.hasTxFee = false b.dataStore = nil b.valueStore = nil b.atomicSwap = v + b.txFee = nil + return nil +} + +// NewTxFee makes a TXOut object which with the specified TxFee +func (b *TXOut) NewTxFee(v *TxFee) error { + b.hasDataStore = false + b.hasValueStore = false + b.hasAtomicSwap = false + b.hasTxFee = true + b.dataStore = nil + b.valueStore = nil + b.atomicSwap = nil + b.txFee = v return nil } @@ -98,6 +120,14 @@ func (b *TXOut) HasAtomicSwap() bool { return b.hasAtomicSwap } +// HasTxFee specifies if the TXOut object has a TxFee +func (b *TXOut) HasTxFee() bool { + if b == nil { + return false + } + return b.hasTxFee +} + // DataStore returns the DataStore of the TXOut object if it exists func (b *TXOut) DataStore() (*DataStore, error) { if b.HasDataStore() { @@ -119,7 +149,15 @@ func (b *TXOut) AtomicSwap() (*AtomicSwap, error) { if b.HasAtomicSwap() { return b.atomicSwap, nil } - return nil, errorz.ErrInvalid{}.New("object does not have a AtomicSwap") + return nil, errorz.ErrInvalid{}.New("object does not have an AtomicSwap") +} + +// TxFee returns the TxFee of the TXOut object if it exists +func (b *TXOut) TxFee() (*TxFee, error) { + if b.HasTxFee() { + return b.txFee, nil + } + return nil, errorz.ErrInvalid{}.New("object does not have a TxFee") } // UnmarshalBinary takes a byte slice and returns the corresponding @@ -160,6 +198,12 @@ func (b *TXOut) UnmarshalCapn(bc mdefs.TXOut) error { } b.dataStore = obj b.hasDataStore = true + b.valueStore = nil + b.hasValueStore = false + b.atomicSwap = nil + b.hasAtomicSwap = false + b.txFee = nil + b.hasTxFee = false case bc.HasValueStore(): cObj, err := bc.ValueStore() if err != nil { @@ -170,8 +214,14 @@ func (b *TXOut) UnmarshalCapn(bc mdefs.TXOut) error { if err != nil { return err } + b.dataStore = nil + b.hasDataStore = false b.valueStore = obj b.hasValueStore = true + b.atomicSwap = nil + b.hasAtomicSwap = false + b.txFee = nil + b.hasTxFee = false case bc.HasAtomicSwap(): cObj, err := bc.AtomicSwap() if err != nil { @@ -182,8 +232,32 @@ func (b *TXOut) UnmarshalCapn(bc mdefs.TXOut) error { if err != nil { return err } + b.dataStore = nil + b.hasDataStore = false + b.valueStore = nil + b.hasValueStore = false b.atomicSwap = obj b.hasAtomicSwap = true + b.txFee = nil + b.hasTxFee = false + case bc.HasTxFee(): + cObj, err := bc.TxFee() + if err != nil { + return err + } + obj := &TxFee{} + err = obj.UnmarshalCapn(cObj) + if err != nil { + return err + } + b.dataStore = nil + b.hasDataStore = false + b.valueStore = nil + b.hasValueStore = false + b.atomicSwap = nil + b.hasAtomicSwap = false + b.txFee = obj + b.hasTxFee = true default: return errorz.ErrInvalid{}.New("TXOut type not defined in UnmarshalCapn") } @@ -239,6 +313,14 @@ func (b *TXOut) MarshalCapn(seg *capnp.Segment) (mdefs.TXOut, error) { if err := bc.SetAtomicSwap(as); err != nil { return bc, err } + case b.hasTxFee: + tf, err := b.txFee.MarshalCapn(seg) + if err != nil { + return bc, err + } + if err := bc.SetTxFee(tf); err != nil { + return bc, err + } default: return mdefs.TXOut{}, errorz.ErrInvalid{}.New("TXOut type not defined in MarshalCapn") } @@ -257,6 +339,9 @@ func (b *TXOut) PreHash() ([]byte, error) { case b.HasAtomicSwap(): obj, _ := b.AtomicSwap() return obj.PreHash() + case b.HasTxFee(): + obj, _ := b.TxFee() + return obj.PreHash() default: return nil, errorz.ErrInvalid{}.New("TXOut type not defined in PreHash") } @@ -274,6 +359,9 @@ func (b *TXOut) UTXOID() ([]byte, error) { case b.HasAtomicSwap(): obj, _ := b.AtomicSwap() return obj.UTXOID() + case b.HasTxFee(): + obj, _ := b.TxFee() + return obj.UTXOID() default: return nil, errorz.ErrInvalid{}.New("TXOut type not defined in UTXOID") } @@ -291,6 +379,9 @@ func (b *TXOut) ChainID() (uint32, error) { case b.HasAtomicSwap(): obj, _ := b.AtomicSwap() return obj.ChainID() + case b.HasTxFee(): + obj, _ := b.TxFee() + return obj.ChainID() default: return 0, errorz.ErrInvalid{}.New("TXOut type not defined for ChainID") } @@ -308,6 +399,9 @@ func (b *TXOut) TXOutIdx() (uint32, error) { case b.HasAtomicSwap(): obj, _ := b.AtomicSwap() return obj.TXOutIdx() + case b.HasTxFee(): + obj, _ := b.TxFee() + return obj.TXOutIdx() default: return 0, errorz.ErrInvalid{}.New("TXOut type not defined in TXOutIdx") } @@ -325,6 +419,9 @@ func (b *TXOut) SetTXOutIdx(idx uint32) error { case b.HasAtomicSwap(): obj, _ := b.AtomicSwap() return obj.SetTXOutIdx(idx) + case b.HasTxFee(): + obj, _ := b.TxFee() + return obj.SetTXOutIdx(idx) default: return errorz.ErrInvalid{}.New("TXOut type not defined in SetTXOutIdx") } @@ -352,6 +449,12 @@ func (b *TXOut) TxHash() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } return utils.CopySlice(obj.TxHash), nil + case b.HasTxFee(): + obj, _ := b.TxFee() + if obj == nil || len(obj.TxHash) != constants.HashLen { + return nil, errorz.ErrInvalid{}.New("not initialized") + } + return utils.CopySlice(obj.TxHash), nil default: return nil, errorz.ErrInvalid{}.New("TXOut type not defined in TxHash") } @@ -369,11 +472,30 @@ func (b *TXOut) SetTxHash(txHash []byte) error { case b.HasAtomicSwap(): obj, _ := b.AtomicSwap() return obj.SetTxHash(utils.CopySlice(txHash)) + case b.HasTxFee(): + obj, _ := b.TxFee() + return obj.SetTxHash(utils.CopySlice(txHash)) default: return errorz.ErrInvalid{}.New("TXOut type not defined in SetTxHash") } } +// IsExpired returns true if the utxo has expired +func (b *TXOut) IsExpired(currentHeight uint32) (bool, error) { + switch { + case b.HasDataStore(): + obj, _ := b.DataStore() + return obj.IsExpired(currentHeight) + case b.HasValueStore(): + return false, nil + case b.HasAtomicSwap(): + obj, _ := b.AtomicSwap() + return obj.IsExpired(currentHeight) + default: + return false, errorz.ErrInvalid{}.New("TXOut type not defined in IsExpired") + } +} + // RemainingValue returns the remaining value after discount func (b *TXOut) RemainingValue(currentHeight uint32) (*uint256.Uint256, error) { switch { @@ -421,7 +543,51 @@ func (b *TXOut) Value() (*uint256.Uint256, error) { obj, _ := b.AtomicSwap() return obj.Value() default: - return nil, errorz.ErrInvalid{}.New("TXOut type not defined in Next") + return nil, errorz.ErrInvalid{}.New("TXOut type not defined in Value") + } +} + +// ValuePlusFee returns the Value of the object plus the associated fee +func (b *TXOut) ValuePlusFee() (*uint256.Uint256, error) { + switch { + case b.HasDataStore(): + obj, _ := b.DataStore() + return obj.ValuePlusFee() + case b.HasValueStore(): + obj, _ := b.ValueStore() + return obj.ValuePlusFee() + case b.HasAtomicSwap(): + obj, _ := b.AtomicSwap() + return obj.ValuePlusFee() + case b.HasTxFee(): + // We return the fee because ValuePlusFee is called to calculate + // the total value out of the Tx; this must include TxFee. + obj, _ := b.TxFee() + return obj.Fee() + default: + return nil, errorz.ErrInvalid{}.New("TXOut type not defined in ValuePlusFee") + } +} + +// ValidateFee validates the Fee of the object +func (b *TXOut) ValidateFee(storage *wrapper.Storage) error { + switch { + case b.HasDataStore(): + obj, _ := b.DataStore() + return obj.ValidateFee(storage) + case b.HasValueStore(): + obj, _ := b.ValueStore() + return obj.ValidateFee(storage) + case b.HasAtomicSwap(): + obj, _ := b.AtomicSwap() + return obj.ValidateFee(storage) + case b.HasTxFee(): + // We cannot validate the total transaction fee here because + // we must look at the *entire* Tx object to ensure at most one TxFee + // UTXO is present + return nil + default: + return errorz.ErrInvalid{}.New("TXOut type not defined in ValidateFee") } } @@ -435,6 +601,8 @@ func (b *TXOut) ValidatePreSignature() error { return nil case b.HasAtomicSwap(): return nil + case b.HasTxFee(): + return nil default: return errorz.ErrInvalid{}.New("TXOut type not defined in ValidatePreSignature") } @@ -452,6 +620,8 @@ func (b *TXOut) ValidateSignature(currentHeight uint32, txIn *TXIn) error { case b.HasAtomicSwap(): obj, _ := b.AtomicSwap() return obj.ValidateSignature(currentHeight, txIn) + case b.HasTxFee(): + return nil default: return errorz.ErrInvalid{}.New("TXOut type not defined in ValidateSignature") } @@ -476,6 +646,8 @@ func (b *TXOut) MustBeMinedBeforeHeight() (uint32, error) { return 0, err } return (iat * constants.EpochLength) - 1, nil + case b.HasTxFee(): + return constants.MaxUint32, nil default: return 0, errorz.ErrInvalid{}.New("TXOut type not defined in MustBeMinedBeforeHeight") } @@ -490,9 +662,6 @@ func (b *TXOut) CannotBeMinedBeforeHeight() (uint32, error) { if err != nil { return 0, err } - if iat == 0 { - return 0, errorz.ErrInvalid{}.New("invalid IssuedAt in utxo") - } return (iat-1)*constants.EpochLength + 1, nil case b.HasValueStore(): return 1, nil @@ -502,10 +671,9 @@ func (b *TXOut) CannotBeMinedBeforeHeight() (uint32, error) { if err != nil { return 0, err } - if iat == 0 { - return 0, errorz.ErrInvalid{}.New("invalid IssuedAt in utxo") - } return (iat-1)*constants.EpochLength + 1, nil + case b.HasTxFee(): + return 1, nil default: return 0, errorz.ErrInvalid{}.New("TXOut type not defined in MustBeMinedBeforeHeight") } diff --git a/application/objs/utxo_test.go b/application/objs/utxo_test.go index 05153d1b..1e083baf 100644 --- a/application/objs/utxo_test.go +++ b/application/objs/utxo_test.go @@ -15,16 +15,20 @@ func TestUTXOCreateValueStore(t *testing.T) { if err != nil { t.Fatal(err) } + fee, err := new(uint256.Uint256).FromUint64(0) + if err != nil { + t.Fatal(err) + } acct := make([]byte, constants.OwnerLen) curveSpec := constants.CurveSecp256k1 txHash := make([]byte, constants.HashLen) utxo := &TXOut{} - err = utxo.CreateValueStore(chainID, value, acct, curveSpec, txHash) + err = utxo.CreateValueStore(chainID, value, fee, acct, curveSpec, txHash) if err == nil { t.Fatal("Should have raised error") } chainID = 1 - err = utxo.CreateValueStore(chainID, value, acct, curveSpec, txHash) + err = utxo.CreateValueStore(chainID, value, fee, acct, curveSpec, txHash) if err != nil { t.Fatal(err) } @@ -83,6 +87,7 @@ func TestUTXODataStoreGood(t *testing.T) { RawData: rawdata, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) dsl := &DSLinker{ @@ -111,6 +116,9 @@ func TestUTXODataStoreGood(t *testing.T) { if utxo.HasAtomicSwap() { t.Fatal("Should not have AtomicSwap!") } + if utxo.HasTxFee() { + t.Fatal("Should not have TxFee!") + } dsCopy, err := utxo.DataStore() if err != nil { @@ -128,26 +136,12 @@ func TestUTXODataStoreGood(t *testing.T) { t.Fatal("Should raise error for no AtomicSwap!") } - owner2 := &ValueStoreOwner{} - owner2.New(ownerAcct, constants.CurveSecp256k1) - - val, err := new(uint256.Uint256).FromUint64(1) - if err != nil { - t.Fatal(err) - } - utxo2 := &TXOut{} - err = utxo2.NewValueStore(&ValueStore{ - VSPreImage: &VSPreImage{ - ChainID: 1, - Value: val, - TXOutIdx: 0, - Owner: owner2, - }, - }) - if err != nil { - t.Fatal(err) + _, err = utxo.TxFee() + if err == nil { + t.Fatal("Should raise error for no TxFee!") } + utxo2 := &TXOut{} utxoBytes, err := utxo.MarshalBinary() if err != nil { t.Fatal(err) @@ -184,6 +178,7 @@ func TestUTXOValueStoreGood(t *testing.T) { Value: val, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) vs := &ValueStore{ @@ -206,6 +201,9 @@ func TestUTXOValueStoreGood(t *testing.T) { if utxo.HasAtomicSwap() { t.Fatal("Should not have AtomicSwap!") } + if utxo.HasTxFee() { + t.Fatal("Should not have TxFee!") + } _, err = utxo.DataStore() if err == nil { @@ -222,6 +220,21 @@ func TestUTXOValueStoreGood(t *testing.T) { if err == nil { t.Fatal("Should raise error for no AtomicSwap!") } + + _, err = utxo.TxFee() + if err == nil { + t.Fatal("Should raise error for no TxFee!") + } + + utxo2 := &TXOut{} + utxoBytes, err := utxo.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = utxo2.UnmarshalBinary(utxoBytes) + if err != nil { + t.Fatal(err) + } } func TestUTXOAtomicSwapGood(t *testing.T) { @@ -281,6 +294,9 @@ func TestUTXOAtomicSwapGood(t *testing.T) { if !utxo.HasAtomicSwap() { t.Fatal("Should have AtomicSwap!") } + if utxo.HasTxFee() { + t.Fatal("Should not have TxFee!") + } _, err = utxo.DataStore() if err == nil { @@ -297,6 +313,78 @@ func TestUTXOAtomicSwapGood(t *testing.T) { t.Fatal(err) } asEqual(t, as, asCopy) + + _, err = utxo.TxFee() + if err == nil { + t.Fatal("Should raise error for TxFee!") + } +} + +func TestUTXOTxFeeGood(t *testing.T) { + cid := uint32(2) + fee, err := new(uint256.Uint256).FromUint64(65537) + if err != nil { + t.Fatal(err) + } + txoid := uint32(17) + + tf := &TxFee{} + tf.TFPreImage = &TFPreImage{ + ChainID: cid, + TXOutIdx: txoid, + Fee: fee.Clone(), + } + tf.TxHash = make([]byte, constants.HashLen) + + utxo := &TXOut{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + + if utxo.HasDataStore() { + t.Fatal("Should not have DataStore!") + } + if utxo.HasValueStore() { + t.Fatal("Should not have ValueStore!") + } + if utxo.HasAtomicSwap() { + t.Fatal("Should not have AtomicSwap!") + } + if !utxo.HasTxFee() { + t.Fatal("Should have TxFee!") + } + + _, err = utxo.DataStore() + if err == nil { + t.Fatal("Should raise error for DataStore!") + } + + _, err = utxo.ValueStore() + if err == nil { + t.Fatal("Should raise error for ValueStore!") + } + + _, err = utxo.AtomicSwap() + if err == nil { + t.Fatal("Should raise error for AtomicSwap!") + } + + tfCopy, err := utxo.TxFee() + if err != nil { + t.Fatal(err) + } + tfEqual(t, tf, tfCopy) + + utxo2 := &TXOut{} + utxoBytes, err := utxo.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = utxo2.UnmarshalBinary(utxoBytes) + if err != nil { + t.Fatal(err) + } } func TestUTXOMarshalBinary(t *testing.T) { @@ -335,6 +423,16 @@ func TestUTXOMarshalBinary(t *testing.T) { if err == nil { t.Fatal("Should have raised error (4)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.MarshalBinary() + if err == nil { + t.Fatal("Should have raised error (5)") + } } func TestUTXOUnmarshalBinary(t *testing.T) { @@ -382,6 +480,16 @@ func TestUTXOPreHash(t *testing.T) { if err == nil { t.Fatal("Should have raised error (4)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.PreHash() + if err == nil { + t.Fatal("Should have raised error (5)") + } } func TestUTXOUTXOID(t *testing.T) { @@ -420,6 +528,16 @@ func TestUTXOUTXOID(t *testing.T) { if err == nil { t.Fatal("Should have raised error (4)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.UTXOID() + if err == nil { + t.Fatal("Should have raised error (5)") + } } func TestUTXOChainID(t *testing.T) { @@ -458,6 +576,16 @@ func TestUTXOChainID(t *testing.T) { if err == nil { t.Fatal("Should have raised error (4)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.ChainID() + if err == nil { + t.Fatal("Should have raised error (5)") + } } func TestUTXOTXOutIdx(t *testing.T) { @@ -496,6 +624,16 @@ func TestUTXOTXOutIdx(t *testing.T) { if err == nil { t.Fatal("Should have raised error (4)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.TXOutIdx() + if err == nil { + t.Fatal("Should have raised error (5)") + } } func TestUTXOSetTXOutIdx(t *testing.T) { @@ -535,6 +673,16 @@ func TestUTXOSetTXOutIdx(t *testing.T) { if err == nil { t.Fatal("Should have raised error (4)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + err = utxo.SetTXOutIdx(idx) + if err == nil { + t.Fatal("Should have raised error (5)") + } } func TestUTXOTxHash(t *testing.T) { @@ -601,6 +749,25 @@ func TestUTXOTxHash(t *testing.T) { if !bytes.Equal(txHash, txHashTrue) { t.Fatal("txHash does not match (4)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.TxHash() + if err == nil { + t.Fatal("Should have raised error (5)") + } + tf.TFPreImage = &TFPreImage{} + tf.TxHash = txHashTrue + txHash, err = utxo.TxHash() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(txHash, txHashTrue) { + t.Fatal("txHash does not match (5)") + } } func TestUTXOSetTxHash(t *testing.T) { @@ -646,6 +813,16 @@ func TestUTXOSetTxHash(t *testing.T) { if err == nil { t.Fatal("Should have raised error (5)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + err = utxo.SetTxHash(txHash) + if err == nil { + t.Fatal("Should have raised error (6)") + } } func TestUTXORemainingValue(t *testing.T) { @@ -685,6 +862,16 @@ func TestUTXORemainingValue(t *testing.T) { if err == nil { t.Fatal("Should have raised error (4)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.RemainingValue(currentHeight) + if err == nil { + t.Fatal("Should have raised error (5)") + } } func TestUTXOMakeTxIn(t *testing.T) { @@ -723,6 +910,16 @@ func TestUTXOMakeTxIn(t *testing.T) { if err == nil { t.Fatal("Should have raised error (4)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.MakeTxIn() + if err == nil { + t.Fatal("Should have raised error (5)") + } } func TestUTXOValue(t *testing.T) { @@ -761,6 +958,64 @@ func TestUTXOValue(t *testing.T) { if err == nil { t.Fatal("Should have raised error (4)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.Value() + if err == nil { + t.Fatal("Should have raised error (5)") + } +} + +func TestUTXOValuePlusFee(t *testing.T) { + utxo := &TXOut{} + _, err := utxo.ValuePlusFee() + if err == nil { + t.Fatal("Should have raised error (1)") + } + + as := &AtomicSwap{} + err = utxo.NewAtomicSwap(as) + if err != nil { + t.Fatal(err) + } + _, err = utxo.ValuePlusFee() + if err == nil { + t.Fatal("Should have raised error (2)") + } + + ds := &DataStore{} + err = utxo.NewDataStore(ds) + if err != nil { + t.Fatal(err) + } + _, err = utxo.ValuePlusFee() + if err == nil { + t.Fatal("Should have raised error (3)") + } + + vs := &ValueStore{} + err = utxo.NewValueStore(vs) + if err != nil { + t.Fatal(err) + } + _, err = utxo.ValuePlusFee() + if err == nil { + t.Fatal("Should have raised error (4)") + } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.ValuePlusFee() + if err == nil { + t.Fatal("Should have raised error (5)") + } } func TestUTXOValidatePreSignature(t *testing.T) { @@ -799,6 +1054,16 @@ func TestUTXOValidatePreSignature(t *testing.T) { if err != nil { t.Fatal("Should pass (2)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + err = utxo.ValidatePreSignature() + if err != nil { + t.Fatal("Should pass (3)") + } } func TestUTXOValidateSignature(t *testing.T) { @@ -839,6 +1104,16 @@ func TestUTXOValidateSignature(t *testing.T) { if err == nil { t.Fatal("Should have raised error (4)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + err = utxo.ValidateSignature(currentHeight, txIn) + if err != nil { + t.Fatal("Should have passed") + } } func TestUTXOMustBeMinedBeforeHeight(t *testing.T) { @@ -899,6 +1174,16 @@ func TestUTXOMustBeMinedBeforeHeight(t *testing.T) { if err != nil { t.Fatal(err) } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.MustBeMinedBeforeHeight() + if err != nil { + t.Fatal(err) + } } func TestUTXOAccount(t *testing.T) { @@ -996,6 +1281,16 @@ func TestUTXOAccount(t *testing.T) { if err != nil { t.Fatal(err) } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.Account() + if err == nil { + t.Fatal("Should have raised error (5)") + } } func TestUTXOGenericOwner(t *testing.T) { @@ -1152,6 +1447,16 @@ func TestUTXOGenericOwner(t *testing.T) { if err != nil { t.Fatal(err) } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + _, err = utxo.GenericOwner() + if err == nil { + t.Fatal("Should have raised error (12)") + } } func TestUTXOIsDeposit(t *testing.T) { @@ -1190,4 +1495,101 @@ func TestUTXOIsDeposit(t *testing.T) { if val { t.Fatal("Should be false (4)") } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + val = utxo.IsDeposit() + if val { + t.Fatal("Should be false (5)") + } +} + +func TestUTXOCannotBeMinedBeforeHeight(t *testing.T) { + utxo := &TXOut{} + _, err := utxo.CannotBeMinedBeforeHeight() + if err == nil { + t.Fatal("Should have raised error (1)") + } + + as := &AtomicSwap{} + err = utxo.NewAtomicSwap(as) + if err != nil { + t.Fatal(err) + } + _, err = utxo.CannotBeMinedBeforeHeight() + if err == nil { + t.Fatal("Should have raised error (2)") + } + as.ASPreImage = &ASPreImage{} + as.ASPreImage.IssuedAt = 0 + _, err = utxo.CannotBeMinedBeforeHeight() + if err == nil { + t.Fatal("Should have raised error (3)") + } + as.ASPreImage.IssuedAt = 2 + heightASTrue := constants.EpochLength + 1 + height, err := utxo.CannotBeMinedBeforeHeight() + if err != nil { + t.Fatal(err) + } + if height != heightASTrue { + t.Fatal("Incorrect height for AtomicSwap in CannotBeMinedBeforeHeight") + } + + ds := &DataStore{} + err = utxo.NewDataStore(ds) + if err != nil { + t.Fatal(err) + } + _, err = utxo.CannotBeMinedBeforeHeight() + if err == nil { + t.Fatal("Should have raised error (4)") + } + ds.DSLinker = &DSLinker{} + ds.DSLinker.DSPreImage = &DSPreImage{} + ds.DSLinker.DSPreImage.IssuedAt = uint32(0) + _, err = utxo.CannotBeMinedBeforeHeight() + if err == nil { + t.Fatal("Should have raised error (5)") + } + ds.DSLinker.DSPreImage.IssuedAt = uint32(3) + heightDSTrue := 2*constants.EpochLength + 1 + height, err = utxo.CannotBeMinedBeforeHeight() + if err != nil { + t.Fatal(err) + } + if height != heightDSTrue { + t.Fatal("Incorrect height for DataStore in CannotBeMinedBeforeHeight") + } + + vs := &ValueStore{} + err = utxo.NewValueStore(vs) + if err != nil { + t.Fatal(err) + } + heightVSTrue := uint32(1) + height, err = utxo.CannotBeMinedBeforeHeight() + if err != nil { + t.Fatal(err) + } + if height != heightVSTrue { + t.Fatal("Incorrect height for ValueStore in CannotBeMinedBeforeHeight") + } + + tf := &TxFee{} + err = utxo.NewTxFee(tf) + if err != nil { + t.Fatal(err) + } + heightTFTrue := uint32(1) + height, err = utxo.CannotBeMinedBeforeHeight() + if err != nil { + t.Fatal(err) + } + if height != heightTFTrue { + t.Fatal("Incorrect height for TxFee in CannotBeMinedBeforeHeight") + } } diff --git a/application/objs/vin.go b/application/objs/vin.go index e9e82a53..a0160816 100644 --- a/application/objs/vin.go +++ b/application/objs/vin.go @@ -39,3 +39,25 @@ func (vin Vin) IsDeposit() []bool { } return ids } + +// IsCleanupVin ensures we have a valid Vin object in Cleanup Tx. +// In this case, the refUTXOs must all be expired DataStores. +func (vin Vin) IsCleanupVin(currentHeight uint32, refUTXOs Vout) bool { + if len(vin) == 0 { + return false + } + // Must ensure that all Vin objects are expired datastores. + for i := 0; i < len(refUTXOs); i++ { + utxo := refUTXOs[i] + if !utxo.HasDataStore() { + // Must have DataStore + return false + } + expired, err := utxo.IsExpired(currentHeight) + if err != nil || !expired { + // DataStore must be expired + return false + } + } + return true +} diff --git a/application/objs/vout.go b/application/objs/vout.go index cb2c6fd5..2bc3edfd 100644 --- a/application/objs/vout.go +++ b/application/objs/vout.go @@ -2,21 +2,23 @@ package objs import ( "github.com/MadBase/MadNet/application/objs/uint256" + "github.com/MadBase/MadNet/application/wrapper" "github.com/MadBase/MadNet/errorz" ) // Vout is a vector of TXOut objects type Vout []*TXOut -// Value sums the total value of the UTXOs without any discount -func (vout Vout) Value() (*uint256.Uint256, error) { +// ValuePlusFee sums the total value of the UTXOs without any discount +// and including associated fees +func (vout Vout) ValuePlusFee() (*uint256.Uint256, error) { sum := uint256.Zero() for i := 0; i < len(vout); i++ { - value, err := vout[i].Value() + value, err := vout[i].ValuePlusFee() if err != nil { return nil, err } - sum, err = sum.Clone().Add(sum.Clone(), value.Clone()) + sum, err = sum.Add(sum, value) if err != nil { return nil, err } @@ -32,7 +34,7 @@ func (vout Vout) RemainingValue(currentHeight uint32) (*uint256.Uint256, error) if err != nil { return nil, err } - sum, err = sum.Clone().Add(sum.Clone(), value.Clone()) + sum, err = sum.Add(sum, value) if err != nil { return nil, err } @@ -79,6 +81,13 @@ func (vout Vout) ValidateTxOutIdx() error { return err } txOutIdx = asTxOutIdx + case utxo.HasTxFee(): + tf, _ := utxo.TxFee() + tfTxOutIdx, err := tf.TXOutIdx() + if err != nil { + return err + } + txOutIdx = tfTxOutIdx default: return errorz.ErrInvalid{}.New("bad txOutIdx: Invalid Type") } @@ -121,6 +130,52 @@ func (vout Vout) PreHash() ([][]byte, error) { return phs, nil } +// ValidateFees validates the Fee from each TXOut in Vout +func (vout Vout) ValidateFees(storage *wrapper.Storage) error { + for i := 0; i < len(vout); i++ { + err := vout[i].ValidateFee(storage) + if err != nil { + return err + } + } + return nil +} + +// ValidateTxFee validates the transaction fee in Vout +// +// There can be at most one TxFee UTXO object in Vout. +// There can be zero TxFee UTXO objects if MinTxFee is zero. +func (vout Vout) ValidateTxFee(storage *wrapper.Storage) error { + maxNumTxFees := 1 + minTxFee, err := storage.GetMinTxFee() + if err != nil { + return err + } + numTxFees := 0 + totalTxFee := new(uint256.Uint256) + for i := 0; i < len(vout); i++ { + if vout[i].HasTxFee() { + numTxFees++ + if numTxFees > maxNumTxFees { + return errorz.ErrInvalid{}.New("invalid Vout: more than 1 TxFee object") + } + txFee, err := vout[i].TxFee() + if err != nil { + return err + } + fee, err := txFee.Fee() + if err != nil { + return err + } + totalTxFee.Add(totalTxFee, fee) + } + } + if totalTxFee.Gte(minTxFee) { + return nil + } + return errorz.ErrInvalid{}.New("invalid Vout: totalTxFee < minTxFee") +} + // ValidatePreSignature validates the PreSignature from each TXOut in Vout func (vout Vout) ValidatePreSignature() error { for i := 0; i < len(vout); i++ { @@ -158,3 +213,28 @@ func (vout Vout) MakeTxIn() (Vin, error) { } return txIns, nil } + +// IsCleanupVout ensures we have a valid Vout object in Cleanup Tx. +// In this case, Vout must be only one ValueStore. +func (vout Vout) IsCleanupVout() bool { + if len(vout) != 1 { + return false + } + // Confirm utxo is ValueStore with no fee + utxo := vout[0] + if !utxo.HasValueStore() { + return false + } + vs, err := utxo.ValueStore() + if err != nil { + return false + } + vsFee, err := vs.Fee() + if err != nil { + return false + } + if !vsFee.IsZero() { + return false + } + return true +} diff --git a/application/objs/vs.go b/application/objs/vs.go index 758a446a..11feded5 100644 --- a/application/objs/vs.go +++ b/application/objs/vs.go @@ -4,6 +4,7 @@ import ( mdefs "github.com/MadBase/MadNet/application/objs/capn" "github.com/MadBase/MadNet/application/objs/uint256" "github.com/MadBase/MadNet/application/objs/valuestore" + "github.com/MadBase/MadNet/application/wrapper" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/utils" @@ -19,7 +20,16 @@ type ValueStore struct { } // New creates a new ValueStore -func (b *ValueStore) New(chainID uint32, value *uint256.Uint256, acct []byte, curveSpec constants.CurveSpec, txHash []byte) error { +func (b *ValueStore) New(chainID uint32, value *uint256.Uint256, fee *uint256.Uint256, acct []byte, curveSpec constants.CurveSpec, txHash []byte) error { + if b == nil { + return errorz.ErrInvalid{}.New("not initialized") + } + if value == nil || value.IsZero() { + return errorz.ErrInvalid{}.New("invalue value: nil or zero") + } + if fee == nil { + return errorz.ErrInvalid{}.New("invalue fee: nil") + } vsowner := &ValueStoreOwner{} vsowner.New(acct, curveSpec) if err := vsowner.Validate(); err != nil { @@ -33,9 +43,10 @@ func (b *ValueStore) New(chainID uint32, value *uint256.Uint256, acct []byte, cu } vsp := &VSPreImage{ ChainID: chainID, - Value: value, + Value: value.Clone(), TXOutIdx: constants.MaxUint32, Owner: vsowner, + Fee: fee.Clone(), } b.VSPreImage = vsp b.TxHash = utils.CopySlice(txHash) @@ -201,12 +212,37 @@ func (b *ValueStore) ChainID() (uint32, error) { // Value returns the Value of the object func (b *ValueStore) Value() (*uint256.Uint256, error) { - if b == nil || b.VSPreImage == nil || b.VSPreImage.Value == nil { + if b == nil || b.VSPreImage == nil || b.VSPreImage.Value == nil || b.VSPreImage.Value.IsZero() { return nil, errorz.ErrInvalid{}.New("not initialized") } return b.VSPreImage.Value.Clone(), nil } +// Fee returns the Fee of the object +func (b *ValueStore) Fee() (*uint256.Uint256, error) { + if b == nil || b.VSPreImage == nil || b.VSPreImage.Fee == nil { + return nil, errorz.ErrInvalid{}.New("not initialized") + } + return b.VSPreImage.Fee.Clone(), nil +} + +// ValuePlusFee returns the Value of the object with the associated fee +func (b *ValueStore) ValuePlusFee() (*uint256.Uint256, error) { + value, err := b.Value() + if err != nil { + return nil, err + } + fee, err := b.Fee() + if err != nil { + return nil, err + } + total, err := new(uint256.Uint256).Add(value, fee) + if err != nil { + return nil, err + } + return total, nil +} + // IsDeposit returns true if the object is a deposit func (b *ValueStore) IsDeposit() bool { if b == nil || b.VSPreImage == nil { @@ -261,6 +297,28 @@ func (b *ValueStore) Sign(txIn *TXIn, s Signer) error { return nil } +// ValidateFee validates the fee of the object at the time of creation +func (b *ValueStore) ValidateFee(storage *wrapper.Storage) error { + fee, err := b.Fee() + if err != nil { + return err + } + if b.IsDeposit() { + if !fee.IsZero() { + return errorz.ErrInvalid{}.New("vs: invalid fee; deposits should have fee equal zero") + } + return nil + } + feeTrue, err := storage.GetValueStoreFee() + if err != nil { + return err + } + if fee.Cmp(feeTrue) != 0 { + return errorz.ErrInvalid{}.New("vs: invalid fee") + } + return nil +} + // ValidateSignature validates the signature of the ValueStore at the time of // consumption func (b *ValueStore) ValidateSignature(txIn *TXIn) error { diff --git a/application/objs/vs_test.go b/application/objs/vs_test.go index ce1d2554..876215f1 100644 --- a/application/objs/vs_test.go +++ b/application/objs/vs_test.go @@ -2,6 +2,7 @@ package objs import ( "bytes" + "math/big" "testing" "github.com/MadBase/MadNet/application/objs/uint256" @@ -34,6 +35,7 @@ func TestValueStoreGood(t *testing.T) { Value: val, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) vs := &ValueStore{ @@ -86,6 +88,7 @@ func TestValueStoreBad1(t *testing.T) { Value: val, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) vs := &ValueStore{ @@ -128,6 +131,7 @@ func TestValueStoreBad2(t *testing.T) { Value: val, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen+1) // Invalid TxHash vs := &ValueStore{ @@ -152,32 +156,56 @@ func TestValueStoreNew(t *testing.T) { if err != nil { t.Fatal(err) } + fee, err := new(uint256.Uint256).FromUint64(0) + if err != nil { + t.Fatal(err) + } acct := make([]byte, 0) curveSpec := constants.CurveSecp256k1 txHash := make([]byte, 0) - err = utxo.valueStore.New(chainID, value, acct, curveSpec, txHash) + err = utxo.valueStore.New(chainID, value, fee, acct, curveSpec, txHash) if err == nil { t.Fatal("Should raise an error (1)") } vs := &ValueStore{} - acct = make([]byte, constants.OwnerLen) - err = vs.New(chainID, value, acct, curveSpec, txHash) + err = vs.New(chainID, value, fee, acct, curveSpec, txHash) if err == nil { t.Fatal("Should raise an error (2)") } - chainID = 1 - err = vs.New(chainID, value, acct, curveSpec, txHash) + acct = make([]byte, constants.OwnerLen) + err = vs.New(chainID, value, fee, acct, curveSpec, txHash) if err == nil { t.Fatal("Should raise an error (3)") } + chainID = 1 + err = vs.New(chainID, value, fee, acct, curveSpec, txHash) + if err == nil { + t.Fatal("Should raise an error (4)") + } + txHash = make([]byte, constants.HashLen) - err = vs.New(chainID, value, acct, curveSpec, txHash) + err = vs.New(chainID, value, fee, acct, curveSpec, txHash) if err != nil { t.Fatal(err) } + + err = vs.New(chainID, nil, fee, acct, curveSpec, txHash) + if err == nil { + t.Fatal("Should raise an error (5)") + } + + err = vs.New(chainID, uint256.Zero(), fee, acct, curveSpec, txHash) + if err == nil { + t.Fatal("Should raise an error (6)") + } + + err = vs.New(chainID, value, nil, acct, curveSpec, txHash) + if err == nil { + t.Fatal("Should raise an error (7)") + } } func TestValueStoreNewFromDeposit(t *testing.T) { @@ -258,6 +286,7 @@ func TestValueStoreMarshalBinary(t *testing.T) { Value: val, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) vs = &ValueStore{ @@ -313,6 +342,7 @@ func TestValueStoreUnmarshalBinary(t *testing.T) { Value: val, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) vs = &ValueStore{ @@ -514,6 +544,83 @@ func TestValueStoreValue(t *testing.T) { } } +func TestValueStoreValuePlusFeeGood(t *testing.T) { + // Prepare ValueStore + vs := &ValueStore{} + vs.VSPreImage = &VSPreImage{} + value32 := uint32(1234567890) + value, err := new(uint256.Uint256).FromUint64(uint64(value32)) + if err != nil { + t.Fatal(err) + } + vs.VSPreImage.Value = value.Clone() + vs.VSPreImage.Fee = new(uint256.Uint256) + + // Check valuePlusFee + valuePlusFee, err := vs.ValuePlusFee() + if err != nil { + t.Fatal(err) + } + if !value.Eq(valuePlusFee) { + t.Fatal("value and valuePlusFee do not match") + } + + // Prepare storage with nonzero fee + valueStoreFee := uint32(1000) + vsFee, err := new(uint256.Uint256).FromUint64(uint64(valueStoreFee)) + if err != nil { + t.Fatal(err) + } + vs.VSPreImage.Fee = vsFee.Clone() + trueVPF, err := new(uint256.Uint256).Add(vsFee, value) + if err != nil { + t.Fatal(err) + } + + // Check valuePlusFee + valuePlusFee, err = vs.ValuePlusFee() + if err != nil { + t.Fatal(err) + } + if !trueVPF.Eq(valuePlusFee) { + t.Fatal("valuePlusFee is not correct") + } + if value.Eq(valuePlusFee) { + t.Fatal("value and valuePlusFee should not be equal") + } +} + +func TestValueStoreValuePlusFeeBad1(t *testing.T) { + vs := &ValueStore{} + _, err := vs.ValuePlusFee() + if err == nil { + t.Fatal("Should raise an error") + } +} + +func TestValueStoreValuePlusFeeBad2(t *testing.T) { + vs := &ValueStore{} + vs.VSPreImage = &VSPreImage{} + + // Cause overflow when computing valuePlusFee + bigString := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + bigBig, ok := new(big.Int).SetString(bigString, 16) + if !ok { + t.Fatal("Invalid conversion") + } + bigUint256, err := new(uint256.Uint256).FromBigInt(bigBig) + if err != nil { + t.Fatal(err) + } + + // Prep for failure + vs.VSPreImage.Value = bigUint256.Clone() + _, err = vs.ValuePlusFee() + if err == nil { + t.Fatal("Should have raised error") + } +} + func TestValueStoreIsDeposit(t *testing.T) { vs := &ValueStore{} val := vs.IsDeposit() @@ -778,6 +885,51 @@ func TestValueStoreMakeTxIn(t *testing.T) { } } +func TestValueStoreValidateFee(t *testing.T) { + msg := makeMockStorageGetter() + storage := makeStorage(msg) + + utxo := &TXOut{} + err := utxo.valueStore.ValidateFee(storage) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + vs := &ValueStore{} + err = vs.ValidateFee(storage) + if err == nil { + t.Fatal("Should have raised error (2)") + } + + vs.VSPreImage = &VSPreImage{} + vs.VSPreImage.Fee = new(uint256.Uint256).SetZero() + err = vs.ValidateFee(storage) + if err != nil { + t.Fatal(err) + } + + vsFee := big.NewInt(1) + msg.SetValueStoreFee(vsFee) + storage = makeStorage(msg) + err = vs.ValidateFee(storage) + if err == nil { + t.Fatal("Should have raised error (3)") + } + + // Now tests for deposit + vs.VSPreImage.TXOutIdx = constants.MaxUint32 + err = vs.ValidateFee(storage) + if err != nil { + t.Fatal(err) + } + + vs.VSPreImage.Fee.SetOne() + err = vs.ValidateFee(storage) + if err == nil { + t.Fatal("Should have raised error (4)") + } +} + func TestValueStoreOwnerSig(t *testing.T) { cid := uint32(2) val, err := new(uint256.Uint256).FromUint64(65537) @@ -803,6 +955,7 @@ func TestValueStoreOwnerSig(t *testing.T) { Value: val, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) vs := &ValueStore{ @@ -863,7 +1016,10 @@ func TestValueStoreOwnerSig2(t *testing.T) { txoid := uint32(17) ownerSigner := &crypto.BNSigner{} - ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))) + err = ownerSigner.SetPrivk(crypto.Hasher([]byte("a"))) + if err != nil { + t.Fatal(err) + } ownerPubk, err := ownerSigner.Pubkey() if err != nil { t.Fatal(err) @@ -877,6 +1033,7 @@ func TestValueStoreOwnerSig2(t *testing.T) { Value: val, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } txHash := make([]byte, constants.HashLen) vs := &ValueStore{ diff --git a/application/objs/vsowner_test.go b/application/objs/vsowner_test.go index 0114183b..e9833e27 100644 --- a/application/objs/vsowner_test.go +++ b/application/objs/vsowner_test.go @@ -218,7 +218,10 @@ func TestVSOwnerValidateSignatureSecp(t *testing.T) { t.Fatal(err) } bnSigner := &crypto.BNSigner{} - bnSigner.SetPrivk(privk) + err = bnSigner.SetPrivk(privk) + if err != nil { + t.Fatal(err) + } vssBN, err := vso.Sign(msg, bnSigner) if err != nil { t.Fatal(err) @@ -280,7 +283,10 @@ func TestVSOwnerValidateSignatureBN(t *testing.T) { privk := make([]byte, 32) privk[0] = 1 privk[31] = 1 - bnSigner.SetPrivk(privk) + err = bnSigner.SetPrivk(privk) + if err != nil { + t.Fatal(err) + } secpSigner := &crypto.Secp256k1Signer{} if err := secpSigner.SetPrivk(privk); err != nil { t.Fatal(err) diff --git a/application/objs/vspi.go b/application/objs/vspi.go index 90780ca0..b7a58207 100644 --- a/application/objs/vspi.go +++ b/application/objs/vspi.go @@ -16,6 +16,7 @@ type VSPreImage struct { Value *uint256.Uint256 TXOutIdx uint32 Owner *ValueStoreOwner + Fee *uint256.Uint256 // preHash []byte } @@ -74,6 +75,20 @@ func (b *VSPreImage) UnmarshalCapn(bc mdefs.VSPreImage) error { return err } b.Owner = owner + fObj := &uint256.Uint256{} + u32array[0] = bc.Fee0() + u32array[1] = bc.Fee1() + u32array[2] = bc.Fee2() + u32array[3] = bc.Fee3() + u32array[4] = bc.Fee4() + u32array[5] = bc.Fee5() + u32array[6] = bc.Fee6() + u32array[7] = bc.Fee7() + err = fObj.FromUint32Array(u32array) + if err != nil { + return err + } + b.Fee = fObj return nil } @@ -120,6 +135,18 @@ func (b *VSPreImage) MarshalCapn(seg *capnp.Segment) (mdefs.VSPreImage, error) { bc.SetValue5(u32array[5]) bc.SetValue6(u32array[6]) bc.SetValue7(u32array[7]) + u32array, err = b.Fee.ToUint32Array() + if err != nil { + return bc, err + } + bc.SetFee0(u32array[0]) + bc.SetFee1(u32array[1]) + bc.SetFee2(u32array[2]) + bc.SetFee3(u32array[3]) + bc.SetFee4(u32array[4]) + bc.SetFee5(u32array[5]) + bc.SetFee6(u32array[6]) + bc.SetFee7(u32array[7]) bc.SetTXOutIdx(b.TXOutIdx) return bc, nil } diff --git a/application/objs/vspi_test.go b/application/objs/vspi_test.go index e09c5f06..ad3d1eac 100644 --- a/application/objs/vspi_test.go +++ b/application/objs/vspi_test.go @@ -34,6 +34,7 @@ func TestVSPreImageGood(t *testing.T) { Value: val, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } vsp2 := &VSPreImage{} vspBytes, err := vsp.MarshalBinary() @@ -87,6 +88,7 @@ func TestVSPreImageBad1(t *testing.T) { Value: val, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } vsp2 := &VSPreImage{} vspBytes, err := vsp.MarshalBinary() @@ -177,6 +179,7 @@ func TestVSPreImagePreHash(t *testing.T) { Value: val, TXOutIdx: txoid, Owner: owner, + Fee: new(uint256.Uint256).SetZero(), } out, err := vsp.PreHash() if err != nil { diff --git a/application/pendingtx/pending_test.go b/application/pendingtx/pending_test.go index 7fd774a2..ddfcbcae 100644 --- a/application/pendingtx/pending_test.go +++ b/application/pendingtx/pending_test.go @@ -64,10 +64,12 @@ func makeVS(ownerSigner objs.Signer) *objs.TXOut { owner := &objs.ValueStoreOwner{} owner.New(ownerAcct, constants.CurveSecp256k1) + fee := uint256.One() vsp := &objs.VSPreImage{ ChainID: cid, Value: val, Owner: owner, + Fee: fee, } vs := &objs.ValueStore{ VSPreImage: vsp, @@ -357,7 +359,8 @@ func TestGetProposal(t *testing.T) { mustAddTx(t, hndlr, tx3, 1) tx4 := makeTxConsuming(c2) mustAddTx(t, hndlr, tx4, 1) - txs, err := hndlr.GetTxsForProposal(nil, context.TODO(), 1, constants.MaxUint32, nil) + maxBytes := constants.MaxUint32 + txs, _, err := hndlr.GetTxsForProposal(nil, context.TODO(), 1, maxBytes, nil) if err != nil { t.Fatal(err) } diff --git a/application/pendingtx/pendingtxhandler.go b/application/pendingtx/pendingtxhandler.go index 4e96a616..945d5d80 100644 --- a/application/pendingtx/pendingtxhandler.go +++ b/application/pendingtx/pendingtxhandler.go @@ -193,19 +193,21 @@ func (pt *Handler) DeleteMined(txnState *badger.Txn, currentHeight uint32, txHas // GetTxsForProposal returns an set of txs that are mutually exclusive with // respect to the consumed UTXOs. This is used to genrete new proposals. -func (pt *Handler) GetTxsForProposal(txnState *badger.Txn, ctx context.Context, currentHeight uint32, maxBytes uint32, tx *objs.Tx) (objs.TxVec, error) { - utxos, err := pt.getTxsInternal(txnState, ctx, currentHeight, maxBytes, tx, false) +func (pt *Handler) GetTxsForProposal(txnState *badger.Txn, ctx context.Context, currentHeight uint32, maxBytes uint32, tx *objs.Tx) (objs.TxVec, uint32, error) { + var utxos objs.TxVec + var err error + utxos, maxBytes, err = pt.getTxsInternal(txnState, ctx, currentHeight, maxBytes, tx, false) if err != nil { utils.DebugTrace(pt.logger, err) - return nil, err + return nil, 0, err } - return utxos, nil + return utxos, maxBytes, nil } // GetTxsForGossip returns the oldest non-expired and non-consumed txs from the // tx pool. These txs may be conflicting in terms of consumed UTXOS. func (pt *Handler) GetTxsForGossip(txnState *badger.Txn, ctx context.Context, currentHeight uint32, maxBytes uint32) ([]*objs.Tx, error) { - utxos, err := pt.getTxsInternal(txnState, ctx, currentHeight, maxBytes, nil, true) + utxos, _, err := pt.getTxsInternal(txnState, ctx, currentHeight, maxBytes, nil, true) if err != nil { utils.DebugTrace(pt.logger, err) return nil, err @@ -218,16 +220,16 @@ func (pt *Handler) GetTxsForGossip(txnState *badger.Txn, ctx context.Context, cu /////////PRIVATE METHODS//////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -func (pt *Handler) getTxsInternal(txnState *badger.Txn, ctx context.Context, currentHeight uint32, maxBytes uint32, tx *objs.Tx, allowConflict bool) ([]*objs.Tx, error) { +func (pt *Handler) getTxsInternal(txnState *badger.Txn, ctx context.Context, currentHeight uint32, maxBytes uint32, tx *objs.Tx, allowConflict bool) ([]*objs.Tx, uint32, error) { txs := objs.TxVec{} if tx != nil { txs = append(txs, tx) } + byteCount := uint32(0) + if len(txs) > 0 { + byteCount += constants.HashLen + } err := pt.db.View(func(txn *badger.Txn) error { - byteCount := uint32(0) - if len(txs) > 0 { - byteCount += constants.HashLen - } it, prefix := pt.indexer.GetOrderedIter(txn) err := func() error { defer it.Close() @@ -309,16 +311,18 @@ func (pt *Handler) getTxsInternal(txnState *badger.Txn, ctx context.Context, cur } return nil }) + if err != nil { + utils.DebugTrace(pt.logger, err) + return nil, 0, err + } out := []*objs.Tx{} for i := 0; i < len(txs); i++ { if txs[i] != nil { out = append(out, txs[i]) } } - if err != nil { - utils.DebugTrace(pt.logger, err) - } - return out, err + remainingBytes := maxBytes - byteCount + return out, remainingBytes, err } func (pt *Handler) checkSize(maxBytes uint32, byteCount uint32) bool { diff --git a/application/txhandler.go b/application/txhandler.go index 0786c5e1..74c654bf 100644 --- a/application/txhandler.go +++ b/application/txhandler.go @@ -13,6 +13,7 @@ import ( "github.com/MadBase/MadNet/application/objs/uint256" "github.com/MadBase/MadNet/application/pendingtx" "github.com/MadBase/MadNet/application/utxohandler" + "github.com/MadBase/MadNet/application/wrapper" trie "github.com/MadBase/MadNet/badgerTrie" consensusdb "github.com/MadBase/MadNet/consensus/db" "github.com/MadBase/MadNet/constants" @@ -31,13 +32,19 @@ type txHandler struct { mTxHdlr *minedtx.MinedTxHandler dHdlr *deposit.Handler uHdlr *utxohandler.UTXOHandler + storage *wrapper.Storage } func (tm *txHandler) GetTxsForGossip(txnState *badger.Txn, currentHeight uint32) ([]*objs.Tx, error) { ctx := context.Background() subCtx, cf := context.WithTimeout(ctx, 100*time.Millisecond) defer cf() - return tm.pTxHdlr.GetTxsForGossip(txnState, subCtx, currentHeight, constants.MaxBytes) + utxos, err := tm.pTxHdlr.GetTxsForGossip(txnState, subCtx, currentHeight, tm.storage.GetMaxBytes()) + if err != nil { + utils.DebugTrace(tm.logger, err) + return nil, err + } + return utxos, nil } func (tm *txHandler) IsValid(txn *badger.Txn, tx []*objs.Tx, currentHeight uint32) (objs.Vout, error) { @@ -77,7 +84,7 @@ func (tm *txHandler) ApplyState(txn *badger.Txn, chainID uint32, height uint32, utils.DebugTrace(tm.logger, err) return nil, err } - if err := txs.Validate(height, vout); err != nil { + if err := txs.Validate(height, vout, tm.storage); err != nil { utils.DebugTrace(tm.logger, err) return nil, err } @@ -106,7 +113,7 @@ func (tm *txHandler) GetTxsForProposal(txn *badger.Txn, chainID uint32, height u ctx := context.Background() subCtx, cf := context.WithTimeout(ctx, 1*time.Second) defer cf() - tx, err := tm.uHdlr.GetExpiredForProposal(txn, subCtx, chainID, height, curveSpec, signer, maxBytes) + tx, maxBytes, err := tm.uHdlr.GetExpiredForProposal(txn, subCtx, chainID, height, curveSpec, signer, maxBytes, tm.storage) if err != nil { utils.DebugTrace(tm.logger, err) return nil, nil, errorz.ErrInvalid{}.New(err.Error()) @@ -133,7 +140,7 @@ func (tm *txHandler) GetTxsForProposal(txn *badger.Txn, chainID uint32, height u return nil, nil, err } } - txs, err := tm.pTxHdlr.GetTxsForProposal(txn, subCtx, height, maxBytes, tx) + txs, _, err := tm.pTxHdlr.GetTxsForProposal(txn, subCtx, height, maxBytes, tx) if err != nil { utils.DebugTrace(tm.logger, err) return nil, nil, errorz.ErrInvalid{}.New(err.Error()) diff --git a/application/utxohandler/utxohandler.go b/application/utxohandler/utxohandler.go index 93422fae..e61f5011 100644 --- a/application/utxohandler/utxohandler.go +++ b/application/utxohandler/utxohandler.go @@ -13,6 +13,7 @@ import ( "github.com/MadBase/MadNet/application/objs" "github.com/MadBase/MadNet/application/objs/uint256" "github.com/MadBase/MadNet/application/utxohandler/utxotrie" + "github.com/MadBase/MadNet/application/wrapper" trie "github.com/MadBase/MadNet/badgerTrie" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" @@ -128,7 +129,7 @@ func (ut *UTXOHandler) IsValid(txn *badger.Txn, txs objs.TxVec, currentHeight ui for j := 0; j < len(utxos); j++ { refUTXOs = append(refUTXOs, utxos[j]) } - if err := tx.ValidateEqualVinVout(refUTXOs, currentHeight); err != nil { + if err := tx.ValidateEqualVinVout(currentHeight, refUTXOs); err != nil { utils.DebugTrace(ut.logger, err) return nil, err } @@ -416,22 +417,22 @@ func (ut *UTXOHandler) GetData(txn *badger.Txn, owner *objs.Owner, dataIdx []byt // GetExpiredForProposal returns a list of UTXOs, the IDs of those UTXOs, and // the total byte count of the returned UTXOs. This is used to collect expired // dataStores for deletion. -func (ut *UTXOHandler) GetExpiredForProposal(txn *badger.Txn, ctx context.Context, chainID, height uint32, curveSpec constants.CurveSpec, signer objs.Signer, maxBytes uint32) (*objs.Tx, error) { - utxoIDs, _ := ut.expIndex.GetExpiredObjects(txn, utils.Epoch(height), maxBytes) +func (ut *UTXOHandler) GetExpiredForProposal(txn *badger.Txn, ctx context.Context, chainID, height uint32, curveSpec constants.CurveSpec, signer objs.Signer, maxBytes uint32, storage *wrapper.Storage) (*objs.Tx, uint32, error) { + utxoIDs, remaingBytes := ut.expIndex.GetExpiredObjects(txn, utils.Epoch(height), maxBytes, constants.MaxTxVectorLength) utxos := []*objs.TXOut{} var utxoID []byte for i := 0; i < len(utxoIDs); i++ { utxoID = utils.CopySlice(utxoIDs[i]) select { case <-ctx.Done(): - return nil, nil + return nil, maxBytes, nil default: // this prevents a node from losing a turn to propose due to taking too long } missing, err := ut.trie.Contains(txn, [][]byte{utils.CopySlice(utxoID)}) if err != nil { utils.DebugTrace(ut.logger, err) - return nil, err + return nil, 0, err } if len(missing) > 0 { continue @@ -439,7 +440,7 @@ func (ut *UTXOHandler) GetExpiredForProposal(txn *badger.Txn, ctx context.Contex utxo, err := ut.getInternal(txn, utils.CopySlice(utxoID)) if err != nil { utils.DebugTrace(ut.logger, err) - return nil, err + return nil, 0, err } utxos = append(utxos, utxo) if len(utxos) == constants.MaxTxVectorLength { @@ -451,30 +452,30 @@ func (ut *UTXOHandler) GetExpiredForProposal(txn *badger.Txn, ctx context.Contex txIns, err := utxos.MakeTxIn() if err != nil { utils.DebugTrace(ut.logger, err) - return nil, err + return nil, 0, err } value, err := utxos.RemainingValue(height) if err != nil { utils.DebugTrace(ut.logger, err) - return nil, err + return nil, 0, err } pubk, err := signer.Pubkey() if err != nil { utils.DebugTrace(ut.logger, err) - return nil, err + return nil, 0, err } account := crypto.GetAccount(pubk) - vsf := &objs.ValueStore{} - err = vsf.New(chainID, value, account, curveSpec, make([]byte, constants.HashLen)) + vs := &objs.ValueStore{} + err = vs.New(chainID, value, uint256.Zero(), account, curveSpec, make([]byte, constants.HashLen)) if err != nil { utils.DebugTrace(ut.logger, err) - return nil, err + return nil, 0, err } utxo := &objs.TXOut{} - err = utxo.NewValueStore(vsf) + err = utxo.NewValueStore(vs) if err != nil { utils.DebugTrace(ut.logger, err) - return nil, err + return nil, 0, err } tx := &objs.Tx{ Vin: txIns, @@ -483,23 +484,23 @@ func (ut *UTXOHandler) GetExpiredForProposal(txn *badger.Txn, ctx context.Contex err = tx.SetTxHash() if err != nil { utils.DebugTrace(ut.logger, err) - return nil, err + return nil, 0, err } for i := 0; i < len(utxos); i++ { ds, err := utxos[i].DataStore() if err != nil { utils.DebugTrace(ut.logger, err) - return nil, err + return nil, 0, err } err = ds.Sign(txIns[i], signer) if err != nil { utils.DebugTrace(ut.logger, err) - return nil, err + return nil, 0, err } } - return tx, nil + return tx, remaingBytes, nil } - return nil, nil + return nil, maxBytes, nil } // GetValueForOwner allows a list of utxoIDs to be returned that are equal or diff --git a/application/utxohandler/utxohandler_test.go b/application/utxohandler/utxohandler_test.go index 1a476f31..65dd3590 100644 --- a/application/utxohandler/utxohandler_test.go +++ b/application/utxohandler/utxohandler_test.go @@ -56,8 +56,9 @@ func makeTxs(t *testing.T, s objs.Signer, v *objs.ValueStore) *objs.Tx { Value: value, Owner: &objs.ValueStoreOwner{SVA: objs.ValueStoreSVA, CurveSpec: constants.CurveSecp256k1, Account: crypto.GetAccount(pubkey)}, TXOutIdx: 0, + Fee: new(uint256.Uint256).SetZero(), }, - TxHash: make([]byte, 32), + TxHash: make([]byte, constants.HashLen), } newUTXO := &objs.TXOut{} err = newUTXO.NewValueStore(newValueStore) diff --git a/application/utxohandler/utxotrie/utxotrie.go b/application/utxohandler/utxotrie/utxotrie.go index 66cd7f47..7cf4b5f7 100644 --- a/application/utxohandler/utxotrie/utxotrie.go +++ b/application/utxohandler/utxotrie/utxotrie.go @@ -3,11 +3,10 @@ package utxotrie import ( "bytes" - "github.com/MadBase/MadNet/constants/dbprefix" - - aobjs "github.com/MadBase/MadNet/application/objs" + "github.com/MadBase/MadNet/application/objs" trie "github.com/MadBase/MadNet/badgerTrie" "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/constants/dbprefix" "github.com/MadBase/MadNet/logging" "github.com/MadBase/MadNet/utils" "github.com/dgraph-io/badger/v2" @@ -164,7 +163,7 @@ func (ut *UTXOTrie) Contains(txn *badger.Txn, utxoIDs [][]byte) ([][]byte, error return missing, nil } -func (ut *UTXOTrie) ApplyState(txn *badger.Txn, txs aobjs.TxVec, height uint32) ([]byte, error) { +func (ut *UTXOTrie) ApplyState(txn *badger.Txn, txs objs.TxVec, height uint32) ([]byte, error) { current, fn, err := ut.session(txn) if err != nil { utils.DebugTrace(ut.logger, err) @@ -251,7 +250,7 @@ func (ut *UTXOTrie) GetCurrentStateRoot(txn *badger.Txn) ([]byte, error) { return rt, nil } -func (ut *UTXOTrie) GetStateRootForProposal(txn *badger.Txn, txs aobjs.TxVec) ([]byte, error) { +func (ut *UTXOTrie) GetStateRootForProposal(txn *badger.Txn, txs objs.TxVec) ([]byte, error) { if len(txs) == 0 { sr, err := GetCurrentStateRoot(txn) if err != nil { @@ -274,7 +273,7 @@ func (ut *UTXOTrie) GetStateRootForProposal(txn *badger.Txn, txs aobjs.TxVec) ([ return sr, nil } -func (ut *UTXOTrie) add(txn *badger.Txn, txs aobjs.TxVec, current *trie.SMT, fn func(txn *badger.Txn, current *trie.SMT, newUTXOIDs [][]byte, newUTXOHashes [][]byte, consumedUTXOIDS [][]byte) ([]byte, error)) ([]byte, error) { +func (ut *UTXOTrie) add(txn *badger.Txn, txs objs.TxVec, current *trie.SMT, fn func(txn *badger.Txn, current *trie.SMT, newUTXOIDs [][]byte, newUTXOHashes [][]byte, consumedUTXOIDS [][]byte) ([]byte, error)) ([]byte, error) { addkeys := [][]byte{} addvalues := [][]byte{} delkeys := [][]byte{} diff --git a/application/wrapper/storage.go b/application/wrapper/storage.go new file mode 100644 index 00000000..b00d6a83 --- /dev/null +++ b/application/wrapper/storage.go @@ -0,0 +1,69 @@ +package wrapper + +import ( + "github.com/MadBase/MadNet/dynamics" + + "github.com/MadBase/MadNet/application/objs/uint256" +) + +// Storage wraps the dynamics.StorageGetter interface to make +// it easier to interact within application logic +type Storage struct { + storage dynamics.StorageGetter +} + +// NewStorage creates a new storage struct which wraps +// the StorageGetter interface +func NewStorage(storageInter dynamics.StorageGetter) *Storage { + storage := &Storage{storage: storageInter} + return storage +} + +// GetMaxBytes returns MaxBytes +func (s *Storage) GetMaxBytes() uint32 { + return s.storage.GetMaxBytes() +} + +// GetAtomicSwapFee returns the fee for AtomicSwap +func (s *Storage) GetAtomicSwapFee() (*uint256.Uint256, error) { + fee := s.storage.GetAtomicSwapFee() + feeUint256 := &uint256.Uint256{} + _, err := feeUint256.FromBigInt(fee) + if err != nil { + return nil, err + } + return feeUint256, nil +} + +// GetDataStoreEpochFee returns the per-epoch fee of DataStore +func (s *Storage) GetDataStoreEpochFee() (*uint256.Uint256, error) { + fee := s.storage.GetDataStoreEpochFee() + feeUint256 := &uint256.Uint256{} + _, err := feeUint256.FromBigInt(fee) + if err != nil { + return nil, err + } + return feeUint256, nil +} + +// GetValueStoreFee returns the fee of ValueStore +func (s *Storage) GetValueStoreFee() (*uint256.Uint256, error) { + fee := s.storage.GetValueStoreFee() + feeUint256 := &uint256.Uint256{} + _, err := feeUint256.FromBigInt(fee) + if err != nil { + return nil, err + } + return feeUint256, nil +} + +// GetMinTxFee returns the minimum TxFee +func (s *Storage) GetMinTxFee() (*uint256.Uint256, error) { + fee := s.storage.GetMinTxFee() + feeUint256 := &uint256.Uint256{} + _, err := feeUint256.FromBigInt(fee) + if err != nil { + return nil, err + } + return feeUint256, nil +} diff --git a/cmd/testutils/inject/main.go b/cmd/testutils/inject/main.go index 14fc085d..c4d7ceb7 100644 --- a/cmd/testutils/inject/main.go +++ b/cmd/testutils/inject/main.go @@ -229,7 +229,10 @@ func (f *funder) setupTestingSigner(i int) (aobjs.Signer, []byte, error) { func (f *funder) setupBNSigner(privk []byte) (*crypto.BNSigner, []byte, error) { signer := &crypto.BNSigner{} - signer.SetPrivk(privk) + err := signer.SetPrivk(privk) + if err != nil { + return nil, nil, err + } pubk, err := signer.Pubkey() if err != nil { return nil, nil, err @@ -279,7 +282,7 @@ func (f *funder) setupTransaction(signer aobjs.Signer, ownerAcct []byte, consume Vin: aobjs.Vin{}, Vout: aobjs.Vout{}, } - chainID := uint32(0) + chainID := uint32(42) for _, utxo := range consumedUtxos { consumedVS, err := utxo.ValueStore() if err != nil { @@ -306,6 +309,7 @@ func (f *funder) setupTransaction(signer aobjs.Signer, ownerAcct []byte, consume Value: uint256.One(), Owner: newOwner, TXOutIdx: 0, + Fee: new(uint256.Uint256).SetZero(), }, TxHash: make([]byte, constants.HashLen), } @@ -327,6 +331,7 @@ func (f *funder) setupTransaction(signer aobjs.Signer, ownerAcct []byte, consume Value: diff, Owner: newOwner, TXOutIdx: 0, + Fee: new(uint256.Uint256).SetZero(), }, TxHash: make([]byte, constants.HashLen), } @@ -479,7 +484,7 @@ func (f *funder) setupDataStoreTransaction(ctx context.Context, signer aobjs.Sig Vin: aobjs.Vin{}, Vout: aobjs.Vout{}, } - chainID := uint32(0) + chainID := uint32(42) for _, utxo := range consumedUtxos { consumedVS, err := utxo.ValueStore() if err != nil { @@ -518,6 +523,7 @@ func (f *funder) setupDataStoreTransaction(ctx context.Context, signer aobjs.Sig RawData: []byte(msg), TXOutIdx: 0, Owner: newOwner, + Fee: new(uint256.Uint256).SetZero(), }, TxHash: make([]byte, constants.HashLen), }, @@ -529,7 +535,10 @@ func (f *funder) setupDataStoreTransaction(ctx context.Context, signer aobjs.Sig fmt.Printf("Consumed Next:%v ValueOut:%v\n", consumedValue, valueOut) fmt.Printf("DS: index:%x deposit:%v EpochOfExpire:%v msg:%s\n", index, deposit, eoe, msg) newUTXO := &aobjs.TXOut{} - newUTXO.NewDataStore(newDataStore) + err = newUTXO.NewDataStore(newDataStore) + if err != nil { + panic(err) + } tx.Vout = append(tx.Vout, newUTXO) } fmt.Printf("Consumed Next:%v ValueOut:%v\n", consumedValue, valueOut) @@ -546,19 +555,29 @@ func (f *funder) setupDataStoreTransaction(ctx context.Context, signer aobjs.Sig Value: diff, Owner: newOwner, TXOutIdx: 0, + Fee: new(uint256.Uint256).SetZero(), }, TxHash: make([]byte, constants.HashLen), } newUTXO := &aobjs.TXOut{} - newUTXO.NewValueStore(newValueStore) + err = newUTXO.NewValueStore(newValueStore) + if err != nil { + panic(err) + } tx.Vout = append(tx.Vout, newUTXO) //valueOut += diff valueOut.Add(valueOut, diff) } fmt.Printf("Consumed Next:%v ValueOut:%v\n", consumedValue, valueOut) - tx.Vout.SetTxOutIdx() + err = tx.Vout.SetTxOutIdx() + if err != nil { + panic(err) + } fmt.Printf("Consumed Next:%v ValueOut:%v\n", consumedValue, valueOut) - tx.SetTxHash() + err = tx.SetTxHash() + if err != nil { + panic(err) + } for _, newUtxo := range tx.Vout { switch { case newUtxo.HasDataStore(): @@ -581,14 +600,20 @@ func (f *funder) setupDataStoreTransaction(ctx context.Context, signer aobjs.Sig return nil, err } txIn := tx.Vin[idx] - consumedVS.Sign(txIn, signer) + err = consumedVS.Sign(txIn, signer) + if err != nil { + panic(err) + } case consumedUtxo.HasDataStore(): consumedDS, err := consumedUtxo.DataStore() if err != nil { return nil, err } txIn := tx.Vin[idx] - consumedDS.Sign(txIn, signer) + err = consumedDS.Sign(txIn, signer) + if err != nil { + panic(err) + } } } fmt.Printf("Consumed Next:%v ValueOut:%v\n", consumedValue, valueOut) @@ -665,7 +690,7 @@ func (f *funder) setupDataStoreTransaction2(ctx context.Context, signer aobjs.Si Vin: aobjs.Vin{}, Vout: aobjs.Vout{}, } - chainID := uint32(0) + chainID := uint32(42) for _, utxo := range consumedUtxos { consumedVS, err := utxo.ValueStore() if err != nil { @@ -713,6 +738,7 @@ func (f *funder) setupDataStoreTransaction2(ctx context.Context, signer aobjs.Si RawData: []byte(msg), TXOutIdx: 0, Owner: newOwner, + Fee: new(uint256.Uint256).SetZero(), }, TxHash: make([]byte, constants.HashLen), }, @@ -741,6 +767,7 @@ func (f *funder) setupDataStoreTransaction2(ctx context.Context, signer aobjs.Si Value: diff, Owner: newOwner, TXOutIdx: 0, + Fee: new(uint256.Uint256).SetZero(), }, TxHash: make([]byte, constants.HashLen), } @@ -864,7 +891,7 @@ func (f *funder) setupDataStoreTransaction3(ctx context.Context, signer aobjs.Si Vin: aobjs.Vin{}, Vout: aobjs.Vout{}, } - chainID := uint32(0) + chainID := uint32(42) for _, utxo := range consumedUtxos { consumedVS, err := utxo.ValueStore() if err != nil { @@ -912,6 +939,7 @@ func (f *funder) setupDataStoreTransaction3(ctx context.Context, signer aobjs.Si RawData: []byte(msg), TXOutIdx: 0, Owner: newOwner, + Fee: new(uint256.Uint256).SetZero(), }, TxHash: make([]byte, constants.HashLen), }, @@ -940,6 +968,7 @@ func (f *funder) setupDataStoreTransaction3(ctx context.Context, signer aobjs.Si Value: diff, Owner: newOwner, TXOutIdx: 0, + Fee: new(uint256.Uint256).SetZero(), }, TxHash: make([]byte, constants.HashLen), } diff --git a/cmd/testutils/many_utxos/main.go b/cmd/testutils/many_utxos/main.go index 6961774b..04181c77 100644 --- a/cmd/testutils/many_utxos/main.go +++ b/cmd/testutils/many_utxos/main.go @@ -117,7 +117,10 @@ func (f *funder) setupTestingSigner(i int) (aobjs.Signer, []byte, error) { func (f *funder) setupBNSigner(privk []byte) (*crypto.BNSigner, []byte, error) { signer := &crypto.BNSigner{} - signer.SetPrivk(privk) + err := signer.SetPrivk(privk) + if err != nil { + return nil, nil, err + } pubk, err := signer.Pubkey() if err != nil { return nil, nil, err diff --git a/cmd/validator/validator.go b/cmd/validator/validator.go index 8f8bdc7f..eaa4d7b4 100644 --- a/cmd/validator/validator.go +++ b/cmd/validator/validator.go @@ -27,6 +27,7 @@ import ( "github.com/MadBase/MadNet/consensus/request" "github.com/MadBase/MadNet/constants" mncrypto "github.com/MadBase/MadNet/crypto" + "github.com/MadBase/MadNet/dynamics" "github.com/MadBase/MadNet/localrpc" "github.com/MadBase/MadNet/logging" "github.com/MadBase/MadNet/peering" @@ -242,6 +243,9 @@ func validatorNode(cmd *cobra.Command, args []string) { // synchronizes execution context, makes sure everything synchronizes with the ctx system - throughout modules consSync := &consensus.Synchronizer{} + // define storage to dynamic values + storage := &dynamics.Storage{} + // account signer for ETH accounts secp256k1Signer := &mncrypto.Secp256k1Signer{} @@ -261,19 +265,24 @@ func validatorNode(cmd *cobra.Command, args []string) { consTxPool.Init(consDB) appDepositHandler.Init() - if err := app.Init(consDB, rawTxPoolDb, appDepositHandler); err != nil { + if err := app.Init(consDB, rawTxPoolDb, appDepositHandler, storage); err != nil { + panic(err) + } + + // Initialize storage + if err := storage.Init(consDB, logger); err != nil { panic(err) } // Initialize consensus - consReqClient.Init(peerManager.P2PClient()) - consReqHandler.Init(consDB, app) + consReqClient.Init(peerManager.P2PClient(), storage) + consReqHandler.Init(consDB, app, storage) consDlManager.Init(consDB, app, consReqClient) consLSHandler.Init(consDB, consDlManager) - consGossipHandlers.Init(consDB, peerManager.P2PClient(), app, consLSHandler) - consGossipClient.Init(consDB, peerManager.P2PClient(), app) - consAdminHandlers.Init(chainID, consDB, mncrypto.Hasher([]byte(config.Configuration.Validator.SymmetricKey)), app, publicKey) - consLSEngine.Init(consDB, consDlManager, app, secp256k1Signer, consAdminHandlers, publicKey, consReqClient) + consGossipHandlers.Init(consDB, peerManager.P2PClient(), app, consLSHandler, storage) + consGossipClient.Init(consDB, peerManager.P2PClient(), app, storage) + consAdminHandlers.Init(chainID, consDB, mncrypto.Hasher([]byte(config.Configuration.Validator.SymmetricKey)), app, publicKey, storage) + consLSEngine.Init(consDB, consDlManager, app, secp256k1Signer, consAdminHandlers, publicKey, consReqClient, storage) // Setup Request Bus svcs := monitor.NewServices(eth, consDB, appDepositHandler, consAdminHandlers, batchSize, chainID) @@ -296,7 +305,7 @@ func validatorNode(cmd *cobra.Command, args []string) { mDB = rawMonitorDb } - consSync.Init(consDB, mDB, tDB, consGossipClient, consGossipHandlers, consTxPool, consLSEngine, app, consAdminHandlers, peerManager) + consSync.Init(consDB, mDB, tDB, consGossipClient, consGossipHandlers, consTxPool, consLSEngine, app, consAdminHandlers, peerManager, storage) localStateHandler.Init(consDB, app, consGossipHandlers, publicKey, consSync.Safe) statusLogger.Init(consLSEngine, peerManager, consAdminHandlers, mon) @@ -306,6 +315,8 @@ func validatorNode(cmd *cobra.Command, args []string) { defer func() { os.Exit(0) }() defer func() { logger.Warning("Graceful unwind of core process complete.") }() + go storage.Start() + go statusLogger.Run() defer statusLogger.Close() diff --git a/consensus/admin/handlers.go b/consensus/admin/handlers.go index 7f148849..8ff4bb08 100644 --- a/consensus/admin/handlers.go +++ b/consensus/admin/handlers.go @@ -11,6 +11,7 @@ import ( "github.com/MadBase/MadNet/consensus/objs" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" + "github.com/MadBase/MadNet/dynamics" "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/interfaces" "github.com/MadBase/MadNet/logging" @@ -41,11 +42,12 @@ type Handlers struct { ethAcct []byte ethPubk []byte appHandler appmock.Application + storage dynamics.StorageGetter ReceiveLock chan interfaces.Lockable } // Init creates all fields and binds external services -func (ah *Handlers) Init(chainID uint32, database *db.Database, secret []byte, appHandler appmock.Application, ethPubk []byte) { +func (ah *Handlers) Init(chainID uint32, database *db.Database, secret []byte, appHandler appmock.Application, ethPubk []byte, storage dynamics.StorageGetter) { ctx := context.Background() subCtx, cancelFunc := context.WithCancel(ctx) ah.ctx = subCtx @@ -58,6 +60,7 @@ func (ah *Handlers) Init(chainID uint32, database *db.Database, secret []byte, a ah.secret = utils.CopySlice(secret) ah.ethAcct = crypto.GetAccount(ethPubk) ah.ReceiveLock = make(chan interfaces.Lockable) + ah.storage = storage } // Close shuts down all workers @@ -260,6 +263,28 @@ func (ah *Handlers) AddSnapshot(bh *objs.BlockHeader, startingEthDKG bool) error }) } +// UpdateDynamicStorage updates dynamic storage values. +func (ah *Handlers) UpdateDynamicStorage(txn *badger.Txn, key, value string, epoch uint32) error { + mutex, ok := ah.getLock() + if !ok { + return nil + } + mutex.Lock() + defer mutex.Unlock() + + update, err := dynamics.NewUpdate(key, value, epoch) + if err != nil { + utils.DebugTrace(ah.logger, err) + return err + } + err = ah.storage.UpdateStorage(txn, update) + if err != nil { + utils.DebugTrace(ah.logger, err) + return err + } + return nil +} + // IsInitialized returns if the database has been initialized yet func (ah *Handlers) IsInitialized() bool { ah.RLock() @@ -369,7 +394,10 @@ func (ah *Handlers) AddPrivateKey(pk []byte, curveSpec constants.CurveSpec) erro privk := utils.CopySlice(pk) // bn key signer := crypto.BNGroupSigner{} - signer.SetPrivk(privk) + err := signer.SetPrivk(privk) + if err != nil { + return err + } pubkey, err := signer.PubkeyShare() if err != nil { return err diff --git a/consensus/db/db.go b/consensus/db/db.go index df23240c..4c6e2d58 100644 --- a/consensus/db/db.go +++ b/consensus/db/db.go @@ -37,11 +37,11 @@ func (db *Database) DB() *badger.DB { return db.rawDB.db } -func (db *Database) View(fn TxnFunc) error { +func (db *Database) View(fn func(txn *badger.Txn) error) error { return db.rawDB.View(fn) } -func (db *Database) Update(fn TxnFunc) error { +func (db *Database) Update(fn func(txn *badger.Txn) error) error { db.Lock() defer db.Unlock() return db.rawDB.Update(fn) @@ -55,6 +55,14 @@ func (db *Database) GarbageCollect() error { return db.rawDB.GarbageCollect() } +func (db *Database) SetValue(txn *badger.Txn, key, value []byte) error { + return db.rawDB.SetValue(txn, key, value) +} + +func (db *Database) GetValue(txn *badger.Txn, key []byte) ([]byte, error) { + return db.rawDB.getValue(txn, key) +} + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// diff --git a/consensus/db/db_test.go b/consensus/db/db_test.go index 9eba61ea..b9de43bb 100644 --- a/consensus/db/db_test.go +++ b/consensus/db/db_test.go @@ -82,15 +82,21 @@ func newDB(t *testing.T) (*testDB, *Database, *testparams) { func TestRoundState(t *testing.T) { groupSigner := &crypto.BNGroupSigner{} - groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + err := groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } groupKey, _ := groupSigner.PubkeyShare() bnSigner := &crypto.BNGroupSigner{} - bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + err = bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + if err != nil { + t.Fatal(err) + } bnKey, _ := groupSigner.PubkeyShare() secpSigner := &crypto.Secp256k1Signer{} - err := secpSigner.SetPrivk(crypto.Hasher([]byte("secret3"))) + err = secpSigner.SetPrivk(crypto.Hasher([]byte("secret3"))) if err != nil { t.Fatal(err) } @@ -147,15 +153,21 @@ func TestRoundState(t *testing.T) { func TestBlockHeader(t *testing.T) { groupSigner := &crypto.BNGroupSigner{} - groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + err := groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } bnSigner := &crypto.BNGroupSigner{} - bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + err = bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + if err != nil { + t.Fatal(err) + } tbd, db, p := newDB(t) defer tbd.Close() badgerD := tbd.db - err := badgerD.Update(func(txn *badger.Txn) error { + err = badgerD.Update(func(txn *badger.Txn) error { sig, err := groupSigner.Sign(p.PrevBlock) if err != nil { t.Fatal(err) @@ -286,15 +298,21 @@ func TestBlockHeader(t *testing.T) { func TestEncryptedStore(t *testing.T) { groupSigner := &crypto.BNGroupSigner{} - groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + err := groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } bnSigner := &crypto.BNGroupSigner{} - bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + err = bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + if err != nil { + t.Fatal(err) + } tbd, db, _ := newDB(t) defer tbd.Close() badgerD := tbd.db - err := badgerD.Update(func(txn *badger.Txn) error { + err = badgerD.Update(func(txn *badger.Txn) error { name := []byte("foo") ec := &objs.EncryptedStore{ Name: name, @@ -319,18 +337,24 @@ func TestEncryptedStore(t *testing.T) { func TestValidatorSet(t *testing.T) { groupSigner := &crypto.BNGroupSigner{} - groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + err := groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } groupKey, _ := groupSigner.PubkeyShare() bnSigner := &crypto.BNGroupSigner{} - bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + err = bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + if err != nil { + t.Fatal(err) + } tbd, db, p := newDB(t) _ = p _ = db defer tbd.Close() badgerD := tbd.db - err := badgerD.Update(func(txn *badger.Txn) error { + err = badgerD.Update(func(txn *badger.Txn) error { vkey0 := crypto.Hasher([]byte("s0"))[12:] gShare0 := crypto.Hasher([]byte("g0")) val0 := &objs.Validator{ @@ -402,21 +426,27 @@ func TestValidatorSet(t *testing.T) { func TestSnapShotMany(t *testing.T) { groupSigner := &crypto.BNGroupSigner{} - groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + err := groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } bnSigner := &crypto.BNGroupSigner{} - bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + err = bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + if err != nil { + t.Fatal(err) + } tbd, db, p := newDB(t) defer tbd.Close() badgerD := tbd.db var bhash []byte - err := badgerD.Update(func(txn *badger.Txn) error { + err = badgerD.Update(func(txn *badger.Txn) error { sig, err := groupSigner.Sign(p.PrevBlock) if err != nil { t.Fatal(err) } - for i := uint32(1); i < 1025; i++ { + for i := uint32(1); i < constants.EpochLength+1; i++ { bh := &objs.BlockHeader{ SigGroup: sig, BClaims: &objs.BClaims{ @@ -467,16 +497,22 @@ func TestSnapShotMany(t *testing.T) { func TestSnapShotOne(t *testing.T) { groupSigner := &crypto.BNGroupSigner{} - groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + err := groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } bnSigner := &crypto.BNGroupSigner{} - bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + err = bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + if err != nil { + t.Fatal(err) + } tbd, db, p := newDB(t) defer tbd.Close() badgerD := tbd.db var bhash []byte - err := badgerD.Update(func(txn *badger.Txn) error { + err = badgerD.Update(func(txn *badger.Txn) error { sig, err := groupSigner.Sign(p.PrevBlock) if err != nil { t.Fatal(err) @@ -528,21 +564,27 @@ func TestSnapShotOne(t *testing.T) { func TestGetLastCommittedBHFSMany(t *testing.T) { groupSigner := &crypto.BNGroupSigner{} - groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + err := groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } bnSigner := &crypto.BNGroupSigner{} - bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + err = bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + if err != nil { + t.Fatal(err) + } tbd, db, p := newDB(t) defer tbd.Close() badgerD := tbd.db var bhash []byte - err := badgerD.Update(func(txn *badger.Txn) error { + err = badgerD.Update(func(txn *badger.Txn) error { sig, err := groupSigner.Sign(p.PrevBlock) if err != nil { t.Fatal(err) } - for i := uint32(1); i < 1025; i++ { + for i := uint32(1); i < constants.EpochLength+1; i++ { bh := &objs.BlockHeader{ SigGroup: sig, BClaims: &objs.BClaims{ @@ -591,16 +633,22 @@ func TestGetLastCommittedBHFSMany(t *testing.T) { func TestGetLastCommittedBHFSOne(t *testing.T) { groupSigner := &crypto.BNGroupSigner{} - groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + err := groupSigner.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } bnSigner := &crypto.BNGroupSigner{} - bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + err = bnSigner.SetPrivk(crypto.Hasher([]byte("secret2"))) + if err != nil { + t.Fatal(err) + } tbd, db, p := newDB(t) defer tbd.Close() badgerD := tbd.db var bhash []byte - err := badgerD.Update(func(txn *badger.Txn) error { + err = badgerD.Update(func(txn *badger.Txn) error { sig, err := groupSigner.Sign(p.PrevBlock) if err != nil { t.Fatal(err) diff --git a/consensus/db/rawdb.go b/consensus/db/rawdb.go index 489b9c8e..b6cad36e 100644 --- a/consensus/db/rawdb.go +++ b/consensus/db/rawdb.go @@ -34,11 +34,11 @@ type rawDataBase struct { logger *logrus.Logger } -func (db *rawDataBase) View(fn TxnFunc) error { +func (db *rawDataBase) View(fn func(txn *badger.Txn) error) error { return db.db.View(fn) } -func (db *rawDataBase) Update(fn TxnFunc) error { +func (db *rawDataBase) Update(fn func(txn *badger.Txn) error) error { txn := db.db.NewTransaction(true) defer txn.Discard() if err := fn(txn); err != nil { diff --git a/consensus/dman/actors_test.go b/consensus/dman/actors_test.go index d6701126..d976fa60 100644 --- a/consensus/dman/actors_test.go +++ b/consensus/dman/actors_test.go @@ -302,7 +302,10 @@ func makeGoodBlock(t *testing.T) []*objs.BlockHeader { t.Fatal(err) } gk := crypto.BNGroupSigner{} - gk.SetPrivk(crypto.Hasher([]byte("secret"))) + err = gk.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } sig, err := gk.Sign(bhsh) if err != nil { t.Fatal(err) diff --git a/consensus/dman/dman.go b/consensus/dman/dman.go index 896ffc32..71bd4557 100644 --- a/consensus/dman/dman.go +++ b/consensus/dman/dman.go @@ -159,7 +159,7 @@ func (dm *DMan) SyncOneBH(txn *badger.Txn, syncToBH *objs.BlockHeader, maxBHSeen bhCache, inCache := dm.downloadActor.bhc.Get(targetHeight) if !inCache && currentHeight != targetHeight { - for i := currentHeight + 1; i < currentHeight+1024; i++ { + for i := currentHeight + 1; i < currentHeight+constants.EpochLength; i++ { height := i if height > maxBHSeen.BClaims.Height { break @@ -226,7 +226,6 @@ func (dm *DMan) SyncOneBH(txn *badger.Txn, syncToBH *objs.BlockHeader, maxBHSeen } return txs, bhCache, true, nil - } func (dm *DMan) DownloadTxs(height, round uint32, txHshLst [][]byte) { diff --git a/consensus/dman/txcache_test.go b/consensus/dman/txcache_test.go index be7162ad..383da99d 100644 --- a/consensus/dman/txcache_test.go +++ b/consensus/dman/txcache_test.go @@ -76,6 +76,8 @@ func Test_txCache_GetHeight(t *testing.T) { h2t, _ := txs2[0].TxHash() if string(h2t) != string(h2) { + t.Logf("h2: %s", h2) + t.Logf("h2t: %s", h2t) t.Fatal("2: bad hash") } txs3, _ := txc.GetHeight(3) diff --git a/consensus/gossip/client.go b/consensus/gossip/client.go index dbca7e5e..ed67ce9e 100644 --- a/consensus/gossip/client.go +++ b/consensus/gossip/client.go @@ -9,6 +9,7 @@ import ( "github.com/MadBase/MadNet/consensus/lstate" "github.com/MadBase/MadNet/consensus/objs" "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/dynamics" "github.com/MadBase/MadNet/interfaces" "github.com/MadBase/MadNet/logging" "github.com/MadBase/MadNet/middleware" @@ -77,6 +78,7 @@ type Client struct { lastHeight uint32 lastRound uint32 app appClient + storage dynamics.StorageGetter inSync *mutexBool isValidator *mutexBool @@ -84,7 +86,7 @@ type Client struct { // Init sets ups all subscriptions. This MUST be run at least once. // It has no effect if run more than once. -func (mb *Client) Init(database *db.Database, client pb.P2PClient, app appClient) { +func (mb *Client) Init(database *db.Database, client pb.P2PClient, app appClient, storage dynamics.StorageGetter) { background := context.Background() ctx, cf := context.WithCancel(background) mb.logger = logging.GetLogger(constants.LoggerGossipBus) @@ -97,6 +99,7 @@ func (mb *Client) Init(database *db.Database, client pb.P2PClient, app appClient mb.sstore = &lstate.Store{} mb.inSync = &mutexBool{} mb.isValidator = &mutexBool{} + mb.storage = storage mb.sstore.Init(database) mb.gossipTimeout = constants.MsgTimeout } diff --git a/consensus/gossip/handlers.go b/consensus/gossip/handlers.go index f0311ecb..f5ed5991 100644 --- a/consensus/gossip/handlers.go +++ b/consensus/gossip/handlers.go @@ -5,6 +5,7 @@ import ( "time" "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/dynamics" "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/utils" "google.golang.org/grpc/codes" @@ -49,7 +50,8 @@ type Handlers struct { // channels acts as per message queues with validation occurring before // the message is queued up - app appHandler + app appHandler + storage dynamics.StorageGetter height *mutexUint32 chainID *mutexUint32 @@ -72,7 +74,7 @@ func (mb *Handlers) getLock(ctx context.Context) (interfaces.Lockable, bool) { // Init will initialize the gossip consumer // it must be run at least once and will have no // effect if run more than once -func (mb *Handlers) Init(database *db.Database, client pb.P2PClient, app appHandler, handlers *lstate.Handlers) { +func (mb *Handlers) Init(database *db.Database, client pb.P2PClient, app appHandler, handlers *lstate.Handlers, storage dynamics.StorageGetter) { mb.logger = logging.GetLogger(constants.LoggerGossipBus) mb.client = client mb.app = app @@ -82,6 +84,7 @@ func (mb *Handlers) Init(database *db.Database, client pb.P2PClient, app appHand ctx, cf := context.WithCancel(background) mb.cancelCtx = cf mb.ctx = ctx + mb.storage = storage mb.height = &mutexUint32{} mb.chainID = &mutexUint32{} mb.isSync = &mutexBool{} diff --git a/consensus/gossip/state_test.go b/consensus/gossip/state_test.go index 6a621bae..3568d900 100644 --- a/consensus/gossip/state_test.go +++ b/consensus/gossip/state_test.go @@ -483,7 +483,10 @@ func makeSigners(t *testing.T) ([]byte, []*crypto.BNGroupSigner, [][]byte, []*cr gpk1 := new(bn256.G2).ScalarBaseMult(gsk1) groupShares[0] = gpk1.Marshal() s1 := new(crypto.BNGroupSigner) - s1.SetPrivk(gsk1.Bytes()) + err := s1.SetPrivk(gsk1.Bytes()) + if err != nil { + t.Fatal(err) + } sig1, err := s1.Sign(msg) if err != nil { t.Fatal(err) @@ -494,7 +497,10 @@ func makeSigners(t *testing.T) ([]byte, []*crypto.BNGroupSigner, [][]byte, []*cr gpk2 := new(bn256.G2).ScalarBaseMult(gsk2) groupShares[1] = gpk2.Marshal() s2 := new(crypto.BNGroupSigner) - s2.SetPrivk(gsk2.Bytes()) + err = s2.SetPrivk(gsk2.Bytes()) + if err != nil { + t.Fatal(err) + } sig2, err := s2.Sign(msg) if err != nil { t.Fatal(err) @@ -505,7 +511,10 @@ func makeSigners(t *testing.T) ([]byte, []*crypto.BNGroupSigner, [][]byte, []*cr gpk3 := new(bn256.G2).ScalarBaseMult(gsk3) groupShares[2] = gpk3.Marshal() s3 := new(crypto.BNGroupSigner) - s3.SetPrivk(gsk3.Bytes()) + err = s3.SetPrivk(gsk3.Bytes()) + if err != nil { + t.Fatal(err) + } sig3, err := s3.Sign(msg) if err != nil { t.Fatal(err) @@ -516,7 +525,10 @@ func makeSigners(t *testing.T) ([]byte, []*crypto.BNGroupSigner, [][]byte, []*cr gpk4 := new(bn256.G2).ScalarBaseMult(gsk4) groupShares[3] = gpk4.Marshal() s4 := new(crypto.BNGroupSigner) - s4.SetPrivk(gsk4.Bytes()) + err = s4.SetPrivk(gsk4.Bytes()) + if err != nil { + t.Fatal(err) + } sig4, err := s4.Sign(msg) if err != nil { t.Fatal(err) diff --git a/consensus/lstate/appman.go b/consensus/lstate/appman.go index c8b69723..7ad51f0b 100644 --- a/consensus/lstate/appman.go +++ b/consensus/lstate/appman.go @@ -7,9 +7,8 @@ import ( "github.com/MadBase/MadNet/consensus/objs" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/errorz" - "github.com/MadBase/MadNet/utils" - "github.com/MadBase/MadNet/interfaces" + "github.com/MadBase/MadNet/utils" "github.com/dgraph-io/badger/v2" ) @@ -33,7 +32,7 @@ func (ce *Engine) AddPendingTx(txn *badger.Txn, d []interfaces.Transaction) erro func (ce *Engine) getValidValue(txn *badger.Txn, rs *RoundStates) ([][]byte, []byte, []byte, []byte, error) { chainID := rs.OwnState.SyncToBH.BClaims.ChainID - txs, stateRoot, err := ce.appHandler.GetValidProposal(txn, chainID, rs.OwnState.SyncToBH.BClaims.Height+1, constants.MaxProposalSize) + txs, stateRoot, err := ce.appHandler.GetValidProposal(txn, chainID, rs.OwnState.SyncToBH.BClaims.Height+1, ce.storage.GetMaxProposalSize()) if err != nil { utils.DebugTrace(ce.logger, err) return nil, nil, nil, nil, err @@ -62,7 +61,7 @@ func (ce *Engine) getValidValue(txn *badger.Txn, rs *RoundStates) ([][]byte, []b utils.DebugTrace(ce.logger, err) return nil, nil, nil, nil, err } - headerRoot = make([]byte, 32) + headerRoot = make([]byte, constants.HashLen) } return txHashes, txRootHash, stateRoot, headerRoot, nil } diff --git a/consensus/lstate/engine.go b/consensus/lstate/engine.go index 477f6ddb..b3e2e2f8 100644 --- a/consensus/lstate/engine.go +++ b/consensus/lstate/engine.go @@ -6,9 +6,6 @@ import ( "errors" "fmt" - "github.com/MadBase/MadNet/errorz" - "github.com/MadBase/MadNet/utils" - "github.com/MadBase/MadNet/consensus/admin" "github.com/MadBase/MadNet/consensus/appmock" "github.com/MadBase/MadNet/consensus/db" @@ -17,7 +14,10 @@ import ( "github.com/MadBase/MadNet/consensus/request" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" + "github.com/MadBase/MadNet/dynamics" + "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/logging" + "github.com/MadBase/MadNet/utils" "github.com/dgraph-io/badger/v2" "github.com/sirupsen/logrus" ) @@ -44,11 +44,13 @@ type Engine struct { ethAcct []byte EthPubk []byte + storage dynamics.StorageGetter + dm *dman.DMan } // Init will initialize the Consensus Engine and all sub modules -func (ce *Engine) Init(database *db.Database, dm *dman.DMan, app appmock.Application, signer *crypto.Secp256k1Signer, adminHandlers *admin.Handlers, publicKey []byte, rbusClient *request.Client) { +func (ce *Engine) Init(database *db.Database, dm *dman.DMan, app appmock.Application, signer *crypto.Secp256k1Signer, adminHandlers *admin.Handlers, publicKey []byte, rbusClient *request.Client, storage dynamics.StorageGetter) { background := context.Background() ctx, cf := context.WithCancel(background) ce.cancelCtx = cf @@ -70,9 +72,11 @@ func (ce *Engine) Init(database *db.Database, dm *dman.DMan, app appmock.Applica appHandler: app, requestBus: ce.RequestBus, } - ce.fastSync.Init(database) + ce.storage = storage + ce.fastSync.Init(database, storage) } +// Status updates the status of the consensus engine func (ce *Engine) Status(status map[string]interface{}) (map[string]interface{}, error) { var rs *RoundStates err := ce.database.View(func(txn *badger.Txn) error { @@ -102,6 +106,7 @@ func (ce *Engine) Status(status map[string]interface{}) (map[string]interface{}, return status, nil } +// UpdateLocalState updates the local state of the consensus engine func (ce *Engine) UpdateLocalState() (bool, error) { var isSync bool updateLocalState := true @@ -152,6 +157,12 @@ func (ce *Engine) UpdateLocalState() (bool, error) { if err != nil { return err } + // Load storage + err = ce.storage.LoadStorage(txn, utils.Epoch(roundState.OwnState.SyncToBH.BClaims.Height)) + if err != nil { + utils.DebugTrace(ce.logger, err) + return err + } if roundState.OwnState.SyncToBH.BClaims.Height%constants.EpochLength == 0 { safe, err := ce.database.GetSafeToProceed(txn, roundState.OwnState.SyncToBH.BClaims.Height) if err != nil { @@ -376,6 +387,11 @@ func (ce *Engine) updateLocalStateInternal(txn *badger.Txn, rs *RoundStates) (bo return true, nil } + // Ensure that storage has updated values + proposalStepTO := ce.storage.GetProposalStepTimeout() + preVoteStepTO := ce.storage.GetPreVoteStepTimeout() + preCommitStepTO := ce.storage.GetPreCommitStepTimeout() + // iterate all possibles from nextRound down to proposal // and take that action ISProposer := rs.LocalIsProposer() @@ -385,9 +401,9 @@ func (ce *Engine) updateLocalStateInternal(txn *badger.Txn, rs *RoundStates) (bo PCCurrent := os.PCCurrent(rcert) PCNCurrent := os.PCNCurrent(rcert) NRCurrent := os.NRCurrent(rcert) - PTOExpired := rs.OwnValidatingState.PTOExpired() - PVTOExpired := rs.OwnValidatingState.PVTOExpired() - PCTOExpired := rs.OwnValidatingState.PCTOExpired() + PTOExpired := rs.OwnValidatingState.PTOExpired(proposalStepTO) + PVTOExpired := rs.OwnValidatingState.PVTOExpired(preVoteStepTO) + PCTOExpired := rs.OwnValidatingState.PCTOExpired(preCommitStepTO) // dispatch to handlers if NRCurrent { @@ -482,6 +498,7 @@ func (ce *Engine) updateLocalStateInternal(txn *badger.Txn, rs *RoundStates) (bo return true, nil } +// Sync attempts to synchronize the local state of consensus engine func (ce *Engine) Sync() (bool, error) { // see if sync is done // if yes exit @@ -495,6 +512,11 @@ func (ce *Engine) Sync() (bool, error) { } return nil } + err = ce.storage.LoadStorage(txn, utils.Epoch(rs.OwnState.SyncToBH.BClaims.Height)) + if err != nil { + utils.DebugTrace(ce.logger, err) + return err + } // begin handling logic if rs.OwnState.MaxBHSeen.BClaims.Height == rs.OwnState.SyncToBH.BClaims.Height { syncDone = true @@ -599,7 +621,11 @@ func (ce *Engine) loadValidationKey(rs *RoundStates) error { return nil } signer := &crypto.BNGroupSigner{} - signer.SetPrivk(pk) + err = signer.SetPrivk(pk) + if err != nil { + utils.DebugTrace(ce.logger, err) + return nil + } err = signer.SetGroupPubk(rs.ValidatorSet.GroupKey) if err != nil { utils.DebugTrace(ce.logger, err) diff --git a/consensus/lstate/fastsync.go b/consensus/lstate/fastsync.go index 7d22aad9..c71dd748 100644 --- a/consensus/lstate/fastsync.go +++ b/consensus/lstate/fastsync.go @@ -13,6 +13,7 @@ import ( "github.com/MadBase/MadNet/consensus/objs" "github.com/MadBase/MadNet/consensus/request" "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/dynamics" "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/logging" "github.com/MadBase/MadNet/middleware" @@ -243,15 +244,15 @@ type bhCache struct { minHeight uint32 } -func (sc *bhCache) Init() { - sc.objs = make(map[nodeKey]*stateResponse) +func (bhc *bhCache) Init() { + bhc.objs = make(map[nodeKey]*stateResponse) } -func (sc *bhCache) getLeafKeys(maxNumber int) []nodeKey { - sc.RLock() - defer sc.RUnlock() +func (bhc *bhCache) getLeafKeys(maxNumber int) []nodeKey { + bhc.RLock() + defer bhc.RUnlock() nodeKeys := []nodeKey{} - for k := range sc.objs { + for k := range bhc.objs { nodeKeys = append(nodeKeys, k) if len(nodeKeys) >= maxNumber { break @@ -260,38 +261,38 @@ func (sc *bhCache) getLeafKeys(maxNumber int) []nodeKey { return nodeKeys } -func (sc *bhCache) contains(nk nodeKey) bool { - sc.RLock() - defer sc.RUnlock() - if sc.objs[nk] == nil { +func (bhc *bhCache) contains(nk nodeKey) bool { + bhc.RLock() + defer bhc.RUnlock() + if bhc.objs[nk] == nil { return false } return true } -func (sc *bhCache) insert(sr *stateResponse) error { - sc.Lock() - defer sc.Unlock() +func (bhc *bhCache) insert(sr *stateResponse) error { + bhc.Lock() + defer bhc.Unlock() nk, err := newNodeKey(sr.key) if err != nil { return err } - sc.objs[nk] = sr + bhc.objs[nk] = sr return nil } -func (sc *bhCache) pop(key []byte) (*stateResponse, error) { - sc.Lock() - defer sc.Unlock() +func (bhc *bhCache) pop(key []byte) (*stateResponse, error) { + bhc.Lock() + defer bhc.Unlock() nk, err := newNodeKey(key) if err != nil { return nil, err } - if sc.objs[nk] == nil { + if bhc.objs[nk] == nil { return nil, errorz.ErrInvalid{}.New("Error in bhCache.pop: missing key in pop request") } - result := sc.objs[nk] - delete(sc.objs, nk) + result := bhc.objs[nk] + delete(bhc.objs, nk) return result, nil } @@ -301,15 +302,15 @@ type bhNodeCache struct { minHeight uint32 } -func (sc *bhNodeCache) Init() { - sc.objs = make(map[nodeKey]*nodeResponse) +func (bhnc *bhNodeCache) Init() { + bhnc.objs = make(map[nodeKey]*nodeResponse) } -func (sc *bhNodeCache) getNodeKeys(maxNumber int) []nodeKey { - sc.RLock() - defer sc.RUnlock() +func (bhnc *bhNodeCache) getNodeKeys(maxNumber int) []nodeKey { + bhnc.RLock() + defer bhnc.RUnlock() nodeKeys := []nodeKey{} - for k := range sc.objs { + for k := range bhnc.objs { nodeKeys = append(nodeKeys, k) if len(nodeKeys) >= maxNumber { break @@ -318,38 +319,38 @@ func (sc *bhNodeCache) getNodeKeys(maxNumber int) []nodeKey { return nodeKeys } -func (sc *bhNodeCache) contains(nk nodeKey) bool { - sc.RLock() - defer sc.RUnlock() - if sc.objs[nk] == nil { +func (bhnc *bhNodeCache) contains(nk nodeKey) bool { + bhnc.RLock() + defer bhnc.RUnlock() + if bhnc.objs[nk] == nil { return false } return true } -func (sc *bhNodeCache) insert(sr *nodeResponse) error { - sc.Lock() - defer sc.Unlock() +func (bhnc *bhNodeCache) insert(sr *nodeResponse) error { + bhnc.Lock() + defer bhnc.Unlock() nk, err := newNodeKey(sr.root) if err != nil { return err } - sc.objs[nk] = sr + bhnc.objs[nk] = sr return nil } -func (sc *bhNodeCache) pop(key []byte) (*nodeResponse, error) { - sc.Lock() - defer sc.Unlock() +func (bhnc *bhNodeCache) pop(key []byte) (*nodeResponse, error) { + bhnc.Lock() + defer bhnc.Unlock() nk, err := newNodeKey(key) if err != nil { return nil, err } - if sc.objs[nk] == nil { + if bhnc.objs[nk] == nil { return nil, errorz.ErrInvalid{}.New("Error in bhNodeCache.pop: missing key in pop request") } - result := sc.objs[nk] - delete(sc.objs, nk) + result := bhnc.objs[nk] + delete(bhnc.objs, nk) return result, nil } @@ -406,12 +407,12 @@ func (a *atomicU32) Get() uint32 { type workFunc func() type SnapShotManager struct { - appHandler appmock.Application - requestBus *request.Client - + appHandler appmock.Application + requestBus *request.Client database *db.Database logger *logrus.Logger snapShotHeight *atomicU32 + storage dynamics.StorageGetter hdrNodeCache *bhNodeCache hdrLeafCache *bhCache @@ -443,161 +444,162 @@ type SnapShotManager struct { } // Init initializes the SnapShotManager -func (ndm *SnapShotManager) Init(database *db.Database) { - ndm.snapShotHeight = new(atomicU32) - ndm.logger = logging.GetLogger(constants.LoggerConsensus) - ndm.database = database - ndm.hdrNodeCache = &bhNodeCache{} - ndm.hdrNodeCache.Init() - ndm.hdrLeafCache = &bhCache{} - ndm.hdrLeafCache.Init() - ndm.stateNodeCache = &nodeCache{} - ndm.stateNodeCache.Init() - ndm.stateLeafCache = &stateCache{} - ndm.stateLeafCache.Init() - ndm.hdrNodeDLs = &downloadTracker{ +func (ssm *SnapShotManager) Init(database *db.Database, storage dynamics.StorageGetter) { + ssm.storage = storage + ssm.snapShotHeight = new(atomicU32) + ssm.logger = logging.GetLogger(constants.LoggerConsensus) + ssm.database = database + ssm.hdrNodeCache = &bhNodeCache{} + ssm.hdrNodeCache.Init() + ssm.hdrLeafCache = &bhCache{} + ssm.hdrLeafCache.Init() + ssm.stateNodeCache = &nodeCache{} + ssm.stateNodeCache.Init() + ssm.stateLeafCache = &stateCache{} + ssm.stateLeafCache.Init() + ssm.hdrNodeDLs = &downloadTracker{ sync.RWMutex{}, make(map[nodeKey]bool), } - ndm.hdrLeafDLs = &downloadTracker{ + ssm.hdrLeafDLs = &downloadTracker{ sync.RWMutex{}, make(map[nodeKey]bool), } - ndm.stateLeafDLs = &downloadTracker{ + ssm.stateLeafDLs = &downloadTracker{ sync.RWMutex{}, make(map[nodeKey]bool), } - ndm.stateNodeDLs = &downloadTracker{ + ssm.stateNodeDLs = &downloadTracker{ sync.RWMutex{}, make(map[nodeKey]bool), } - ndm.statusChan = make(chan string) - ndm.hdrLeafDlChan = make(chan *dlReq, chanBuffering) - ndm.hdrNodeDlChan = make(chan *dlReq, chanBuffering) - ndm.stateNodeDlChan = make(chan *dlReq, chanBuffering) - ndm.stateLeafDlChan = make(chan *dlReq, chanBuffering) - ndm.workChan = make(chan workFunc, chanBuffering*2) - ndm.closeChan = make(chan struct{}) - ndm.closeOnce = sync.Once{} - - go ndm.downloadWithRetryHdrLeafWorker() - go ndm.downloadWithRetryHdrNodeWorker() - go ndm.downloadWithRetryStateLeafWorker() - go ndm.downloadWithRetryStateNodeWorker() - go ndm.loggingDelayer() -} - -func (ndm *SnapShotManager) close() { - ndm.closeOnce.Do(func() { - close(ndm.closeChan) + ssm.statusChan = make(chan string) + ssm.hdrLeafDlChan = make(chan *dlReq, chanBuffering) + ssm.hdrNodeDlChan = make(chan *dlReq, chanBuffering) + ssm.stateNodeDlChan = make(chan *dlReq, chanBuffering) + ssm.stateLeafDlChan = make(chan *dlReq, chanBuffering) + ssm.workChan = make(chan workFunc, chanBuffering*2) + ssm.closeChan = make(chan struct{}) + ssm.closeOnce = sync.Once{} + + go ssm.downloadWithRetryHdrLeafWorker() + go ssm.downloadWithRetryHdrNodeWorker() + go ssm.downloadWithRetryStateLeafWorker() + go ssm.downloadWithRetryStateNodeWorker() + go ssm.loggingDelayer() +} + +func (ssm *SnapShotManager) close() { + ssm.closeOnce.Do(func() { + close(ssm.closeChan) }) } -func (ndm *SnapShotManager) startFastSync(txn *badger.Txn, snapShotBlockHeader *objs.BlockHeader) error { - if ndm.finalizeFastSyncChan == nil { - ndm.finalizeOnce = sync.Once{} - ndm.finalizeFastSyncChan = make(chan struct{}) +func (ssm *SnapShotManager) startFastSync(txn *badger.Txn, snapShotBlockHeader *objs.BlockHeader) error { + if ssm.finalizeFastSyncChan == nil { + ssm.finalizeOnce = sync.Once{} + ssm.finalizeFastSyncChan = make(chan struct{}) for i := 0; i < minWorkers; i++ { - go ndm.worker(ndm.finalizeFastSyncChan) + go ssm.worker(ssm.finalizeFastSyncChan) } } - ndm.tailSyncHeight = snapShotBlockHeader.BClaims.Height - if err := ndm.database.SetCommittedBlockHeaderFastSync(txn, snapShotBlockHeader); err != nil { - utils.DebugTrace(ndm.logger, err) + ssm.tailSyncHeight = snapShotBlockHeader.BClaims.Height + if err := ssm.database.SetCommittedBlockHeaderFastSync(txn, snapShotBlockHeader); err != nil { + utils.DebugTrace(ssm.logger, err) return err } - ndm.snapShotHeight.Set(snapShotBlockHeader.BClaims.Height) + ssm.snapShotHeight.Set(snapShotBlockHeader.BClaims.Height) // call dropBefore on the caches - ndm.stateNodeCache.dropBefore(snapShotBlockHeader.BClaims.Height) - ndm.stateLeafCache.dropBefore(snapShotBlockHeader.BClaims.Height) + ssm.stateNodeCache.dropBefore(snapShotBlockHeader.BClaims.Height) + ssm.stateLeafCache.dropBefore(snapShotBlockHeader.BClaims.Height) // cleanup the db of any previous data - if err := ndm.cleanupDatabase(txn); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.cleanupDatabase(txn); err != nil { + utils.DebugTrace(ssm.logger, err) return err } // insert the request for the root node into the database if !bytes.Equal(utils.CopySlice(snapShotBlockHeader.BClaims.StateRoot), make([]byte, constants.HashLen)) { // Do NOT request all-zero byte slice stateRoot - if err := ndm.database.SetPendingNodeKey(txn, utils.CopySlice(snapShotBlockHeader.BClaims.StateRoot), 0); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.database.SetPendingNodeKey(txn, utils.CopySlice(snapShotBlockHeader.BClaims.StateRoot), 0); err != nil { + utils.DebugTrace(ssm.logger, err) return err } } - if err := ndm.database.SetPendingHdrNodeKey(txn, utils.CopySlice(snapShotBlockHeader.BClaims.HeaderRoot), 0); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.database.SetPendingHdrNodeKey(txn, utils.CopySlice(snapShotBlockHeader.BClaims.HeaderRoot), 0); err != nil { + utils.DebugTrace(ssm.logger, err) return err } - canonicalBHTrieKey := ndm.database.MakeHeaderTrieKeyFromHeight(snapShotBlockHeader.BClaims.Height) + canonicalBHTrieKey := ssm.database.MakeHeaderTrieKeyFromHeight(snapShotBlockHeader.BClaims.Height) bHash, err := snapShotBlockHeader.BlockHash() if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } - if err := ndm.database.SetPendingHdrLeafKey(txn, canonicalBHTrieKey, bHash); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.database.SetPendingHdrLeafKey(txn, canonicalBHTrieKey, bHash); err != nil { + utils.DebugTrace(ssm.logger, err) return err } return nil } -func (ndm *SnapShotManager) cleanupDatabase(txn *badger.Txn) error { - if err := ndm.database.DropPendingLeafKeys(txn); err != nil { - utils.DebugTrace(ndm.logger, err) +func (ssm *SnapShotManager) cleanupDatabase(txn *badger.Txn) error { + if err := ssm.database.DropPendingLeafKeys(txn); err != nil { + utils.DebugTrace(ssm.logger, err) return err } - if err := ndm.database.DropPendingNodeKeys(txn); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.database.DropPendingNodeKeys(txn); err != nil { + utils.DebugTrace(ssm.logger, err) return err } - if err := ndm.appHandler.BeginSnapShotSync(txn); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.appHandler.BeginSnapShotSync(txn); err != nil { + utils.DebugTrace(ssm.logger, err) return err } return nil } -func (ndm *SnapShotManager) Update(txn *badger.Txn, snapShotBlockHeader *objs.BlockHeader) (bool, error) { +func (ssm *SnapShotManager) Update(txn *badger.Txn, snapShotBlockHeader *objs.BlockHeader) (bool, error) { // a difference in height implies the target has changed for the canonical // state, thus re-init the object and drop all stale data // return after the drop so the next iteration sees the dropped data is in the // db transaction - if ndm.snapShotHeight.Get() != snapShotBlockHeader.BClaims.Height { - err := ndm.startFastSync(txn, snapShotBlockHeader) + if ssm.snapShotHeight.Get() != snapShotBlockHeader.BClaims.Height { + err := ssm.startFastSync(txn, snapShotBlockHeader) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return false, err } return false, nil } - if err := ndm.updateSync(txn, snapShotBlockHeader.BClaims.Height); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.updateSync(txn, snapShotBlockHeader.BClaims.Height); err != nil { + utils.DebugTrace(ssm.logger, err) return false, err } - hnCount, snCount, slCount, hlCount, bhCount, err := ndm.getKeyCounts(txn) + hnCount, snCount, slCount, hlCount, bhCount, err := ssm.getKeyCounts(txn) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return false, err } logMsg := fmt.Sprintf("FastSyncing@%v |HN:%v HL:%v CBH:%v |SN:%v SL:%v |Prct:%v", snapShotBlockHeader.BClaims.Height, hnCount, hlCount, bhCount, snCount, slCount, (bhCount*100)/int(snapShotBlockHeader.BClaims.Height)) - ndm.status(logMsg) - if err := ndm.updateDls(txn, snapShotBlockHeader.BClaims.Height, bhCount, hlCount); err != nil { - utils.DebugTrace(ndm.logger, err) + ssm.status(logMsg) + if err := ssm.updateDls(txn, snapShotBlockHeader.BClaims.Height, bhCount, hlCount); err != nil { + utils.DebugTrace(ssm.logger, err) return false, err } pCount := (int(snapShotBlockHeader.BClaims.Height) - bhCount) if pCount < 0 { pCount = 0 } - pCount += ndm.hdrLeafDLs.Size() + ndm.hdrNodeDLs.Size() - pCount += ndm.stateNodeDLs.Size() + ndm.stateLeafDLs.Size() + pCount += ssm.hdrLeafDLs.Size() + ssm.hdrNodeDLs.Size() + pCount += ssm.stateNodeDLs.Size() + ssm.stateLeafDLs.Size() pCount += snCount + slCount + hnCount + hlCount if pCount == 0 { - if err := ndm.finalizeSync(txn, snapShotBlockHeader); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.finalizeSync(txn, snapShotBlockHeader); err != nil { + utils.DebugTrace(ssm.logger, err) return false, err } return true, nil @@ -605,164 +607,164 @@ func (ndm *SnapShotManager) Update(txn *badger.Txn, snapShotBlockHeader *objs.Bl return false, nil } -func (ndm *SnapShotManager) status(msg string) { +func (ssm *SnapShotManager) status(msg string) { select { - case ndm.statusChan <- msg: + case ssm.statusChan <- msg: return default: return } } -func (ndm *SnapShotManager) loggingDelayer() { +func (ssm *SnapShotManager) loggingDelayer() { for { select { - case msg := <-ndm.statusChan: - ndm.logger.Info(msg) - case <-ndm.closeChan: + case msg := <-ssm.statusChan: + ssm.logger.Info(msg) + case <-ssm.closeChan: return } time.Sleep(5 * time.Second) } } -func (ndm *SnapShotManager) getKeyCounts(txn *badger.Txn) (int, int, int, int, int, error) { - hnCount, err := ndm.database.CountPendingHdrNodeKeys(txn) +func (ssm *SnapShotManager) getKeyCounts(txn *badger.Txn) (int, int, int, int, int, error) { + hnCount, err := ssm.database.CountPendingHdrNodeKeys(txn) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return 0, 0, 0, 0, 0, err } - snCount, err := ndm.database.CountPendingNodeKeys(txn) + snCount, err := ssm.database.CountPendingNodeKeys(txn) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return 0, 0, 0, 0, 0, err } - slCount, err := ndm.database.CountPendingLeafKeys(txn) + slCount, err := ssm.database.CountPendingLeafKeys(txn) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return 0, 0, 0, 0, 0, err } - hlCount, err := ndm.database.CountPendingHdrLeafKeys(txn) + hlCount, err := ssm.database.CountPendingHdrLeafKeys(txn) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return 0, 0, 0, 0, 0, err } - bhCount, err := ndm.database.CountCommittedBlockHeaders(txn) + bhCount, err := ssm.database.CountCommittedBlockHeaders(txn) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return 0, 0, 0, 0, 0, err } return hnCount, snCount, slCount, hlCount, bhCount, nil } -func (ndm *SnapShotManager) finalizeSync(txn *badger.Txn, snapShotBlockHeader *objs.BlockHeader) error { - ndm.finalizeOnce.Do(func() { close(ndm.finalizeFastSyncChan) }) - if err := ndm.database.UpdateHeaderTrieRootFastSync(txn, snapShotBlockHeader); err != nil { - utils.DebugTrace(ndm.logger, err) +func (ssm *SnapShotManager) finalizeSync(txn *badger.Txn, snapShotBlockHeader *objs.BlockHeader) error { + ssm.finalizeOnce.Do(func() { close(ssm.finalizeFastSyncChan) }) + if err := ssm.database.UpdateHeaderTrieRootFastSync(txn, snapShotBlockHeader); err != nil { + utils.DebugTrace(ssm.logger, err) return err } - if err := ndm.appHandler.FinalizeSnapShotRoot(txn, snapShotBlockHeader.BClaims.StateRoot, snapShotBlockHeader.BClaims.Height); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.appHandler.FinalizeSnapShotRoot(txn, snapShotBlockHeader.BClaims.StateRoot, snapShotBlockHeader.BClaims.Height); err != nil { + utils.DebugTrace(ssm.logger, err) return err } return nil } -func (ndm *SnapShotManager) updateDls(txn *badger.Txn, snapShotHeight uint32, bhCount int, hlCount int) error { - if err := ndm.dlHdrNodes(txn, snapShotHeight); err != nil { - utils.DebugTrace(ndm.logger, err) +func (ssm *SnapShotManager) updateDls(txn *badger.Txn, snapShotHeight uint32, bhCount int, hlCount int) error { + if err := ssm.dlHdrNodes(txn, snapShotHeight); err != nil { + utils.DebugTrace(ssm.logger, err) return err } - if err := ndm.dlHdrLeaves(txn, snapShotHeight); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.dlHdrLeaves(txn, snapShotHeight); err != nil { + utils.DebugTrace(ssm.logger, err) return err } if hlCount == 0 && bhCount >= int(snapShotHeight)-int(constants.EpochLength) { - if err := ndm.dlStateNodes(txn, snapShotHeight); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.dlStateNodes(txn, snapShotHeight); err != nil { + utils.DebugTrace(ssm.logger, err) return err } - if err := ndm.dlStateLeaves(txn, snapShotHeight); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.dlStateLeaves(txn, snapShotHeight); err != nil { + utils.DebugTrace(ssm.logger, err) return err } } return nil } -func (ndm *SnapShotManager) updateSync(txn *badger.Txn, snapShotHeight uint32) error { - if err := ndm.syncHdrNodes(txn, snapShotHeight); err != nil { - utils.DebugTrace(ndm.logger, err) +func (ssm *SnapShotManager) updateSync(txn *badger.Txn, snapShotHeight uint32) error { + if err := ssm.syncHdrNodes(txn, snapShotHeight); err != nil { + utils.DebugTrace(ssm.logger, err) return err } - if err := ndm.syncHdrLeaves(txn, snapShotHeight); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.syncHdrLeaves(txn, snapShotHeight); err != nil { + utils.DebugTrace(ssm.logger, err) return err } - if err := ndm.syncTailingBlockHeaders(txn, snapShotHeight); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.syncTailingBlockHeaders(txn, snapShotHeight); err != nil { + utils.DebugTrace(ssm.logger, err) return err } - if err := ndm.syncStateNodes(txn, snapShotHeight); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.syncStateNodes(txn, snapShotHeight); err != nil { + utils.DebugTrace(ssm.logger, err) return err } - if err := ndm.syncStateLeaves(txn, snapShotHeight); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.syncStateLeaves(txn, snapShotHeight); err != nil { + utils.DebugTrace(ssm.logger, err) return err } return nil } -func (ndm *SnapShotManager) syncHdrNodes(txn *badger.Txn, snapShotHeight uint32) error { +func (ssm *SnapShotManager) syncHdrNodes(txn *badger.Txn, snapShotHeight uint32) error { // get a set of node header keys and sync the header trie based on those // node keys - nodeHdrKeys := ndm.hdrNodeCache.getNodeKeys(maxNumber) + nodeHdrKeys := ssm.hdrNodeCache.getNodeKeys(maxNumber) for i := 0; i < len(nodeHdrKeys); i++ { - resp, err := ndm.hdrNodeCache.pop(utils.CopySlice(nodeHdrKeys[i].key[:])) + resp, err := ssm.hdrNodeCache.pop(utils.CopySlice(nodeHdrKeys[i].key[:])) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) continue } // remove the keys from the pending set in the database - err = ndm.database.DeletePendingHdrNodeKey(txn, utils.CopySlice(nodeHdrKeys[i].key[:])) + err = ssm.database.DeletePendingHdrNodeKey(txn, utils.CopySlice(nodeHdrKeys[i].key[:])) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) } // store all of those nodes into the database and get new pending keys - pendingBatch, newLayer, lvs, err := ndm.database.SetSnapShotHdrNode(txn, resp.batch, resp.root, resp.layer) + pendingBatch, newLayer, lvs, err := ssm.database.SetSnapShotHdrNode(txn, resp.batch, resp.root, resp.layer) if err != nil { // should not return if err invalid - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) continue } // store new pending keys to db for j := 0; j < len(pendingBatch); j++ { - ok, err := ndm.database.ContainsSnapShotHdrNode(txn, utils.CopySlice(pendingBatch[j])) + ok, err := ssm.database.ContainsSnapShotHdrNode(txn, utils.CopySlice(pendingBatch[j])) if err != nil { return err } if ok { continue } - if err := ndm.database.SetPendingHdrNodeKey(txn, utils.CopySlice(pendingBatch[j]), newLayer); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.database.SetPendingHdrNodeKey(txn, utils.CopySlice(pendingBatch[j]), newLayer); err != nil { + utils.DebugTrace(ssm.logger, err) return err } } for j := 0; j < len(lvs); j++ { nk, _ := newNodeKey(lvs[j].Key) - if ndm.hdrLeafDLs.Contains(nk) { + if ssm.hdrLeafDLs.Contains(nk) { continue } - if ndm.hdrLeafCache.contains(nk) { + if ssm.hdrLeafCache.contains(nk) { continue } exists := true - _, err = ndm.database.GetCommittedBlockHeaderByHash(txn, utils.CopySlice(lvs[j].Value)) + _, err = ssm.database.GetCommittedBlockHeaderByHash(txn, utils.CopySlice(lvs[j].Value)) if err != nil { if err != badger.ErrKeyNotFound { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } exists = false @@ -770,8 +772,8 @@ func (ndm *SnapShotManager) syncHdrNodes(txn *badger.Txn, snapShotHeight uint32) if exists { continue } - if err := ndm.database.SetPendingHdrLeafKey(txn, utils.CopySlice(lvs[j].Key), utils.CopySlice(lvs[j].Value)); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.database.SetPendingHdrLeafKey(txn, utils.CopySlice(lvs[j].Key), utils.CopySlice(lvs[j].Value)); err != nil { + utils.DebugTrace(ssm.logger, err) return err } } @@ -779,14 +781,14 @@ func (ndm *SnapShotManager) syncHdrNodes(txn *badger.Txn, snapShotHeight uint32) return nil } -func (ndm *SnapShotManager) findTailSyncHeight(txn *badger.Txn, thisHeight int, lastHeight int) (*objs.BlockHeader, error) { +func (ssm *SnapShotManager) findTailSyncHeight(txn *badger.Txn, thisHeight int, lastHeight int) (*objs.BlockHeader, error) { var lastKnown *objs.BlockHeader for i := thisHeight; lastHeight < i; i-- { if i <= 2 { break } if lastKnown == nil && i%int(constants.EpochLength) == 0 { - bh, err := ndm.database.GetCommittedBlockHeader(txn, uint32(i)) + bh, err := ssm.database.GetCommittedBlockHeader(txn, uint32(i)) if err != nil { if err != badger.ErrKeyNotFound { return nil, err @@ -796,23 +798,23 @@ func (ndm *SnapShotManager) findTailSyncHeight(txn *badger.Txn, thisHeight int, lastKnown = bh } if lastKnown == nil { - ssbh, err := ndm.database.GetSnapshotBlockHeader(txn, uint32(i)) + ssbh, err := ssm.database.GetSnapshotBlockHeader(txn, uint32(i)) if err != nil { return nil, err } if ssbh != nil { - if err := ndm.database.SetCommittedBlockHeaderFastSync(txn, ssbh); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.database.SetCommittedBlockHeaderFastSync(txn, ssbh); err != nil { + utils.DebugTrace(ssm.logger, err) return nil, err } lastKnown = ssbh } } } - bh, err := ndm.database.GetCommittedBlockHeader(txn, uint32(i)) + bh, err := ssm.database.GetCommittedBlockHeader(txn, uint32(i)) if err != nil { if err != badger.ErrKeyNotFound { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return nil, err } continue @@ -825,54 +827,54 @@ func (ndm *SnapShotManager) findTailSyncHeight(txn *badger.Txn, thisHeight int, return lastKnown, nil } -func (ndm *SnapShotManager) syncTailingBlockHeaders(txn *badger.Txn, snapShotHeight uint32) error { +func (ssm *SnapShotManager) syncTailingBlockHeaders(txn *badger.Txn, snapShotHeight uint32) error { count := 0 { - if ndm.tailSyncHeight == 2 { + if ssm.tailSyncHeight == 2 { return nil } - lastKnown, err := ndm.findTailSyncHeight(txn, int(ndm.tailSyncHeight), 1) + lastKnown, err := ssm.findTailSyncHeight(txn, int(ssm.tailSyncHeight), 1) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } - if lastKnown.BClaims.Height != ndm.tailSyncHeight { - ndm.tailSyncHeight = lastKnown.BClaims.Height - key := ndm.database.MakeHeaderTrieKeyFromHeight(lastKnown.BClaims.Height - 1) + if lastKnown.BClaims.Height != ssm.tailSyncHeight { + ssm.tailSyncHeight = lastKnown.BClaims.Height + key := ssm.database.MakeHeaderTrieKeyFromHeight(lastKnown.BClaims.Height - 1) exists := true - _, err = ndm.database.GetPendingHdrLeafKey(txn, utils.CopySlice(key)) + _, err = ssm.database.GetPendingHdrLeafKey(txn, utils.CopySlice(key)) if err != nil { if err != badger.ErrKeyNotFound { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } exists = false } nk, err := newNodeKey(utils.CopySlice(key)) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } - if ndm.hdrLeafDLs.Contains(nk) { + if ssm.hdrLeafDLs.Contains(nk) { exists = true } - if ndm.hdrLeafCache.contains(nk) { + if ssm.hdrLeafCache.contains(nk) { exists = true } if !exists { value := utils.CopySlice(lastKnown.BClaims.PrevBlock) - if err := ndm.database.SetPendingHdrLeafKey(txn, utils.CopySlice(key), utils.CopySlice(value)); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.database.SetPendingHdrLeafKey(txn, utils.CopySlice(key), utils.CopySlice(value)); err != nil { + utils.DebugTrace(ssm.logger, err) return err } count++ } } } - if ndm.tailSyncHeight <= constants.EpochLength { + if ssm.tailSyncHeight <= constants.EpochLength { return nil } - start := int(ndm.tailSyncHeight) - int(constants.EpochLength)%int(constants.EpochLength) + start := int(ssm.tailSyncHeight) - int(constants.EpochLength)%int(constants.EpochLength) start = int(start) * int(constants.EpochLength) if start <= int(constants.EpochLength) { return nil @@ -882,10 +884,10 @@ func (ndm *SnapShotManager) syncTailingBlockHeaders(txn *badger.Txn, snapShotHei return nil } count++ - known, err := ndm.findTailSyncHeight(txn, i, i-int(constants.EpochLength)) + known, err := ssm.findTailSyncHeight(txn, i, i-int(constants.EpochLength)) if err != nil { if err != badger.ErrKeyNotFound { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } continue @@ -893,39 +895,39 @@ func (ndm *SnapShotManager) syncTailingBlockHeaders(txn *badger.Txn, snapShotHei if known.BClaims.Height == 1 { return nil } - key := ndm.database.MakeHeaderTrieKeyFromHeight(known.BClaims.Height - 1) + key := ssm.database.MakeHeaderTrieKeyFromHeight(known.BClaims.Height - 1) nk, err := newNodeKey(utils.CopySlice(key)) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } - if ndm.hdrLeafDLs.Contains(nk) { + if ssm.hdrLeafDLs.Contains(nk) { continue } - if ndm.hdrLeafCache.contains(nk) { + if ssm.hdrLeafCache.contains(nk) { continue } - _, err = ndm.database.GetPendingHdrLeafKey(txn, utils.CopySlice(key)) + _, err = ssm.database.GetPendingHdrLeafKey(txn, utils.CopySlice(key)) if err != nil { if err != badger.ErrKeyNotFound { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } continue } value := utils.CopySlice(known.BClaims.PrevBlock) - if err := ndm.database.SetPendingHdrLeafKey(txn, utils.CopySlice(key), utils.CopySlice(value)); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.database.SetPendingHdrLeafKey(txn, utils.CopySlice(key), utils.CopySlice(value)); err != nil { + utils.DebugTrace(ssm.logger, err) return err } } return nil } -func (ndm *SnapShotManager) dlHdrNodes(txn *badger.Txn, snapShotHeight uint32) error { +func (ssm *SnapShotManager) dlHdrNodes(txn *badger.Txn, snapShotHeight uint32) error { count := 0 // iterate the pending keys in database and start downloads for each pending until the limit is reached - iter := ndm.database.GetPendingHdrNodeKeysIter(txn) + iter := ssm.database.GetPendingHdrNodeKeysIter(txn) defer iter.Close() for { if count >= maxNumber { @@ -933,7 +935,7 @@ func (ndm *SnapShotManager) dlHdrNodes(txn *badger.Txn, snapShotHeight uint32) e } dlroot, dllayer, isDone, err := iter.Next() if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } if isDone { @@ -941,25 +943,25 @@ func (ndm *SnapShotManager) dlHdrNodes(txn *badger.Txn, snapShotHeight uint32) e } nk, err := newNodeKey(utils.CopySlice(dlroot)) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } - ok, err := ndm.database.ContainsSnapShotHdrNode(txn, utils.CopySlice(dlroot)) + ok, err := ssm.database.ContainsSnapShotHdrNode(txn, utils.CopySlice(dlroot)) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } if ok { - if err := ndm.database.DeletePendingHdrNodeKey(txn, dlroot); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.database.DeletePendingHdrNodeKey(txn, dlroot); err != nil { + utils.DebugTrace(ssm.logger, err) return nil } continue } - if ndm.hdrNodeDLs.Contains(nk) { + if ssm.hdrNodeDLs.Contains(nk) { continue } - if ndm.hdrNodeCache.contains(nk) { + if ssm.hdrNodeCache.contains(nk) { continue } r := &dlReq{ @@ -967,48 +969,48 @@ func (ndm *SnapShotManager) dlHdrNodes(txn *badger.Txn, snapShotHeight uint32) e key: utils.CopySlice(dlroot), layer: dllayer, } - ndm.hdrNodeDLs.Push(nk) + ssm.hdrNodeDLs.Push(nk) select { - case ndm.hdrNodeDlChan <- r: + case ssm.hdrNodeDlChan <- r: count++ default: - ndm.hdrNodeDLs.Pop(nk) + ssm.hdrNodeDLs.Pop(nk) return nil } } } -func (ndm *SnapShotManager) syncStateNodes(txn *badger.Txn, snapShotHeight uint32) error { +func (ssm *SnapShotManager) syncStateNodes(txn *badger.Txn, snapShotHeight uint32) error { // get keys from node cache and use those keys to store elements from the // node cache into the database as well as to get the leaf keys and store // those into the database as well - nodeKeys := ndm.stateNodeCache.getNodeKeys(snapShotHeight, maxNumber) + nodeKeys := ssm.stateNodeCache.getNodeKeys(snapShotHeight, maxNumber) //for each key for i := 0; i < len(nodeKeys); i++ { - resp, err := ndm.stateNodeCache.pop(snapShotHeight, utils.CopySlice(nodeKeys[i].key[:])) + resp, err := ssm.stateNodeCache.pop(snapShotHeight, utils.CopySlice(nodeKeys[i].key[:])) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) continue } // remove the keys from the pending set in the database - err = ndm.database.DeletePendingNodeKey(txn, utils.CopySlice(nodeKeys[i].key[:])) + err = ssm.database.DeletePendingNodeKey(txn, utils.CopySlice(nodeKeys[i].key[:])) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } // store all of those nodes into the database and get new pending keys - pendingBatch, newLayer, lvs, err := ndm.appHandler.StoreSnapShotNode(txn, utils.CopySlice(resp.batch), utils.CopySlice(resp.root), resp.layer) + pendingBatch, newLayer, lvs, err := ssm.appHandler.StoreSnapShotNode(txn, utils.CopySlice(resp.batch), utils.CopySlice(resp.root), resp.layer) if err != nil { // should not return if err invalid - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) continue } // store pending leaves ( blockheader height/hashes) for j := 0; j < len(lvs); j++ { exists := true - if _, err := ndm.appHandler.GetSnapShotStateData(txn, utils.CopySlice(lvs[j].Key)); err != nil { + if _, err := ssm.appHandler.GetSnapShotStateData(txn, utils.CopySlice(lvs[j].Key)); err != nil { if err != badger.ErrKeyNotFound { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } exists = false @@ -1016,15 +1018,15 @@ func (ndm *SnapShotManager) syncStateNodes(txn *badger.Txn, snapShotHeight uint3 if exists { continue } - if err := ndm.database.SetPendingLeafKey(txn, utils.CopySlice(lvs[j].Key), utils.CopySlice(lvs[j].Value)); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.database.SetPendingLeafKey(txn, utils.CopySlice(lvs[j].Key), utils.CopySlice(lvs[j].Value)); err != nil { + utils.DebugTrace(ssm.logger, err) return err } } // store new pending keys to db for j := 0; j < len(pendingBatch); j++ { - if err := ndm.database.SetPendingNodeKey(txn, utils.CopySlice(pendingBatch[j]), newLayer); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.database.SetPendingNodeKey(txn, utils.CopySlice(pendingBatch[j]), newLayer); err != nil { + utils.DebugTrace(ssm.logger, err) return err } } @@ -1032,14 +1034,14 @@ func (ndm *SnapShotManager) syncStateNodes(txn *badger.Txn, snapShotHeight uint3 return nil } -func (ndm *SnapShotManager) dlStateNodes(txn *badger.Txn, snapShotHeight uint32) error { +func (ssm *SnapShotManager) dlStateNodes(txn *badger.Txn, snapShotHeight uint32) error { // iterate the pending keys in database and start downloads for each pending until the limit is reached - iter := ndm.database.GetPendingNodeKeysIter(txn) + iter := ssm.database.GetPendingNodeKeysIter(txn) defer iter.Close() for { dlroot, dllayer, isDone, err := iter.Next() if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } if isDone { @@ -1047,13 +1049,13 @@ func (ndm *SnapShotManager) dlStateNodes(txn *badger.Txn, snapShotHeight uint32) } nk, err := newNodeKey(dlroot) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } - if ndm.stateNodeDLs.Contains(nk) { + if ssm.stateNodeDLs.Contains(nk) { continue } - if ndm.stateNodeCache.contains(snapShotHeight, nk) { + if ssm.stateNodeCache.contains(snapShotHeight, nk) { continue } r := &dlReq{ @@ -1061,51 +1063,51 @@ func (ndm *SnapShotManager) dlStateNodes(txn *badger.Txn, snapShotHeight uint32) layer: dllayer, key: dlroot, } - ndm.stateNodeDLs.Push(nk) + ssm.stateNodeDLs.Push(nk) select { - case ndm.stateNodeDlChan <- r: + case ssm.stateNodeDlChan <- r: default: - ndm.stateNodeDLs.Pop(nk) + ssm.stateNodeDLs.Pop(nk) return nil } } } -func (ndm *SnapShotManager) syncStateLeaves(txn *badger.Txn, snapShotHeight uint32) error { +func (ssm *SnapShotManager) syncStateLeaves(txn *badger.Txn, snapShotHeight uint32) error { // get keys from state cache use those to store the state data into the db - leafKeys := ndm.stateLeafCache.getLeafKeys(snapShotHeight, maxNumber) + leafKeys := ssm.stateLeafCache.getLeafKeys(snapShotHeight, maxNumber) // loop through LeafNode keys and retrieve from stateCache; // store data before deleting from database. for i := 0; i < len(leafKeys); i++ { - resp, err := ndm.stateLeafCache.pop(snapShotHeight, utils.CopySlice(leafKeys[i].key[:])) + resp, err := ssm.stateLeafCache.pop(snapShotHeight, utils.CopySlice(leafKeys[i].key[:])) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) continue } // remove the keys from the pending set in the database - err = ndm.database.DeletePendingLeafKey(txn, utils.CopySlice(leafKeys[i].key[:])) + err = ssm.database.DeletePendingLeafKey(txn, utils.CopySlice(leafKeys[i].key[:])) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) } // store key-value state data - err = ndm.appHandler.StoreSnapShotStateData(txn, utils.CopySlice(resp.key), utils.CopySlice(resp.value), utils.CopySlice(resp.data)) + err = ssm.appHandler.StoreSnapShotStateData(txn, utils.CopySlice(resp.key), utils.CopySlice(resp.value), utils.CopySlice(resp.data)) if err != nil { // should not return if err invalid - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) continue } } return nil } -func (ndm *SnapShotManager) dlStateLeaves(txn *badger.Txn, snapShotHeight uint32) error { +func (ssm *SnapShotManager) dlStateLeaves(txn *badger.Txn, snapShotHeight uint32) error { // iterate the pending keys in database and start downloads for each pending until the limit is reached - iter := ndm.database.GetPendingLeafKeysIter(txn) + iter := ssm.database.GetPendingLeafKeysIter(txn) defer iter.Close() for { dlkey, dlvalue, isDone, err := iter.Next() if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } if isDone { @@ -1113,13 +1115,13 @@ func (ndm *SnapShotManager) dlStateLeaves(txn *badger.Txn, snapShotHeight uint32 } nk, err := newNodeKey(dlkey) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } - if ndm.stateLeafDLs.Contains(nk) { + if ssm.stateLeafDLs.Contains(nk) { continue } - if ndm.stateLeafCache.contains(snapShotHeight, nk) { + if ssm.stateLeafCache.contains(snapShotHeight, nk) { continue } r := &dlReq{ @@ -1127,55 +1129,55 @@ func (ndm *SnapShotManager) dlStateLeaves(txn *badger.Txn, snapShotHeight uint32 key: dlkey, value: dlvalue, } - ndm.stateLeafDLs.Push(nk) + ssm.stateLeafDLs.Push(nk) select { - case ndm.stateLeafDlChan <- r: + case ssm.stateLeafDlChan <- r: default: - ndm.stateLeafDLs.Pop(nk) + ssm.stateLeafDLs.Pop(nk) return nil } } } -func (ndm *SnapShotManager) syncHdrLeaves(txn *badger.Txn, snapShotHeight uint32) error { +func (ssm *SnapShotManager) syncHdrLeaves(txn *badger.Txn, snapShotHeight uint32) error { // get keys from state cache use those to store the state data into the db - leafKeys := ndm.hdrLeafCache.getLeafKeys(maxNumber) + leafKeys := ssm.hdrLeafCache.getLeafKeys(maxNumber) // loop through LeafNode keys and retrieve from stateCache; // store data before deleting from database. for i := 0; i < len(leafKeys); i++ { - resp, err := ndm.hdrLeafCache.pop(leafKeys[i].key[:]) + resp, err := ssm.hdrLeafCache.pop(leafKeys[i].key[:]) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) continue } - err = ndm.database.DeletePendingHdrLeafKey(txn, leafKeys[i].key[:]) + err = ssm.database.DeletePendingHdrLeafKey(txn, leafKeys[i].key[:]) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } bh := &objs.BlockHeader{} err = bh.UnmarshalBinary(resp.data) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } - err = ndm.database.SetCommittedBlockHeaderFastSync(txn, bh) + err = ssm.database.SetCommittedBlockHeaderFastSync(txn, bh) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } } return nil } -func (ndm *SnapShotManager) dlHdrLeaves(txn *badger.Txn, snapShotHeight uint32) error { +func (ssm *SnapShotManager) dlHdrLeaves(txn *badger.Txn, snapShotHeight uint32) error { // iterate the pending keys in database and start downloads for each pending until the limit is reached - iter := ndm.database.GetPendingHdrLeafKeysIter(txn) + iter := ssm.database.GetPendingHdrLeafKeysIter(txn) defer iter.Close() for { dlkey, dlvalue, isDone, err := iter.Next() if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } if isDone { @@ -1183,13 +1185,13 @@ func (ndm *SnapShotManager) dlHdrLeaves(txn *badger.Txn, snapShotHeight uint32) } nk, err := newNodeKey(dlkey) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return err } - if ndm.hdrLeafDLs.Contains(nk) { + if ssm.hdrLeafDLs.Contains(nk) { continue } - if ndm.hdrLeafCache.contains(nk) { + if ssm.hdrLeafCache.contains(nk) { continue } r := &dlReq{ @@ -1197,54 +1199,54 @@ func (ndm *SnapShotManager) dlHdrLeaves(txn *badger.Txn, snapShotHeight uint32) key: dlkey, value: dlvalue, } - ndm.hdrLeafDLs.Push(nk) + ssm.hdrLeafDLs.Push(nk) select { - case ndm.hdrLeafDlChan <- r: + case ssm.hdrLeafDlChan <- r: default: - ndm.hdrLeafDLs.Pop(nk) + ssm.hdrLeafDLs.Pop(nk) break } } return nil } -func (ndm *SnapShotManager) worker(killChan <-chan struct{}) { +func (ssm *SnapShotManager) worker(killChan <-chan struct{}) { for { select { case <-killChan: return - case <-ndm.closeChan: + case <-ssm.closeChan: return - case w := <-ndm.workChan: + case w := <-ssm.workChan: w() } } } -func (ndm *SnapShotManager) sendWork(w func()) { +func (ssm *SnapShotManager) sendWork(w func()) { select { - case <-ndm.closeChan: + case <-ssm.closeChan: return - case ndm.workChan <- w: + case ssm.workChan <- w: return } } -func (ndm *SnapShotManager) downloadWithRetryStateNodeWorker() { +func (ssm *SnapShotManager) downloadWithRetryStateNodeWorker() { for { select { - case <-ndm.closeChan: + case <-ssm.closeChan: return - case dl := <-ndm.stateNodeDlChan: - ndm.sendWork(ndm.downloadWithRetryStateNodeClosure(dl)) + case dl := <-ssm.stateNodeDlChan: + ssm.sendWork(ssm.downloadWithRetryStateNodeClosure(dl)) } } } -func (ndm *SnapShotManager) downloadWithRetryHdrLeafWorker() { +func (ssm *SnapShotManager) downloadWithRetryHdrLeafWorker() { for { select { - case <-ndm.closeChan: + case <-ssm.closeChan: return default: var stop <-chan time.Time @@ -1252,11 +1254,11 @@ func (ndm *SnapShotManager) downloadWithRetryHdrLeafWorker() { func() { for { select { - case <-ndm.closeChan: + case <-ssm.closeChan: return case <-stop: return - case dl := <-ndm.hdrLeafDlChan: + case dl := <-ssm.hdrLeafDlChan: if stop == nil { stop = time.After(50 * time.Millisecond) } @@ -1267,50 +1269,50 @@ func (ndm *SnapShotManager) downloadWithRetryHdrLeafWorker() { } } }() - ndm.sendWork(ndm.downloadWithRetryHdrLeafClosure(cache)) + ssm.sendWork(ssm.downloadWithRetryHdrLeafClosure(cache)) } } } -func (ndm *SnapShotManager) downloadWithRetryHdrNodeWorker() { +func (ssm *SnapShotManager) downloadWithRetryHdrNodeWorker() { for { select { - case <-ndm.closeChan: + case <-ssm.closeChan: return - case dl := <-ndm.hdrNodeDlChan: - ndm.sendWork(ndm.downloadWithRetryHdrNodeClosure(dl)) + case dl := <-ssm.hdrNodeDlChan: + ssm.sendWork(ssm.downloadWithRetryHdrNodeClosure(dl)) } } } -func (ndm *SnapShotManager) downloadWithRetryStateLeafWorker() { +func (ssm *SnapShotManager) downloadWithRetryStateLeafWorker() { for { select { - case <-ndm.closeChan: + case <-ssm.closeChan: return - case dl := <-ndm.stateLeafDlChan: - ndm.sendWork(ndm.downloadWithRetryStateLeafClosure(dl)) + case dl := <-ssm.stateLeafDlChan: + ssm.sendWork(ssm.downloadWithRetryStateLeafClosure(dl)) } } } -func (ndm *SnapShotManager) downloadWithRetryStateNodeClosure(dl *dlReq) workFunc { +func (ssm *SnapShotManager) downloadWithRetryStateNodeClosure(dl *dlReq) workFunc { snapShotHeight := dl.snapShotHeight root := dl.key layer := dl.layer nk, _ := newNodeKey(root) return func() { - defer ndm.stateNodeDLs.Pop(nk) - if snapShotHeight < ndm.snapShotHeight.Get() { + defer ssm.stateNodeDLs.Pop(nk) + if snapShotHeight < ssm.snapShotHeight.Get() { return } opts := []grpc.CallOption{ grpc_retry.WithBackoff(grpc_retry.BackoffExponentialWithJitter(backOffAmount*time.Millisecond, backOffJitter)), grpc_retry.WithMax(maxRetryCount), } - resp, err := ndm.requestBus.RequestP2PGetSnapShotNode(context.Background(), snapShotHeight, root, opts...) + resp, err := ssm.requestBus.RequestP2PGetSnapShotNode(context.Background(), snapShotHeight, root, opts...) if err != nil { - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return } if len(resp) == 0 { @@ -1323,11 +1325,11 @@ func (ndm *SnapShotManager) downloadWithRetryStateNodeClosure(dl *dlReq) workFun batch: resp, } // store to the cache - ndm.stateNodeCache.insert(snapShotHeight, nr) + ssm.stateNodeCache.insert(snapShotHeight, nr) } } -func (ndm *SnapShotManager) downloadWithRetryHdrLeafClosure(dl []*dlReq) workFunc { +func (ssm *SnapShotManager) downloadWithRetryHdrLeafClosure(dl []*dlReq) workFunc { return func() { heightList := make([]uint32, len(dl)) hashMap := make(map[uint32][]byte) @@ -1340,7 +1342,7 @@ func (ndm *SnapShotManager) downloadWithRetryHdrLeafClosure(dl []*dlReq) workFun keyMap[blockHeight] = nk heightList[i] = blockHeight hashMap[blockHeight] = utils.CopySlice(value) - defer ndm.hdrLeafDLs.Pop(nk) + defer ssm.hdrLeafDLs.Pop(nk) } opts := []grpc.CallOption{ grpc_retry.WithBackoff(grpc_retry.BackoffExponentialWithJitter(backOffAmount*time.Millisecond, backOffJitter)), @@ -1348,7 +1350,7 @@ func (ndm *SnapShotManager) downloadWithRetryHdrLeafClosure(dl []*dlReq) workFun } peerOpt := middleware.NewPeerInterceptor() newOpts := append(opts, peerOpt) - resp, err := ndm.requestBus.RequestP2PGetBlockHeaders(context.Background(), heightList, newOpts...) + resp, err := ssm.requestBus.RequestP2PGetBlockHeaders(context.Background(), heightList, newOpts...) if err != nil { return } @@ -1367,18 +1369,18 @@ func (ndm *SnapShotManager) downloadWithRetryHdrLeafClosure(dl []*dlReq) workFun bhashResp, err := resp[i].BlockHash() if err != nil { peer.Feedback(-2) - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) continue } if !bytes.Equal(bhash, bhashResp) { peer.Feedback(-2) - utils.DebugTrace(ndm.logger, errors.New("Bad block hash")) + utils.DebugTrace(ssm.logger, errors.New("Bad block hash")) continue } bhBytes, err := resp[i].MarshalBinary() if err != nil { peer.Feedback(-2) - utils.DebugTrace(ndm.logger, err) + utils.DebugTrace(ssm.logger, err) return } nk := keyMap[height] @@ -1390,19 +1392,19 @@ func (ndm *SnapShotManager) downloadWithRetryHdrLeafClosure(dl []*dlReq) workFun } // store to the cache peer.Feedback(1) - ndm.hdrLeafCache.insert(sr) + ssm.hdrLeafCache.insert(sr) } } } -func (ndm *SnapShotManager) downloadWithRetryStateLeafClosure(dl *dlReq) workFunc { +func (ssm *SnapShotManager) downloadWithRetryStateLeafClosure(dl *dlReq) workFunc { snapShotHeight := dl.snapShotHeight key := dl.key value := dl.value nk, _ := newNodeKey(key) return func() { - defer ndm.stateLeafDLs.Pop(nk) - if snapShotHeight < ndm.snapShotHeight.Get() { + defer ssm.stateLeafDLs.Pop(nk) + if snapShotHeight < ssm.snapShotHeight.Get() { return } opts := []grpc.CallOption{ @@ -1411,7 +1413,7 @@ func (ndm *SnapShotManager) downloadWithRetryStateLeafClosure(dl *dlReq) workFun } peerOpt := middleware.NewPeerInterceptor() newOpts := append(opts, peerOpt) - resp, err := ndm.requestBus.RequestP2PGetSnapShotStateData(context.Background(), key, newOpts...) + resp, err := ssm.requestBus.RequestP2PGetSnapShotStateData(context.Background(), key, newOpts...) if err != nil { return @@ -1428,11 +1430,11 @@ func (ndm *SnapShotManager) downloadWithRetryStateLeafClosure(dl *dlReq) workFun data: utils.CopySlice(resp), } // store to the cache - ndm.stateLeafCache.insert(snapShotHeight, sr) + ssm.stateLeafCache.insert(snapShotHeight, sr) } } -func (ndm *SnapShotManager) downloadWithRetryHdrNodeClosure(dl *dlReq) workFunc { +func (ssm *SnapShotManager) downloadWithRetryHdrNodeClosure(dl *dlReq) workFunc { snapShotHeight := dl.snapShotHeight root := dl.key layer := dl.layer @@ -1444,8 +1446,8 @@ func (ndm *SnapShotManager) downloadWithRetryHdrNodeClosure(dl *dlReq) workFunc } peerOpt := middleware.NewPeerInterceptor() newOpts := append(opts, peerOpt) - defer ndm.hdrNodeDLs.Pop(nk) - resp, err := ndm.requestBus.RequestP2PGetSnapShotHdrNode(context.Background(), root, newOpts...) + defer ssm.hdrNodeDLs.Pop(nk) + resp, err := ssm.requestBus.RequestP2PGetSnapShotHdrNode(context.Background(), root, newOpts...) if err != nil { return } @@ -1461,8 +1463,8 @@ func (ndm *SnapShotManager) downloadWithRetryHdrNodeClosure(dl *dlReq) workFunc batch: resp, } // store to the cache - if err := ndm.hdrNodeCache.insert(nr); err != nil { - utils.DebugTrace(ndm.logger, err) + if err := ssm.hdrNodeCache.insert(nr); err != nil { + utils.DebugTrace(ssm.logger, err) } } } diff --git a/consensus/lstate/handlers.go b/consensus/lstate/handlers.go index 6dfc0d90..f378ae4b 100644 --- a/consensus/lstate/handlers.go +++ b/consensus/lstate/handlers.go @@ -4,18 +4,16 @@ import ( "bytes" "fmt" - "github.com/MadBase/MadNet/constants" - "github.com/MadBase/MadNet/errorz" - "github.com/MadBase/MadNet/utils" - "github.com/sirupsen/logrus" - "github.com/MadBase/MadNet/consensus/db" "github.com/MadBase/MadNet/consensus/dman" "github.com/MadBase/MadNet/consensus/objs" + "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" + "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/logging" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" "github.com/dgraph-io/badger/v2" + "github.com/sirupsen/logrus" ) type Handlers struct { @@ -347,7 +345,7 @@ func (mb *Handlers) PreValidate(v interface{}) error { if err != nil { return err } - GroupKey = gUtils.CopySlice(vSet.GroupKey) + GroupKey = utils.CopySlice(vSet.GroupKey) } vSet, err := mb.database.GetValidatorSet(txn, height) if err != nil { @@ -391,7 +389,7 @@ func (mb *Handlers) PreValidate(v interface{}) error { } } for _, cs := range CoSigners { - if !vSet.IsValidTuple(gUtils.CopySlice(cs), GroupKey) { + if !vSet.IsValidTuple(utils.CopySlice(cs), GroupKey) { return errorz.ErrInvalid{}.New("bad co signer") } } diff --git a/consensus/lstate/rstate.go b/consensus/lstate/rstate.go index 406a4071..adc2deb0 100644 --- a/consensus/lstate/rstate.go +++ b/consensus/lstate/rstate.go @@ -4,11 +4,10 @@ import ( "bytes" "errors" - "github.com/MadBase/MadNet/errorz" - "github.com/MadBase/MadNet/consensus/objs" "github.com/MadBase/MadNet/crypto" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/errorz" + "github.com/MadBase/MadNet/utils" ) type RoundStates struct { @@ -287,7 +286,7 @@ func (r *RoundStates) RCert() *objs.RCert { } func (r *RoundStates) PrevBlock() []byte { - prevBlock := gUtils.CopySlice(r.OwnRoundState().RCert.RClaims.PrevBlock) + prevBlock := utils.CopySlice(r.OwnRoundState().RCert.RClaims.PrevBlock) return prevBlock } diff --git a/consensus/lstate/statecastlocal.go b/consensus/lstate/statecastlocal.go index bc465990..360bd9fa 100644 --- a/consensus/lstate/statecastlocal.go +++ b/consensus/lstate/statecastlocal.go @@ -2,6 +2,7 @@ package lstate import ( "github.com/MadBase/MadNet/consensus/objs" + "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" "github.com/MadBase/MadNet/utils" "github.com/dgraph-io/badger/v2" @@ -21,7 +22,7 @@ func (ce *Engine) castNewProposalValue(txn *badger.Txn, rs *RoundStates) error { return err } if stateRoot == nil { - stateRoot = make([]byte, 32) + stateRoot = make([]byte, constants.HashLen) } p := &objs.Proposal{ PClaims: &objs.PClaims{ diff --git a/consensus/lstate/statechnglocal.go b/consensus/lstate/statechnglocal.go index d0d43885..d62d1dab 100644 --- a/consensus/lstate/statechnglocal.go +++ b/consensus/lstate/statechnglocal.go @@ -4,14 +4,12 @@ import ( "bytes" "errors" + "github.com/MadBase/MadNet/consensus/objs" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/interfaces" "github.com/MadBase/MadNet/utils" - - "github.com/MadBase/MadNet/consensus/objs" - gUtils "github.com/MadBase/MadNet/utils" "github.com/dgraph-io/badger/v2" ) @@ -75,13 +73,13 @@ func (ce *Engine) doPendingPreVoteStep(txn *badger.Txn, rs *RoundStates) error { return err } bclaims := rs.OwnState.SyncToBH.BClaims - PrevBlock := gUtils.CopySlice(rcert.RClaims.PrevBlock) + PrevBlock := utils.CopySlice(rcert.RClaims.PrevBlock) headerRoot, err := ce.database.GetHeaderTrieRoot(txn, rs.OwnState.SyncToBH.BClaims.Height) if err != nil { utils.DebugTrace(ce.logger, err) return err } - StateRoot := gUtils.CopySlice(bclaims.StateRoot) + StateRoot := utils.CopySlice(bclaims.StateRoot) p := &objs.Proposal{ PClaims: &objs.PClaims{ BClaims: &objs.BClaims{ @@ -432,7 +430,8 @@ func (ce *Engine) doPendingNext(txn *badger.Txn, rs *RoundStates) error { // cast a next round if rcert.RClaims.Round != constants.DEADBLOCKROUND { if rcert.RClaims.Round == constants.DEADBLOCKROUNDNR { - if rs.OwnValidatingState.DBRNRExpired() { + dbrnrTO := ce.storage.GetDeadBlockRoundNextRoundTimeout() + if rs.OwnValidatingState.DBRNRExpired(dbrnrTO) { // Wait a long time before moving into Dead Block Round if len(pcl)+len(pcnl) >= rs.GetCurrentThreshold() { if err := ce.castNextRound(txn, rs); err != nil { @@ -557,6 +556,8 @@ func (ce *Engine) doRoundJump(txn *badger.Txn, rs *RoundStates, rc *objs.RCert) return nil } +// doCheckValidValue is called by non-validating nodes to update ValidValue +// and perform other related tasks. func (ce *Engine) doCheckValidValue(txn *badger.Txn, rs *RoundStates) error { ce.logger.Debugf("doCheckValidValue: MAXBH:%v STBH:%v RH:%v RN:%v", rs.OwnState.MaxBHSeen.BClaims.Height, rs.OwnState.SyncToBH.BClaims.Height, rs.OwnRoundState().RCert.RClaims.Height, rs.OwnRoundState().RCert.RClaims.Round) @@ -714,6 +715,8 @@ func (ce *Engine) doHeightJumpStep(txn *badger.Txn, rs *RoundStates, rcert *objs rs.OwnValidatingState.LockedValue = nil return true, nil } + // TODO: handle case (else case) in which the ValidValue does not match + // with local ValidValue; may or may not be possible. } // could not use valid value - only option left is to check if the block // had no tx's in it - if so we can build it with no additional information @@ -728,13 +731,13 @@ func (ce *Engine) doHeightJumpStep(txn *badger.Txn, rs *RoundStates, rcert *objs return false, err } bclaims := rs.OwnState.SyncToBH.BClaims - PrevBlock := gUtils.CopySlice(ownRcert.RClaims.PrevBlock) + PrevBlock := utils.CopySlice(ownRcert.RClaims.PrevBlock) headerRoot, err := ce.database.GetHeaderTrieRoot(txn, rs.OwnState.SyncToBH.BClaims.Height) if err != nil { utils.DebugTrace(ce.logger, err) return false, err } - StateRoot := gUtils.CopySlice(bclaims.StateRoot) + StateRoot := utils.CopySlice(bclaims.StateRoot) bh := &objs.BlockHeader{ BClaims: &objs.BClaims{ PrevBlock: PrevBlock, @@ -745,7 +748,7 @@ func (ce *Engine) doHeightJumpStep(txn *badger.Txn, rs *RoundStates, rcert *objs Height: ownRcert.RClaims.Height, }, TxHshLst: txs, - SigGroup: gUtils.CopySlice(rcert.SigGroup), + SigGroup: utils.CopySlice(rcert.SigGroup), } bhash, err := bh.BlockHash() if err != nil { diff --git a/consensus/objs/bclaims_test.go b/consensus/objs/bclaims_test.go index 84408b11..a871d014 100644 --- a/consensus/objs/bclaims_test.go +++ b/consensus/objs/bclaims_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/MadBase/MadNet/crypto" + "github.com/MadBase/MadNet/utils" ) func generateChain(length int) ([]*BClaims, [][][]byte, error) { @@ -129,3 +130,124 @@ func bclaimsEqual(t *testing.T, bclaims, bclaims2 *BClaims) { t.Fatal("fail") } } + +func TestBClaimsMarshal(t *testing.T) { + bh := &BlockHeader{} + _, err := bh.BClaims.MarshalBinary() + if err == nil { + t.Fatal("Should have raised error (0)") + } + + bclaims := &BClaims{} + _, err = bclaims.MarshalBinary() + if err == nil { + t.Fatal("Should have raised error (1)") + } + + bclaims = &BClaims{ + ChainID: 1, + } + _, err = bclaims.MarshalBinary() + if err == nil { + t.Fatal("Should have raised error (2)") + } + + bclaims = &BClaims{ + ChainID: 1, + Height: 1, + TxCount: 0, + } + data, err := bclaims.MarshalBinary() + if err != nil { + t.Fatal(err) + } + bc2 := &BClaims{} + err = bc2.UnmarshalBinary(data) + if err == nil { + t.Fatal("Should have raised error (3)") + } + + bclaims = &BClaims{ + ChainID: 1, + Height: 1, + TxCount: 0, + PrevBlock: crypto.Hasher([]byte("Genesis")), + } + data, err = bclaims.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = bc2.UnmarshalBinary(data) + if err == nil { + t.Fatal("Should have raised error (4)") + } + + bclaims = &BClaims{ + ChainID: 1, + Height: 1, + TxCount: 0, + PrevBlock: crypto.Hasher([]byte("Genesis")), + TxRoot: crypto.Hasher([]byte("")), + } + data, err = bclaims.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = bc2.UnmarshalBinary(data) + if err == nil { + t.Fatal("Should have raised error (5)") + } + + bclaims = &BClaims{ + ChainID: 1, + Height: 1, + TxCount: 0, + PrevBlock: crypto.Hasher([]byte("Genesis")), + TxRoot: crypto.Hasher([]byte("")), + StateRoot: crypto.Hasher([]byte("")), + } + data, err = bclaims.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = bc2.UnmarshalBinary(data) + if err == nil { + t.Fatal("Should have raised error (6)") + } + + bclaims = &BClaims{ + ChainID: 223, + Height: 225, + TxCount: 0, + PrevBlock: crypto.Hasher([]byte("Genesis")), + TxRoot: crypto.Hasher([]byte("")), + StateRoot: crypto.Hasher([]byte("")), + HeaderRoot: crypto.Hasher([]byte("")), + } + data, err = bclaims.MarshalBinary() + if err != nil { + t.Fatal(err) + } + err = bc2.UnmarshalBinary(data) + if err != nil { + t.Fatal(err) + } + + // Take valid marshaled data and raise an error for invalid ChainID + dataBad1 := utils.CopySlice(data) + dataBad1[8] = 0 + bc3 := &BClaims{} + err = bc3.UnmarshalBinary(dataBad1) + if err == nil { + t.Fatal("Should have raised error (7)") + } + + // Take valid marshaled data and raise an error for invalid Height + dataBad2 := utils.CopySlice(data) + dataBad2[12] = 0 + bc4 := &BClaims{} + err = bc4.UnmarshalBinary(dataBad2) + if err == nil { + t.Fatal("Should have raised error (8)") + } +} diff --git a/consensus/objs/bclms.go b/consensus/objs/bclms.go index d4e2c1fc..8d0e4e9a 100644 --- a/consensus/objs/bclms.go +++ b/consensus/objs/bclms.go @@ -3,7 +3,6 @@ package objs import ( "github.com/MadBase/MadNet/consensus/objs/bclaims" mdefs "github.com/MadBase/MadNet/consensus/objs/capn" - "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" "github.com/MadBase/MadNet/errorz" capnp "zombiezen.com/go/capnproto2" @@ -58,9 +57,6 @@ func (b *BClaims) UnmarshalCapn(bc mdefs.BClaims) error { b.HeaderRoot = bc.HeaderRoot() b.StateRoot = bc.StateRoot() b.TxRoot = bc.TxRoot() - if len(b.PrevBlock) != constants.HashLen { - return errorz.ErrInvalid{}.New("capn bclaims prevblock bad len") - } if b.Height < 1 { return errorz.ErrInvalid{}.New("capn bclaims bad height") } diff --git a/consensus/objs/bhIndKey.go b/consensus/objs/bhIndKey.go index a287a5d5..3b64f8d0 100644 --- a/consensus/objs/bhIndKey.go +++ b/consensus/objs/bhIndKey.go @@ -3,7 +3,7 @@ package objs import ( "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) // BlockHeaderHashIndexKey ... @@ -21,8 +21,8 @@ func (b *BlockHeaderHashIndexKey) UnmarshalBinary(data []byte) error { if len(data) != (constants.HashLen + 2) { return errorz.ErrInvalid{}.New("Invalid BlockHeaderHashIndexKey") } - b.Prefix = gUtils.CopySlice(data[0:2]) - b.BlockHash = gUtils.CopySlice(data[2:]) + b.Prefix = utils.CopySlice(data[0:2]) + b.BlockHash = utils.CopySlice(data[2:]) return nil } @@ -39,8 +39,8 @@ func (b *BlockHeaderHashIndexKey) MarshalBinary() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("BlockHeaderHashIndexKey: invalid Prefix") } key := []byte{} - Prefix := gUtils.CopySlice(b.Prefix) - BlockHash := gUtils.CopySlice(b.BlockHash) + Prefix := utils.CopySlice(b.Prefix) + BlockHash := utils.CopySlice(b.BlockHash) key = append(key, Prefix...) key = append(key, BlockHash...) return key, nil diff --git a/consensus/objs/bhdr.go b/consensus/objs/bhdr.go index 2089c9bf..5e4683a6 100644 --- a/consensus/objs/bhdr.go +++ b/consensus/objs/bhdr.go @@ -3,13 +3,12 @@ package objs import ( "bytes" - "github.com/MadBase/MadNet/errorz" - "github.com/MadBase/MadNet/consensus/objs/blockheader" mdefs "github.com/MadBase/MadNet/consensus/objs/capn" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/errorz" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -35,29 +34,6 @@ func (b *BlockHeader) UnmarshalBinary(data []byte) error { // UnmarshalCapn unmarshals the capnproto definition of the object func (b *BlockHeader) UnmarshalCapn(bh mdefs.BlockHeader) error { - if err := b.unmarshalCapnUnsafe(bh); err != nil { - return err - } - if len(b.TxHshLst) != int(b.BClaims.TxCount) { - return errorz.ErrInvalid{}.New("cap bclaims txhsh lst len mismatch") - } - return nil -} - -// UnmarshalBinaryUnsafe unmarshals a BlockHeader in an unsafe manner; -// that is, we do not look at the TxHshLst. This is necessary during -// fast synchronization, as transactions are not synced. -func (b *BlockHeader) UnmarshalBinaryUnsafe(data []byte) error { - bh, err := blockheader.Unmarshal(data) - if err != nil { - return err - } - defer bh.Struct.Segment().Message().Reset(nil) - return b.unmarshalCapnUnsafe(bh) -} - -func (b *BlockHeader) unmarshalCapnUnsafe(bh mdefs.BlockHeader) error { - b.BClaims = &BClaims{} err := blockheader.Validate(bh) if err != nil { return err @@ -68,11 +44,19 @@ func (b *BlockHeader) unmarshalCapnUnsafe(bh mdefs.BlockHeader) error { return err } b.TxHshLst = lst + b.BClaims = &BClaims{} err = b.BClaims.UnmarshalCapn(bh.BClaims()) if err != nil { return err } - b.SigGroup = bh.SigGroup() + sigGroup := bh.SigGroup() + if len(sigGroup) != constants.CurveBN256EthSigLen { + return errorz.ErrInvalid{}.New("cap bclaims sigGroup incorrect length") + } + b.SigGroup = sigGroup + if len(b.TxHshLst) != int(b.BClaims.TxCount) { + return errorz.ErrInvalid{}.New("cap bclaims txhsh lst len mismatch") + } return nil } @@ -176,7 +160,7 @@ func (b *BlockHeader) GetRCert() (*RCert, error) { return nil, err } rc := &RCert{ - SigGroup: b.SigGroup, + SigGroup: utils.CopySlice(b.SigGroup), RClaims: &RClaims{ ChainID: b.BClaims.ChainID, Height: b.BClaims.Height + 1, @@ -193,8 +177,8 @@ func (b *BlockHeader) MakeDeadRoundProposal(rcert *RCert, headerRoot []byte) (*P return nil, errorz.ErrInvalid{}.New("not initialized") } txRoot, err := MakeTxRoot([][]byte{}) - StateRoot := gUtils.CopySlice(b.BClaims.StateRoot) - prevBlock := gUtils.CopySlice(rcert.RClaims.PrevBlock) + StateRoot := utils.CopySlice(b.BClaims.StateRoot) + prevBlock := utils.CopySlice(rcert.RClaims.PrevBlock) if err != nil { return nil, err } diff --git a/consensus/objs/bhdr_test.go b/consensus/objs/bhdr_test.go index 7e3e3b56..c3302e1c 100644 --- a/consensus/objs/bhdr_test.go +++ b/consensus/objs/bhdr_test.go @@ -18,7 +18,10 @@ func TestBlockHeader(t *testing.T) { t.Fatal(err) } gk := crypto.BNGroupSigner{} - gk.SetPrivk(crypto.Hasher([]byte("secret"))) + err = gk.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } sig, err := gk.Sign(bhsh) if err != nil { t.Fatal(err) @@ -82,7 +85,10 @@ func TestBlockHeaderBad(t *testing.T) { t.Fatal(err) } gk := crypto.BNGroupSigner{} - gk.SetPrivk(crypto.Hasher([]byte("secret"))) + err = gk.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } sig, err := gk.Sign(bhsh) if err != nil { t.Fatal(err) @@ -109,12 +115,7 @@ func TestBlockHeaderBad(t *testing.T) { t.Fatal(err) } bh2 := &BlockHeader{} - err = bh2.UnmarshalBinaryUnsafe(bhdrBytes) - if err != nil { - t.Fatal(err) - } - bh3 := &BlockHeader{} - err = bh3.UnmarshalBinary(bhdrBytes) + err = bh2.UnmarshalBinary(bhdrBytes) if err == nil { t.Fatal("Should have raised error (5)") } diff --git a/consensus/objs/capn/gen.go b/consensus/objs/capn/gen.go index 0bcd304a..df805f89 100644 --- a/consensus/objs/capn/gen.go +++ b/consensus/objs/capn/gen.go @@ -7,4 +7,4 @@ import ( // Import check to ensure capnp is installed. var _ = capnp.Tag -//go:generate capnp compile -I /home/et3p/go/src/zombiezen.com/go/capnproto2/std -ogo consensus.capnp +//go:generate capnp compile -I $GOPATH/src/zombiezen.com/go/capnproto2/std -ogo consensus.capnp diff --git a/consensus/objs/cbkey.go b/consensus/objs/cbkey.go index d5137e62..c8d9ebfd 100644 --- a/consensus/objs/cbkey.go +++ b/consensus/objs/cbkey.go @@ -2,7 +2,7 @@ package objs import ( "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) // BlockHeaderHeightKey ... @@ -20,8 +20,8 @@ func (b *BlockHeaderHeightKey) UnmarshalBinary(data []byte) error { if len(data) != 6 { return errorz.ErrInvalid{}.New("Invalid data for BlockHeaderHeightKey unmarshalling") } - b.Prefix = gUtils.CopySlice(data[0:2]) - nb, err := gUtils.UnmarshalUint32(data[2:6]) + b.Prefix = utils.CopySlice(data[0:2]) + nb, err := utils.UnmarshalUint32(data[2:6]) if err != nil { return err } @@ -39,8 +39,8 @@ func (b *BlockHeaderHeightKey) MarshalBinary() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } key := []byte{} - Prefix := gUtils.CopySlice(b.Prefix) - nb := gUtils.MarshalUint32(b.Height) + Prefix := utils.CopySlice(b.Prefix) + nb := utils.MarshalUint32(b.Height) key = append(key, Prefix...) key = append(key, nb...) return key, nil diff --git a/consensus/objs/estore.go b/consensus/objs/estore.go index 27d64fa8..dad7b05c 100644 --- a/consensus/objs/estore.go +++ b/consensus/objs/estore.go @@ -6,12 +6,11 @@ import ( "crypto/rand" "io" - "github.com/MadBase/MadNet/errorz" - mdefs "github.com/MadBase/MadNet/consensus/objs/capn" "github.com/MadBase/MadNet/consensus/objs/estore" + "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/interfaces" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -41,10 +40,10 @@ func (b *EncryptedStore) UnmarshalCapn(bh mdefs.EncryptedStore) error { if err != nil { return err } - b.Name = gUtils.CopySlice(bh.Name()) - b.cypherText = gUtils.CopySlice(bh.CypherText()) - b.nonce = gUtils.CopySlice(bh.Nonce()) - b.Kid = gUtils.CopySlice(bh.Kid()) + b.Name = utils.CopySlice(bh.Name()) + b.cypherText = utils.CopySlice(bh.CypherText()) + b.nonce = utils.CopySlice(bh.Nonce()) + b.Kid = utils.CopySlice(bh.Kid()) return nil } @@ -85,7 +84,7 @@ func (b *EncryptedStore) MarshalCapn(seg *capnp.Segment) (mdefs.EncryptedStore, } bh = tmp } - name := gUtils.CopySlice(b.Name) + name := utils.CopySlice(b.Name) err := bh.SetName(name) if err != nil { return mdefs.EncryptedStore{}, err diff --git a/consensus/objs/nh.go b/consensus/objs/nh.go index 551d3dd6..7e685ffb 100644 --- a/consensus/objs/nh.go +++ b/consensus/objs/nh.go @@ -3,12 +3,11 @@ package objs import ( "bytes" - "github.com/MadBase/MadNet/errorz" - mdefs "github.com/MadBase/MadNet/consensus/objs/capn" "github.com/MadBase/MadNet/consensus/objs/nextheight" "github.com/MadBase/MadNet/crypto" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/errorz" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -52,7 +51,7 @@ func (b *NextHeight) UnmarshalCapn(bh mdefs.NextHeight) error { if err != nil { return err } - b.Signature = gUtils.CopySlice(bh.Signature()) + b.Signature = utils.CopySlice(bh.Signature()) return nil } @@ -135,7 +134,7 @@ func (b *NextHeight) ValidateSignatures(secpVal *crypto.Secp256k1Validator, bnVa PreCommitCE := []byte{} PreCommitCE = append(PreCommitCE, PreCommitSigDesignator()...) PreCommitCE = append(PreCommitCE, canonicalEncoding...) - pubkey, err := secpVal.Validate(PreCommitCE, gUtils.CopySlice(sig)) + pubkey, err := secpVal.Validate(PreCommitCE, utils.CopySlice(sig)) if err != nil { return err } diff --git a/consensus/objs/nh_test.go b/consensus/objs/nh_test.go index f7670c9e..b763afde 100644 --- a/consensus/objs/nh_test.go +++ b/consensus/objs/nh_test.go @@ -19,7 +19,10 @@ func TestNextHeight(t *testing.T) { t.Fatal(err) } gk := &crypto.BNGroupSigner{} - gk.SetPrivk(crypto.Hasher([]byte("secret"))) + err = gk.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } sig, err := gk.Sign(bhsh) if err != nil { t.Fatal(err) diff --git a/consensus/objs/nhl.go b/consensus/objs/nhl.go index e982e896..a504e7b6 100644 --- a/consensus/objs/nhl.go +++ b/consensus/objs/nhl.go @@ -2,7 +2,7 @@ package objs import ( "github.com/MadBase/MadNet/crypto" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) // NextHeightList ... @@ -11,7 +11,7 @@ type NextHeightList []*NextHeight func (nhl NextHeightList) MakeBlockHeader(bns *crypto.BNGroupSigner, groupShares [][]byte) (*BlockHeader, *RCert, error) { sigs := [][]byte{} for _, nh := range nhl { - sigs = append(sigs, gUtils.CopySlice(nh.NHClaims.SigShare)) + sigs = append(sigs, utils.CopySlice(nh.NHClaims.SigShare)) } SigGroup, err := bns.Aggregate(sigs, groupShares) if err != nil { @@ -28,7 +28,7 @@ func (nhl NextHeightList) MakeBlockHeader(bns *crypto.BNGroupSigner, groupShares } txHshlst := [][]byte{} for _, hsh := range nhl[0].NHClaims.Proposal.TxHshLst { - chsh := gUtils.CopySlice(hsh) + chsh := utils.CopySlice(hsh) txHshlst = append(txHshlst, chsh) } @@ -41,8 +41,8 @@ func (nhl NextHeightList) MakeBlockHeader(bns *crypto.BNGroupSigner, groupShares if err != nil { return nil, nil, err } - PrevBlockCopy := gUtils.CopySlice(PrevBlock) - SigGroupCopy := gUtils.CopySlice(SigGroup) + PrevBlockCopy := utils.CopySlice(PrevBlock) + SigGroupCopy := utils.CopySlice(SigGroup) rc := &RCert{ RClaims: &RClaims{ ChainID: bclaims.ChainID, diff --git a/consensus/objs/nr_test.go b/consensus/objs/nr_test.go index 573a2068..a1e6fa56 100644 --- a/consensus/objs/nr_test.go +++ b/consensus/objs/nr_test.go @@ -19,7 +19,10 @@ func TestNextRound(t *testing.T) { t.Fatal(err) } gk := &crypto.BNGroupSigner{} - gk.SetPrivk(crypto.Hasher([]byte("secret"))) + err = gk.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } sig, err := gk.Sign(bhsh) if err != nil { t.Fatal(err) diff --git a/consensus/objs/nrc.go b/consensus/objs/nrc.go index 62ed3796..e985c4ab 100644 --- a/consensus/objs/nrc.go +++ b/consensus/objs/nrc.go @@ -3,11 +3,10 @@ package objs import ( "bytes" - "github.com/MadBase/MadNet/errorz" - mdefs "github.com/MadBase/MadNet/consensus/objs/capn" "github.com/MadBase/MadNet/consensus/objs/nrclaims" "github.com/MadBase/MadNet/crypto" + "github.com/MadBase/MadNet/errorz" capnp "zombiezen.com/go/capnproto2" ) diff --git a/consensus/objs/nrl.go b/consensus/objs/nrl.go index 591c5d9b..c6f90d61 100644 --- a/consensus/objs/nrl.go +++ b/consensus/objs/nrl.go @@ -2,7 +2,7 @@ package objs import ( "github.com/MadBase/MadNet/crypto" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) // NextRoundList ... @@ -11,13 +11,13 @@ type NextRoundList []*NextRound func (nrl NextRoundList) MakeRoundCert(bns *crypto.BNGroupSigner, groupShares [][]byte) (*RCert, error) { sigs := [][]byte{} for _, nr := range nrl { - sigs = append(sigs, gUtils.CopySlice(nr.NRClaims.SigShare)) + sigs = append(sigs, utils.CopySlice(nr.NRClaims.SigShare)) } SigGroup, err := bns.Aggregate(sigs, groupShares) if err != nil { return nil, err } - PrevBlock := gUtils.CopySlice(nrl[0].NRClaims.RClaims.PrevBlock) + PrevBlock := utils.CopySlice(nrl[0].NRClaims.RClaims.PrevBlock) rc := &RCert{ RClaims: &RClaims{ ChainID: nrl[0].NRClaims.RClaims.ChainID, diff --git a/consensus/objs/ostate/ostate.go b/consensus/objs/ostate/ostate.go index 73a6dedb..ff77bcde 100644 --- a/consensus/objs/ostate/ostate.go +++ b/consensus/objs/ostate/ostate.go @@ -3,7 +3,7 @@ package ostate import ( mdefs "github.com/MadBase/MadNet/consensus/objs/capn" "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -14,7 +14,7 @@ func Marshal(v mdefs.OwnState) ([]byte, error) { return nil, err } defer v.Struct.Segment().Message().Reset(nil) - out := gUtils.CopySlice(raw) + out := utils.CopySlice(raw) return out, nil } diff --git a/consensus/objs/ovs.go b/consensus/objs/ovs.go index c269ebfb..fa15ad86 100644 --- a/consensus/objs/ovs.go +++ b/consensus/objs/ovs.go @@ -3,11 +3,10 @@ package objs import ( "time" - "github.com/MadBase/MadNet/errorz" - mdefs "github.com/MadBase/MadNet/consensus/objs/capn" "github.com/MadBase/MadNet/consensus/objs/ovstate" "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/errorz" capnp "zombiezen.com/go/capnproto2" ) @@ -132,24 +131,24 @@ func (b *OwnValidatingState) MarshalCapn(seg *capnp.Segment) (mdefs.OwnValidatin return bh, nil } -func (b *OwnValidatingState) PTOExpired() bool { +func (b *OwnValidatingState) PTOExpired(proposalStepTO time.Duration) bool { rs := b.RoundStarted - return rs+int64(constants.ProposalStepTO)/constants.OneBillion < time.Now().Unix() + return rs+int64(proposalStepTO)/constants.OneBillion < time.Now().Unix() } -func (b *OwnValidatingState) PVTOExpired() bool { +func (b *OwnValidatingState) PVTOExpired(preVoteStepTO time.Duration) bool { rs := b.PreVoteStepStarted - return rs+int64(constants.PreVoteStepTO)/constants.OneBillion < time.Now().Unix() + return rs+int64(preVoteStepTO)/constants.OneBillion < time.Now().Unix() } -func (b *OwnValidatingState) PCTOExpired() bool { +func (b *OwnValidatingState) PCTOExpired(preCommitStepTO time.Duration) bool { rs := b.PreCommitStepStarted - return rs+int64(constants.PreCommitStepTO)/constants.OneBillion < time.Now().Unix() + return rs+int64(preCommitStepTO)/constants.OneBillion < time.Now().Unix() } -func (b *OwnValidatingState) DBRNRExpired() bool { +func (b *OwnValidatingState) DBRNRExpired(dbrnrTO time.Duration) bool { rs := b.PreCommitStepStarted - return rs+int64(constants.DBRNRTO)/constants.OneBillion < time.Now().Unix() + return rs+int64(dbrnrTO)/constants.OneBillion < time.Now().Unix() } func (b *OwnValidatingState) SetRoundStarted() { diff --git a/consensus/objs/pc.go b/consensus/objs/pc.go index d7f75cba..cf9111a9 100644 --- a/consensus/objs/pc.go +++ b/consensus/objs/pc.go @@ -3,12 +3,11 @@ package objs import ( "bytes" - "github.com/MadBase/MadNet/errorz" - mdefs "github.com/MadBase/MadNet/consensus/objs/capn" "github.com/MadBase/MadNet/consensus/objs/precommit" "github.com/MadBase/MadNet/crypto" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/errorz" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -145,7 +144,7 @@ func (b *PreCommit) ValidateSignatures(secpVal *crypto.Secp256k1Validator, bnVal CE = append(CE, PreVoteSigDesignator()...) CE = append(CE, canonicalEncoding...) for _, sig := range b.PreVotes { - pubkey, err := secpVal.Validate(CE, gUtils.CopySlice(sig)) + pubkey, err := secpVal.Validate(CE, utils.CopySlice(sig)) if err != nil { return err } @@ -167,9 +166,9 @@ func (b *PreCommit) MakeImplPreVotes() (PreVoteList, error) { if err != nil { return nil, err } - groupKey := gUtils.CopySlice(b.GroupKey) - voter := gUtils.CopySlice(b.Signers[idx]) - sig := gUtils.CopySlice(pv) + groupKey := utils.CopySlice(b.GroupKey) + voter := utils.CopySlice(b.Signers[idx]) + sig := utils.CopySlice(pv) pV := &PreVote{ Signature: sig, Proposal: pc.Proposal, diff --git a/consensus/objs/pc_test.go b/consensus/objs/pc_test.go index f2d49b78..461e854d 100644 --- a/consensus/objs/pc_test.go +++ b/consensus/objs/pc_test.go @@ -19,7 +19,10 @@ func TestPreCommit(t *testing.T) { t.Fatal(err) } gk := crypto.BNGroupSigner{} - gk.SetPrivk(crypto.Hasher([]byte("secret"))) + err = gk.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } sig, err := gk.Sign(bhsh) if err != nil { t.Fatal(err) diff --git a/consensus/objs/pcl.go b/consensus/objs/pcl.go index 6e60679a..8eee7dc2 100644 --- a/consensus/objs/pcl.go +++ b/consensus/objs/pcl.go @@ -2,7 +2,7 @@ package objs import ( "github.com/MadBase/MadNet/crypto" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) type PreCommitList []*PreCommit @@ -20,7 +20,7 @@ func (pcl PreCommitList) MakeNextHeight(secpSigner *crypto.Secp256k1Signer, bnSi } sigs := [][]byte{} for _, pc := range pcl { - s := gUtils.CopySlice(pc.Signature) + s := utils.CopySlice(pc.Signature) sigs = append(sigs, s) } nh := &NextHeight{ diff --git a/consensus/objs/pclms.go b/consensus/objs/pclms.go index 385fa64f..51516f84 100644 --- a/consensus/objs/pclms.go +++ b/consensus/objs/pclms.go @@ -3,10 +3,9 @@ package objs import ( "bytes" - "github.com/MadBase/MadNet/errorz" - mdefs "github.com/MadBase/MadNet/consensus/objs/capn" "github.com/MadBase/MadNet/consensus/objs/pclaims" + "github.com/MadBase/MadNet/errorz" capnp "zombiezen.com/go/capnproto2" ) diff --git a/consensus/objs/pclms_test.go b/consensus/objs/pclms_test.go index 667fb530..da9e6e0f 100644 --- a/consensus/objs/pclms_test.go +++ b/consensus/objs/pclms_test.go @@ -18,7 +18,10 @@ func TestPClaims(t *testing.T) { t.Fatal(err) } gk := crypto.BNGroupSigner{} - gk.SetPrivk(crypto.Hasher([]byte("secret"))) + err = gk.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } sig, err := gk.Sign(bhsh) if err != nil { t.Fatal(err) diff --git a/consensus/objs/pcn.go b/consensus/objs/pcn.go index adf48b96..59105203 100644 --- a/consensus/objs/pcn.go +++ b/consensus/objs/pcn.go @@ -5,7 +5,7 @@ import ( "github.com/MadBase/MadNet/consensus/objs/precommitnil" "github.com/MadBase/MadNet/crypto" "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -40,7 +40,7 @@ func (b *PreCommitNil) UnmarshalCapn(bh mdefs.PreCommitNil) error { if err != nil { return err } - b.Signature = gUtils.CopySlice(bh.Signature()) + b.Signature = utils.CopySlice(bh.Signature()) return nil } diff --git a/consensus/objs/pcn_test.go b/consensus/objs/pcn_test.go index 48b1e0c7..25bca425 100644 --- a/consensus/objs/pcn_test.go +++ b/consensus/objs/pcn_test.go @@ -19,7 +19,10 @@ func TestPreCommitNil(t *testing.T) { t.Fatal(err) } gk := &crypto.BNGroupSigner{} - gk.SetPrivk(crypto.Hasher([]byte("secret"))) + err = gk.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } sig, err := gk.Sign(bhsh) if err != nil { t.Fatal(err) diff --git a/consensus/objs/phlkey.go b/consensus/objs/phlkey.go index 95edc4cb..e3eac0ab 100644 --- a/consensus/objs/phlkey.go +++ b/consensus/objs/phlkey.go @@ -2,7 +2,7 @@ package objs import ( "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) // PendingHdrLeafKey ... @@ -20,8 +20,8 @@ func (b *PendingHdrLeafKey) UnmarshalBinary(data []byte) error { if len(data) != 34 { return errorz.ErrInvalid{}.New("Invalid data for PendingHdrLeafKey unmarshalling") } - b.Prefix = gUtils.CopySlice(data[0:2]) - b.Key = gUtils.CopySlice(data[2:]) + b.Prefix = utils.CopySlice(data[0:2]) + b.Key = utils.CopySlice(data[2:]) return nil } @@ -32,7 +32,7 @@ func (b *PendingHdrLeafKey) MarshalBinary() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } key := []byte{} - key = append(key, gUtils.CopySlice(b.Prefix)...) - key = append(key, gUtils.CopySlice(b.Key)...) + key = append(key, utils.CopySlice(b.Prefix)...) + key = append(key, utils.CopySlice(b.Key)...) return key, nil } diff --git a/consensus/objs/plkey.go b/consensus/objs/plkey.go index 84947943..d6890ebc 100644 --- a/consensus/objs/plkey.go +++ b/consensus/objs/plkey.go @@ -2,7 +2,7 @@ package objs import ( "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) // PendingLeafKey ... @@ -20,8 +20,8 @@ func (b *PendingLeafKey) UnmarshalBinary(data []byte) error { if len(data) != 34 { return errorz.ErrInvalid{}.New("Invalid data for BlockHeaderHeightKey unmarshalling") } - b.Prefix = gUtils.CopySlice(data[0:2]) - b.Key = gUtils.CopySlice(data[2:]) + b.Prefix = utils.CopySlice(data[0:2]) + b.Key = utils.CopySlice(data[2:]) return nil } @@ -32,7 +32,7 @@ func (b *PendingLeafKey) MarshalBinary() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } key := []byte{} - key = append(key, gUtils.CopySlice(b.Prefix)...) - key = append(key, gUtils.CopySlice(b.Key)...) + key = append(key, utils.CopySlice(b.Prefix)...) + key = append(key, utils.CopySlice(b.Key)...) return key, nil } diff --git a/consensus/objs/pnkey.go b/consensus/objs/pnkey.go index 1f7f9ad4..7546ca99 100644 --- a/consensus/objs/pnkey.go +++ b/consensus/objs/pnkey.go @@ -2,7 +2,7 @@ package objs import ( "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) // PendingNodeKey ... @@ -20,8 +20,8 @@ func (b *PendingNodeKey) UnmarshalBinary(data []byte) error { if len(data) != 34 { return errorz.ErrInvalid{}.New("Invalid data for PendingNodeKey unmarshalling") } - b.Prefix = gUtils.CopySlice(data[0:2]) - b.Key = gUtils.CopySlice(data[2:]) + b.Prefix = utils.CopySlice(data[0:2]) + b.Key = utils.CopySlice(data[2:]) return nil } @@ -32,7 +32,7 @@ func (b *PendingNodeKey) MarshalBinary() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } key := []byte{} - key = append(key, gUtils.CopySlice(b.Prefix)...) - key = append(key, gUtils.CopySlice(b.Key)...) + key = append(key, utils.CopySlice(b.Prefix)...) + key = append(key, utils.CopySlice(b.Key)...) return key, nil } diff --git a/consensus/objs/prop.go b/consensus/objs/prop.go index 477934b0..e5288678 100644 --- a/consensus/objs/prop.go +++ b/consensus/objs/prop.go @@ -3,13 +3,12 @@ package objs import ( "bytes" - "github.com/MadBase/MadNet/constants" - "github.com/MadBase/MadNet/errorz" - mdefs "github.com/MadBase/MadNet/consensus/objs/capn" "github.com/MadBase/MadNet/consensus/objs/proposal" + "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/errorz" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -51,7 +50,7 @@ func (b *Proposal) UnmarshalCapn(bh mdefs.Proposal) error { return err } b.TxHshLst = lst - b.Signature = gUtils.CopySlice(bh.Signature()) + b.Signature = utils.CopySlice(bh.Signature()) if b.PClaims.RCert.RClaims.Round == constants.DEADBLOCKROUND { if len(b.TxHshLst) != 0 { return errorz.ErrInvalid{}.New("non empty tx hash lst in deadblockround") diff --git a/consensus/objs/prop_test.go b/consensus/objs/prop_test.go index ba79f3b2..f47268f5 100644 --- a/consensus/objs/prop_test.go +++ b/consensus/objs/prop_test.go @@ -19,7 +19,10 @@ func TestProposal(t *testing.T) { t.Fatal(err) } gk := &crypto.BNGroupSigner{} - gk.SetPrivk(crypto.Hasher([]byte("secret"))) + err = gk.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } sig, err := gk.Sign(bhsh) if err != nil { t.Fatal(err) @@ -27,7 +30,7 @@ func TestProposal(t *testing.T) { secpSigner := &crypto.Secp256k1Signer{} err = secpSigner.SetPrivk(crypto.Hasher([]byte("secret"))) if err != nil { - t.Fatal(err) + t.Fatal(err) } bh := &BlockHeader{ BClaims: bclaims, diff --git a/consensus/objs/pv.go b/consensus/objs/pv.go index 5941f632..1173c5e8 100644 --- a/consensus/objs/pv.go +++ b/consensus/objs/pv.go @@ -5,7 +5,7 @@ import ( "github.com/MadBase/MadNet/consensus/objs/prevote" "github.com/MadBase/MadNet/crypto" "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -40,7 +40,7 @@ func (b *PreVote) UnmarshalCapn(bh mdefs.PreVote) error { if err != nil { return err } - b.Signature = gUtils.CopySlice(bh.Signature()) + b.Signature = utils.CopySlice(bh.Signature()) return nil } diff --git a/consensus/objs/pv_test.go b/consensus/objs/pv_test.go index 59febd91..c73a2418 100644 --- a/consensus/objs/pv_test.go +++ b/consensus/objs/pv_test.go @@ -19,7 +19,10 @@ func TestPreVote(t *testing.T) { t.Fatal(err) } gk := crypto.BNGroupSigner{} - gk.SetPrivk(crypto.Hasher([]byte("secret"))) + err = gk.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } sig, err := gk.Sign(bhsh) if err != nil { t.Fatal(err) diff --git a/consensus/objs/pvl.go b/consensus/objs/pvl.go index a9004cbb..5e170068 100644 --- a/consensus/objs/pvl.go +++ b/consensus/objs/pvl.go @@ -2,7 +2,7 @@ package objs import ( "github.com/MadBase/MadNet/crypto" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) type PreVoteList []*PreVote @@ -11,7 +11,7 @@ type PreVoteNilList []bool func (pvl PreVoteList) MakePreCommit(secpSigner *crypto.Secp256k1Signer) (*PreCommit, error) { sigs := [][]byte{} for _, pv := range pvl { - s := gUtils.CopySlice(pv.Signature) + s := utils.CopySlice(pv.Signature) sigs = append(sigs, s) } propBytes, err := pvl[0].Proposal.MarshalBinary() diff --git a/consensus/objs/pvn.go b/consensus/objs/pvn.go index a05ded6e..1b53cbe1 100644 --- a/consensus/objs/pvn.go +++ b/consensus/objs/pvn.go @@ -5,7 +5,7 @@ import ( "github.com/MadBase/MadNet/consensus/objs/prevotenil" "github.com/MadBase/MadNet/crypto" "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -40,7 +40,7 @@ func (b *PreVoteNil) UnmarshalCapn(bh mdefs.PreVoteNil) error { if err != nil { return err } - b.Signature = gUtils.CopySlice(bh.Signature()) + b.Signature = utils.CopySlice(bh.Signature()) return nil } diff --git a/consensus/objs/pvn_test.go b/consensus/objs/pvn_test.go index cd19ac5e..73f17085 100644 --- a/consensus/objs/pvn_test.go +++ b/consensus/objs/pvn_test.go @@ -19,7 +19,10 @@ func TestPreVoteNil(t *testing.T) { t.Fatal(err) } gk := &crypto.BNGroupSigner{} - gk.SetPrivk(crypto.Hasher([]byte("secret"))) + err = gk.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } sig, err := gk.Sign(bhsh) if err != nil { t.Fatal(err) diff --git a/consensus/objs/rc.go b/consensus/objs/rc.go index 534355f1..6217600c 100644 --- a/consensus/objs/rc.go +++ b/consensus/objs/rc.go @@ -6,7 +6,7 @@ import ( "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -40,7 +40,7 @@ func (b *RCert) UnmarshalCapn(bh mdefs.RCert) error { if err != nil { return err } - b.SigGroup = gUtils.CopySlice(bh.SigGroup()) + b.SigGroup = utils.CopySlice(bh.SigGroup()) return nil } @@ -98,7 +98,7 @@ func (b *RCert) MarshalCapn(seg *capnp.Segment) (mdefs.RCert, error) { // ValidateSignature validates the group signature on the RCert func (b *RCert) ValidateSignature(bnVal *crypto.BNGroupValidator) error { - if b == nil || b.RClaims == nil { + if b == nil || b.RClaims == nil || b.RClaims.Height == 0 || b.RClaims.ChainID == 0 || b.RClaims.Round == 0 || b.RClaims.Round > constants.DEADBLOCKROUND { return errorz.ErrInvalid{}.New("not initialized") } if b.RClaims.Height == 1 && b.RClaims.Round == 1 { @@ -113,6 +113,9 @@ func (b *RCert) ValidateSignature(bnVal *crypto.BNGroupValidator) error { // There is nothing we can check because there is no group signature return nil } + if len(b.RClaims.PrevBlock) != constants.HashLen { + return errorz.ErrInvalid{}.New("invalid PrevBlock") + } if b.RClaims.Round > 1 { canonicalEncoding, err := b.RClaims.MarshalBinary() if err != nil { @@ -192,11 +195,11 @@ func (b *RCert) NextRound(secpSigner *crypto.Secp256k1Signer, bnSigner *crypto.B if b == nil { return nil, errorz.ErrInvalid{}.New("not initialized") } - nrrClaims := &RClaims{} rcClaims, err := b.RClaims.MarshalBinary() if err != nil { return nil, err } + nrrClaims := &RClaims{} err = nrrClaims.UnmarshalBinary(rcClaims) if err != nil { return nil, err diff --git a/consensus/objs/rc_test.go b/consensus/objs/rc_test.go index bf0b5bd6..b9380045 100644 --- a/consensus/objs/rc_test.go +++ b/consensus/objs/rc_test.go @@ -1,8 +1,10 @@ package objs import ( + "bytes" "testing" + "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" ) @@ -19,7 +21,10 @@ func TestRCert(t *testing.T) { t.Fatal(err) } gk := &crypto.BNGroupSigner{} - gk.SetPrivk(crypto.Hasher([]byte("secret"))) + err = gk.SetPrivk(crypto.Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } sig, err := gk.Sign(bhsh) if err != nil { t.Fatal(err) @@ -59,3 +64,159 @@ func TestRCert(t *testing.T) { t.Fatal(err) } } + +func TestRCertMarshal(t *testing.T) { + pvn := &PreVoteNil{} + _, err := pvn.RCert.MarshalBinary() + if err == nil { + t.Fatal("Should have raised error (0)") + } + + rc := &RCert{} + _, err = rc.MarshalBinary() + if err == nil { + t.Fatal("Should have raised error (1)") + } +} + +func TestRCertValidateSignature(t *testing.T) { + bnVal := &crypto.BNGroupValidator{} + pvn := &PreVoteNil{} + err := pvn.RCert.ValidateSignature(bnVal) + if err == nil { + t.Fatal("Should have raised error (0)") + } + + // Invalid RClaims object + rc := &RCert{} + err = rc.ValidateSignature(bnVal) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + // Everything is good + rc.RClaims = &RClaims{} + rc.RClaims.ChainID = 1 + rc.RClaims.Height = 1 + rc.RClaims.Round = 1 + err = rc.ValidateSignature(bnVal) + if err != nil { + t.Fatal(err) + } + groupKey := make([]byte, constants.CurveBN256EthPubkeyLen) + if !bytes.Equal(rc.GroupKey, groupKey) { + t.Fatal("Invalid GroupKey") + } + + // Invalid Height/Round combination; not possible to be Height 1, Round 2 + rc.GroupKey = nil + rc.RClaims.Height = 1 + rc.RClaims.Round = 2 + err = rc.ValidateSignature(bnVal) + if err == nil { + t.Fatal("Should have raised error (2)") + } + + // No error is raised + rc.RClaims.Height = 2 + rc.RClaims.Round = 1 + err = rc.ValidateSignature(bnVal) + if err != nil { + t.Fatal(err) + } + + // Should raise an error for invalid RClaims object + rc.RClaims.Height = 2 + rc.RClaims.Round = constants.DEADBLOCKROUND + 1 + err = rc.ValidateSignature(bnVal) + if err == nil { + t.Fatal("Should have raised error (3)") + } + + // Should raise an error for invalid PrevBlock + rc.RClaims.Height = 2 + rc.RClaims.Round = 2 + rc.RClaims.PrevBlock = make([]byte, constants.HashLen+1) + err = rc.ValidateSignature(bnVal) + if err == nil { + t.Fatal("Should have raised error (4)") + } + + // Should raise an error for invalid PrevBlock + rc.RClaims.Height = 2 + rc.RClaims.Round = 2 + rc.RClaims.PrevBlock = make([]byte, constants.HashLen) + err = rc.ValidateSignature(bnVal) + if err == nil { + t.Fatal("Should have raised error (5)") + } + + // Should raise an error for invalid PrevBlock + rc.RClaims.Height = 3 + rc.RClaims.Round = 1 + rc.RClaims.PrevBlock = make([]byte, constants.HashLen) + err = rc.ValidateSignature(bnVal) + if err == nil { + t.Fatal("Should have raised error (6)") + } +} + +func TestRCertPreVoteNil(t *testing.T) { + rc := &RCert{} + _, err := rc.PreVoteNil(nil) + if err == nil { + t.Fatal("Should have raised error (1)") + } + rc.RClaims = &RClaims{ + ChainID: 1, + Height: 1, + Round: 1, + PrevBlock: make([]byte, constants.HashLen), + } + _, err = rc.PreVoteNil(nil) + if err == nil { + t.Fatal("Should have raised error (2)") + } +} + +func TestRCertPreCommitNil(t *testing.T) { + rc := &RCert{} + _, err := rc.PreCommitNil(nil) + if err == nil { + t.Fatal("Should have raised error (1)") + } + rc.RClaims = &RClaims{ + ChainID: 1, + Height: 1, + Round: 1, + PrevBlock: make([]byte, constants.HashLen), + } + _, err = rc.PreCommitNil(nil) + if err == nil { + t.Fatal("Should have raised error (2)") + } +} + +func TestRCertNextRound(t *testing.T) { + nrc := &NRClaims{} + _, err := nrc.RCert.NextRound(nil, nil) + if err == nil { + t.Fatal("Should have raised error (0)") + } + + rc := &RCert{} + _, err = rc.NextRound(nil, nil) + if err == nil { + t.Fatal("Should have raised error (1)") + } + rc.RClaims = &RClaims{ + ChainID: 1, + Height: 1, + Round: 1, + PrevBlock: make([]byte, constants.HashLen), + } + _, err = rc.NextRound(nil, nil) + if err == nil { + t.Fatal("Should have raised error (2)") + } +} diff --git a/consensus/objs/rclms.go b/consensus/objs/rclms.go index 9e990ece..d1de1813 100644 --- a/consensus/objs/rclms.go +++ b/consensus/objs/rclms.go @@ -5,7 +5,7 @@ import ( "github.com/MadBase/MadNet/consensus/objs/rclaims" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -37,10 +37,7 @@ func (b *RClaims) UnmarshalCapn(bh mdefs.RClaims) error { b.ChainID = bh.ChainID() b.Height = bh.Height() b.Round = bh.Round() - b.PrevBlock = gUtils.CopySlice(bh.PrevBlock()) - if len(b.PrevBlock) != constants.HashLen { - return errorz.ErrInvalid{}.New("rclaims bad prevb len") - } + b.PrevBlock = utils.CopySlice(bh.PrevBlock()) if b.Height < 1 { return errorz.ErrInvalid{}.New("rclaims bad height") } diff --git a/consensus/objs/rclms_test.go b/consensus/objs/rclms_test.go index d0afb169..844b7d68 100644 --- a/consensus/objs/rclms_test.go +++ b/consensus/objs/rclms_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/utils" ) func rclaimsEqual(t *testing.T, rclaims, rclaims2 *RClaims) { @@ -116,3 +117,46 @@ func TestRClaimsBad(t *testing.T) { t.Fatal("Should have raised error (5)") } } + +func TestRClaimsBad2(t *testing.T) { + cid := uint32(66) + height := uint32(113) + round := uint32(175) + prevHash := make([]byte, constants.HashLen) + + rcl := &RClaims{ + ChainID: cid, + Height: height, + Round: round, + PrevBlock: prevHash, + } + data, err := rcl.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + rcl2 := &RClaims{} + // Raise error for bad ChainID + dataBad1 := utils.CopySlice(data) + dataBad1[8] = 0 + err = rcl2.UnmarshalBinary(dataBad1) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + // Raise error for bad Height + dataBad2 := utils.CopySlice(data) + dataBad2[12] = 0 + err = rcl2.UnmarshalBinary(dataBad2) + if err == nil { + t.Fatal("Should have raised error (2)") + } + + // Raise error for bad Round + dataBad3 := utils.CopySlice(data) + dataBad3[16] = 0 + err = rcl2.UnmarshalBinary(dataBad3) + if err == nil { + t.Fatal("Should have raised error (3)") + } +} diff --git a/consensus/objs/rs.go b/consensus/objs/rs.go index d5c3a4f2..5915e127 100644 --- a/consensus/objs/rs.go +++ b/consensus/objs/rs.go @@ -3,12 +3,11 @@ package objs import ( "errors" - "github.com/MadBase/MadNet/constants" - "github.com/MadBase/MadNet/errorz" - mdefs "github.com/MadBase/MadNet/consensus/objs/capn" "github.com/MadBase/MadNet/consensus/objs/rstate" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/errorz" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -54,9 +53,9 @@ func (b *RoundState) UnmarshalCapn(bh mdefs.RoundState) error { if err != nil { return err } - b.VAddr = gUtils.CopySlice(bh.VAddr()) - b.GroupKey = gUtils.CopySlice(bh.GroupKey()) - b.GroupShare = gUtils.CopySlice(bh.GroupShare()) + b.VAddr = utils.CopySlice(bh.VAddr()) + b.GroupKey = utils.CopySlice(bh.GroupKey()) + b.GroupShare = utils.CopySlice(bh.GroupShare()) b.GroupIdx = bh.GroupIdx() b.ImplicitPVN = bh.ImplicitPVN() b.ImplicitPCN = bh.ImplicitPCN() diff --git a/consensus/objs/rs_test.go b/consensus/objs/rs_test.go index a4581df0..2cdcbbf8 100644 --- a/consensus/objs/rs_test.go +++ b/consensus/objs/rs_test.go @@ -158,7 +158,10 @@ func makeSigners(t *testing.T, num int) ([]*crypto.Secp256k1Signer, []*crypto.BN } secpSigners = append(secpSigners, secpSigner) bnSigner := &crypto.BNGroupSigner{} - bnSigner.SetPrivk(crypto.Hasher([]byte("secret" + strconv.Itoa(i)))) + err = bnSigner.SetPrivk(crypto.Hasher([]byte("secret" + strconv.Itoa(i)))) + if err != nil { + t.Fatal(err) + } bnSigners = append(bnSigners, bnSigner) } return secpSigners, bnSigners diff --git a/consensus/objs/rskeycurr.go b/consensus/objs/rskeycurr.go index 01db5433..839be7a6 100644 --- a/consensus/objs/rskeycurr.go +++ b/consensus/objs/rskeycurr.go @@ -5,8 +5,7 @@ import ( "encoding/hex" "github.com/MadBase/MadNet/errorz" - - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) //# index historic by by groupKey|height|round|vAddr @@ -26,7 +25,7 @@ func (b *RoundStateCurrentKey) MarshalBinary() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } key := []byte{} - Prefix := gUtils.CopySlice(b.Prefix) + Prefix := utils.CopySlice(b.Prefix) GroupKey := make([]byte, hex.EncodedLen(len(b.GroupKey))) VAddr := make([]byte, hex.EncodedLen(len(b.VAddr))) _ = hex.Encode(GroupKey, b.GroupKey) diff --git a/consensus/objs/rskeyhist.go b/consensus/objs/rskeyhist.go index 8c7fd3cc..0b30e2f3 100644 --- a/consensus/objs/rskeyhist.go +++ b/consensus/objs/rskeyhist.go @@ -5,8 +5,7 @@ import ( "encoding/hex" "github.com/MadBase/MadNet/errorz" - - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) // RoundStateHistoricKey ... @@ -24,11 +23,11 @@ func (b *RoundStateHistoricKey) MarshalBinary() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } key := []byte{} - Prefix := gUtils.CopySlice(b.Prefix) + Prefix := utils.CopySlice(b.Prefix) VAddr := make([]byte, hex.EncodedLen(len(b.VAddr))) _ = hex.Encode(VAddr, b.VAddr) - Height := gUtils.MarshalUint32(b.Height) - Round := gUtils.MarshalUint32(b.Round) + Height := utils.MarshalUint32(b.Height) + Round := utils.MarshalUint32(b.Round) key = append(key, Prefix...) key = append(key, []byte("|")...) key = append(key, Height...) @@ -50,7 +49,7 @@ func (b *RoundStateHistoricKey) UnmarshalBinary(data []byte) error { return errorz.ErrCorrupt } b.Prefix = splitData[0] - Height, err := gUtils.UnmarshalUint32(splitData[1]) + Height, err := utils.UnmarshalUint32(splitData[1]) if err != nil { return err } @@ -58,7 +57,7 @@ func (b *RoundStateHistoricKey) UnmarshalBinary(data []byte) error { return errorz.ErrInvalid{}.New("invalid height in unmarshalling") } b.Height = Height - Round, err := gUtils.UnmarshalUint32(splitData[2]) + Round, err := utils.UnmarshalUint32(splitData[2]) if err != nil { return err } @@ -81,8 +80,8 @@ func (b *RoundStateHistoricKey) MakeIterKey() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } key := []byte{} - Prefix := gUtils.CopySlice(b.Prefix) - Height := gUtils.MarshalUint32(b.Height) + Prefix := utils.CopySlice(b.Prefix) + Height := utils.MarshalUint32(b.Height) key = append(key, Prefix...) key = append(key, []byte("|")...) key = append(key, Height...) diff --git a/consensus/objs/safetoproceedkey.go b/consensus/objs/safetoproceedkey.go index 3e97fd56..6d1aabce 100644 --- a/consensus/objs/safetoproceedkey.go +++ b/consensus/objs/safetoproceedkey.go @@ -4,8 +4,7 @@ import ( "bytes" "github.com/MadBase/MadNet/errorz" - - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) // SafeToProceedKey ... @@ -21,8 +20,8 @@ func (b *SafeToProceedKey) MarshalBinary() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } key := []byte{} - Prefix := gUtils.CopySlice(b.Prefix) - Height := gUtils.MarshalUint32(b.Height) + Prefix := utils.CopySlice(b.Prefix) + Height := utils.MarshalUint32(b.Height) key = append(key, Prefix...) key = append(key, []byte("|")...) key = append(key, Height...) @@ -41,7 +40,7 @@ func (b *SafeToProceedKey) UnmarshalBinary(data []byte) error { return errorz.ErrCorrupt } b.Prefix = splitData[0] - Height, err := gUtils.UnmarshalUint32(splitData[1]) + Height, err := utils.UnmarshalUint32(splitData[1]) if err != nil { return err } diff --git a/consensus/objs/sbhkey.go b/consensus/objs/sbhkey.go index 51c4804d..ff374115 100644 --- a/consensus/objs/sbhkey.go +++ b/consensus/objs/sbhkey.go @@ -2,7 +2,7 @@ package objs import ( "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) // StagedBlockHeaderKey ... @@ -20,8 +20,8 @@ func (b *StagedBlockHeaderKey) UnmarshalBinary(data []byte) error { if len(data) != 6 { return errorz.ErrInvalid{}.New("Invalid data for StagedBlockHeaderKey unmarshalling") } - b.Prefix = gUtils.CopySlice(data[0:2]) - b.Key = gUtils.CopySlice(data[2:]) + b.Prefix = utils.CopySlice(data[0:2]) + b.Key = utils.CopySlice(data[2:]) return nil } @@ -32,7 +32,7 @@ func (b *StagedBlockHeaderKey) MarshalBinary() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } key := []byte{} - Prefix := gUtils.CopySlice(b.Prefix) + Prefix := utils.CopySlice(b.Prefix) key = append(key, Prefix...) key = append(key, b.Key...) return key, nil diff --git a/consensus/objs/state_test.go b/consensus/objs/state_test.go index 4a1348a7..32738ce7 100644 --- a/consensus/objs/state_test.go +++ b/consensus/objs/state_test.go @@ -248,7 +248,10 @@ func makeSigners2(t *testing.T) ([]byte, []*crypto.BNGroupSigner, [][]byte, []*c gpk1 := new(bn256.G2).ScalarBaseMult(gsk1) groupShares[0] = gpk1.Marshal() s1 := new(crypto.BNGroupSigner) - s1.SetPrivk(gsk1.Bytes()) + err := s1.SetPrivk(gsk1.Bytes()) + if err != nil { + t.Fatal(err) + } sig1, err := s1.Sign(msg) if err != nil { t.Fatal(err) @@ -259,7 +262,10 @@ func makeSigners2(t *testing.T) ([]byte, []*crypto.BNGroupSigner, [][]byte, []*c gpk2 := new(bn256.G2).ScalarBaseMult(gsk2) groupShares[1] = gpk2.Marshal() s2 := new(crypto.BNGroupSigner) - s2.SetPrivk(gsk2.Bytes()) + err = s2.SetPrivk(gsk2.Bytes()) + if err != nil { + t.Fatal(err) + } sig2, err := s2.Sign(msg) if err != nil { t.Fatal(err) @@ -270,7 +276,10 @@ func makeSigners2(t *testing.T) ([]byte, []*crypto.BNGroupSigner, [][]byte, []*c gpk3 := new(bn256.G2).ScalarBaseMult(gsk3) groupShares[2] = gpk3.Marshal() s3 := new(crypto.BNGroupSigner) - s3.SetPrivk(gsk3.Bytes()) + err = s3.SetPrivk(gsk3.Bytes()) + if err != nil { + t.Fatal(err) + } sig3, err := s3.Sign(msg) if err != nil { t.Fatal(err) @@ -281,7 +290,10 @@ func makeSigners2(t *testing.T) ([]byte, []*crypto.BNGroupSigner, [][]byte, []*c gpk4 := new(bn256.G2).ScalarBaseMult(gsk4) groupShares[3] = gpk4.Marshal() s4 := new(crypto.BNGroupSigner) - s4.SetPrivk(gsk4.Bytes()) + err = s4.SetPrivk(gsk4.Bytes()) + if err != nil { + t.Fatal(err) + } sig4, err := s4.Sign(msg) if err != nil { t.Fatal(err) diff --git a/consensus/objs/txcachekey.go b/consensus/objs/txcachekey.go index 4ec1f8cf..c03efeb0 100644 --- a/consensus/objs/txcachekey.go +++ b/consensus/objs/txcachekey.go @@ -3,10 +3,9 @@ package objs import ( "bytes" - "github.com/MadBase/MadNet/errorz" - "github.com/MadBase/MadNet/constants" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/errorz" + "github.com/MadBase/MadNet/utils" ) // TxCacheKey ... @@ -23,9 +22,9 @@ func (b *TxCacheKey) MarshalBinary() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } key := []byte{} - Prefix := gUtils.CopySlice(b.Prefix) - TxHash := gUtils.CopySlice(b.TxHash) - Height := gUtils.MarshalUint32(b.Height) + Prefix := utils.CopySlice(b.Prefix) + TxHash := utils.CopySlice(b.TxHash) + Height := utils.MarshalUint32(b.Height) key = append(key, Prefix...) key = append(key, []byte("|")...) key = append(key, Height...) @@ -45,7 +44,7 @@ func (b *TxCacheKey) UnmarshalBinary(data []byte) error { return errorz.ErrCorrupt } b.Prefix = splitData[0] - Height, err := gUtils.UnmarshalUint32(splitData[1]) + Height, err := utils.UnmarshalUint32(splitData[1]) if err != nil { return err } @@ -53,7 +52,7 @@ func (b *TxCacheKey) UnmarshalBinary(data []byte) error { return errorz.ErrInvalid{}.New("invalid height for unmarshalling") } b.Height = Height - TxHash := gUtils.CopySlice(splitData[2]) + TxHash := utils.CopySlice(splitData[2]) if len(TxHash) != constants.HashLen { return errorz.ErrInvalid{}.New("invalid txhash for unmarshalling; incorrect length") } @@ -67,8 +66,8 @@ func (b *TxCacheKey) MakeIterKey() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } key := []byte{} - Prefix := gUtils.CopySlice(b.Prefix) - Height := gUtils.MarshalUint32(b.Height) + Prefix := utils.CopySlice(b.Prefix) + Height := utils.MarshalUint32(b.Height) key = append(key, Prefix...) key = append(key, []byte("|")...) key = append(key, Height...) diff --git a/consensus/objs/txcachekey_test.go b/consensus/objs/txcachekey_test.go index 33d26e04..1c73ad42 100644 --- a/consensus/objs/txcachekey_test.go +++ b/consensus/objs/txcachekey_test.go @@ -6,7 +6,7 @@ import ( "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) func txckEqual(t *testing.T, txck, txck2 *TxCacheKey) { @@ -48,7 +48,7 @@ func TestTxCacheKey(t *testing.T) { } txckEqual(t, txck, txck2) - hb := gUtils.MarshalUint32(height) + hb := utils.MarshalUint32(height) iterKeyTrue := []byte{} iterKeyTrue = append(iterKeyTrue, prefix...) iterKeyTrue = append(iterKeyTrue, []byte("|")...) diff --git a/consensus/objs/util.go b/consensus/objs/util.go index 8e4e980f..a9e8917a 100644 --- a/consensus/objs/util.go +++ b/consensus/objs/util.go @@ -12,6 +12,8 @@ import ( "github.com/MadBase/MadNet/utils" ) +// ExtractHR extracts the Height and Round from an interface; +// it panics on undefined types. func ExtractHR(any interface{}) (uint32, uint32) { switch v := any.(type) { case *RoundState: @@ -65,6 +67,8 @@ func ExtractHR(any interface{}) (uint32, uint32) { } } +// ExtractHCID extracts the Height and ChainID from an interface; +// it panics on undefined types. func ExtractHCID(any interface{}) (uint32, uint32) { switch v := any.(type) { case *RCert: @@ -94,11 +98,16 @@ func ExtractHCID(any interface{}) (uint32, uint32) { case *BlockHeader: rc := v.BClaims return rc.Height, rc.ChainID + case *BClaims: + rc := v + return rc.Height, rc.ChainID default: panic(fmt.Sprintf("undefined type in ExtractHCID %T", v)) } } +// ExtractRCertAny extracts an RCert from an interface; +// it panics on undefined types. func ExtractRCertAny(any interface{}) (*RCert, error) { switch v := any.(type) { case *BlockHeader: @@ -108,6 +117,8 @@ func ExtractRCertAny(any interface{}) (*RCert, error) { } } +// ExtractRCert extracts an RCert from an interface; +// it panics on undefined types. func ExtractRCert(any interface{}) *RCert { switch v := any.(type) { case *RoundState: @@ -151,6 +162,22 @@ func ExtractRCert(any interface{}) *RCert { } } +// RelateHR relates Height and Round between objects. +// Simply: +// if a is before b, return -1 +// if a is after b, return 1 +// if a equals b, return 0 +// +// More explicitly, we extract the height and round from objects a and b. +// +// If (aHeight == bHeight) && (aRound == bRound) +// return 0 +// +// If (aHeight < bHeight) || ((aHeight == bHeight) && (aRound < bRound)) +// return -1 +// +// If (aHeight > bHeight) || ((aHeight == bHeight) && (aRound > bRound)) +// return 1 func RelateHR(a, b interface{}) int { ah, ar := ExtractHR(a) bh, br := ExtractHR(b) @@ -171,6 +198,16 @@ func RelateHR(a, b interface{}) int { return 1 } +// RelateH relates Height between objects +// +// If aHeight == bHeight +// return 0 +// +// If aHeight < bHeight +// return -1 +// +// If aHeight > bHeight +// return 1 func RelateH(a, b interface{}) int { if a == nil && b != nil { return -1 @@ -189,6 +226,7 @@ func RelateH(a, b interface{}) int { return 1 } +// BClaimsEqual determines if two objects have equal BClaims objects func BClaimsEqual(a, b interface{}) (bool, error) { ab := ExtractBClaims(a) bb := ExtractBClaims(b) @@ -206,6 +244,8 @@ func BClaimsEqual(a, b interface{}) (bool, error) { return true, nil } +// ExtractBClaims extracts BClaims from an interface; +// it panics on undefined types. func ExtractBClaims(any interface{}) *BClaims { switch v := any.(type) { case *BlockHeader: @@ -223,12 +263,14 @@ func ExtractBClaims(any interface{}) *BClaims { } } +// PrevBlockEqual determines if objects agree on the previous block func PrevBlockEqual(a, b interface{}) bool { ab := ExtractRCert(a) bb := ExtractRCert(b) return bytes.Equal(ab.RClaims.PrevBlock, bb.RClaims.PrevBlock) } +// IsDeadBlockRound determines if an object is for the DeadBlockRound func IsDeadBlockRound(any interface{}) bool { _, r := ExtractHR(any) return r == constants.DEADBLOCKROUND @@ -242,6 +284,9 @@ func MakeTxRoot(txHashes [][]byte) ([]byte, error) { values := [][]byte{} for i := 0; i < len(txHashes); i++ { txHash := txHashes[i] + if len(txHash) != constants.HashLen { + return nil, errorz.ErrInvalid{}.New("incorrect txHash length") + } values = append(values, crypto.Hasher(txHash)) } // new in memory smt diff --git a/consensus/objs/util_test.go b/consensus/objs/util_test.go new file mode 100644 index 00000000..27c45365 --- /dev/null +++ b/consensus/objs/util_test.go @@ -0,0 +1,336 @@ +package objs + +import ( + "bytes" + "testing" + + "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/crypto" +) + +func TestMakeTxRoot(t *testing.T) { + hashNull := crypto.Hasher([]byte{}) + retHash, err := MakeTxRoot(nil) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(hashNull, retHash) { + t.Fatal("invalid TxRoot") + } + + hashesBad := [][]byte{} + hashesBad = append(hashesBad, []byte{0}) + _, err = MakeTxRoot(hashesBad) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestGetProposerIdx(t *testing.T) { + height := uint32(1) + round := uint32(1) + numv := 4 + propIdx := uint8(1) + retIdx := GetProposerIdx(numv, height, round) + if retIdx != propIdx { + t.Fatal("Invalid proposer index (1)") + } + height = 5 + retIdx = GetProposerIdx(numv, height, round) + if retIdx != propIdx { + t.Fatal("Invalid proposer index (2)") + } + round = 2 + propIdx = 2 + retIdx = GetProposerIdx(numv, height, round) + if retIdx != propIdx { + t.Fatal("Invalid proposer index (3)") + } +} + +func TestSplitBlob(t *testing.T) { + data := make([]byte, 10) + blen := 3 + _, err := SplitBlob(data, blen) + if err == nil { + t.Fatal("Should have raised error (1)") + } + data = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + blen = 2 + trueBuf := [][]byte{} + trueBuf = append(trueBuf, []byte{1, 2}) + trueBuf = append(trueBuf, []byte{3, 4}) + trueBuf = append(trueBuf, []byte{5, 6}) + trueBuf = append(trueBuf, []byte{7, 8}) + trueBuf = append(trueBuf, []byte{9, 10}) + buf, err := SplitBlob(data, blen) + if err != nil { + t.Fatal(err) + } + if len(buf) != len(trueBuf) { + t.Fatal("invalid split") + } + for k := 0; k < len(trueBuf); k++ { + if !bytes.Equal(buf[k], trueBuf[k]) { + t.Fatalf("invalid buf split") + } + } +} + +func TestExtractHR(t *testing.T) { + trueCid := uint32(42) + trueHeight := uint32(137) + trueRound := uint32(1) + bclaims := &BClaims{ + ChainID: trueCid, + Height: trueHeight, + TxCount: 0, + PrevBlock: crypto.Hasher([]byte("Genesis")), + TxRoot: crypto.Hasher([]byte("")), + StateRoot: crypto.Hasher([]byte("")), + HeaderRoot: crypto.Hasher([]byte("")), + } + height, round := ExtractHR(bclaims) + if height != trueHeight { + t.Fatal("Invalid height") + } + if round != trueRound { + t.Fatal("Invalid round") + } +} + +func TestExtractHRBad(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("Should panic") + } + }() + ExtractHR(nil) +} + +func TestExtractHCID(t *testing.T) { + trueCid := uint32(42) + trueHeight := uint32(137) + bclaims := &BClaims{ + ChainID: trueCid, + Height: trueHeight, + TxCount: 0, + PrevBlock: crypto.Hasher([]byte("Genesis")), + TxRoot: crypto.Hasher([]byte("")), + StateRoot: crypto.Hasher([]byte("")), + HeaderRoot: crypto.Hasher([]byte("")), + } + height, cid := ExtractHCID(bclaims) + if height != trueHeight { + t.Fatal("Invalid height") + } + if cid != trueCid { + t.Fatal("Invalid ChainID") + } +} + +func TestExtractHCIDBad(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("Should panic") + } + }() + ExtractHCID(nil) +} + +func TestExtractRCertAny(t *testing.T) { + cid := uint32(1) + height := uint32(2) + prevBlock := crypto.Hasher([]byte("Genesis")) + txRoot := crypto.Hasher([]byte("TxRoot")) + stateRoot := crypto.Hasher([]byte("StateRoot")) + headerRoot := crypto.Hasher([]byte("HeaderRoot")) + bc := &BClaims{ + ChainID: cid, + Height: height, + TxCount: 0, + PrevBlock: prevBlock, + TxRoot: txRoot, + StateRoot: stateRoot, + HeaderRoot: headerRoot, + } + sigGroup := make([]byte, constants.CurveBN256EthSigLen) + bh := &BlockHeader{ + BClaims: bc, + SigGroup: sigGroup, + } + bhsh, err := bh.BlockHash() + if err != nil { + t.Fatal(err) + } + + rc, err := ExtractRCertAny(bh) + if err != nil { + t.Fatal(err) + } + if rc.RClaims.ChainID != cid { + t.Fatal("invalid ChainID") + } + if rc.RClaims.Height != height+1 { + t.Fatal("invalid Height") + } + if rc.RClaims.Round != 1 { + t.Fatal("invalid Round") + } + if !bytes.Equal(bhsh, rc.RClaims.PrevBlock) { + t.Fatal("invalid PrevBlock") + } +} + +func TestExtractRCertAnyBad(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("Should panic") + } + }() + ExtractRCertAny(nil) +} + +func TestExtractRCertBad(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("Should panic") + } + }() + ExtractRCert(nil) +} + +func TestExtractBClaimsBad(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("Should panic") + } + }() + ExtractBClaims(nil) +} + +func TestRelateHR(t *testing.T) { + cid := uint32(66) + height1 := uint32(2) + round1 := uint32(1) + prevHash1 := make([]byte, constants.HashLen) + rc1 := &RClaims{ + ChainID: cid, + Height: height1, + Round: round1, + PrevBlock: prevHash1, + } + + height2 := uint32(2) + round2 := uint32(2) + prevHash2 := make([]byte, constants.HashLen) + rc2 := &RClaims{ + ChainID: cid, + Height: height2, + Round: round2, + PrevBlock: prevHash2, + } + + rel := RelateHR(rc1, rc2) + if rel != -1 { + t.Fatal("Should return -1") + } + + rel = RelateHR(rc2, rc1) + if rel != 1 { + t.Fatal("Should return 1") + } + + rel = RelateHR(rc1, rc1) + if rel != 0 { + t.Fatal("Should return 0") + } + + height3 := uint32(3) + round3 := uint32(1) + prevHash3 := make([]byte, constants.HashLen) + rc3 := &RClaims{ + ChainID: cid, + Height: height3, + Round: round3, + PrevBlock: prevHash3, + } + + height4 := uint32(4) + round4 := uint32(2) + prevHash4 := make([]byte, constants.HashLen) + rc4 := &RClaims{ + ChainID: cid, + Height: height4, + Round: round4, + PrevBlock: prevHash4, + } + + rel = RelateHR(rc3, rc4) + if rel != -1 { + t.Fatal("Should return -1") + } + rel = RelateHR(rc4, rc3) + if rel != 1 { + t.Fatal("Should return 1") + } +} + +func TestBClaimsEqual(t *testing.T) { + height1 := uint32(1) + cid := uint32(1) + bc1 := &BClaims{ + ChainID: cid, + Height: height1, + TxCount: 0, + PrevBlock: crypto.Hasher([]byte("Genesis")), + TxRoot: crypto.Hasher([]byte("")), + StateRoot: crypto.Hasher([]byte("")), + HeaderRoot: crypto.Hasher([]byte("")), + } + bh1 := &BlockHeader{ + BClaims: bc1, + } + + height2 := uint32(2) + bc2 := &BClaims{ + ChainID: cid, + Height: height2, + TxCount: 0, + PrevBlock: crypto.Hasher([]byte("Genesis")), + TxRoot: crypto.Hasher([]byte("")), + StateRoot: crypto.Hasher([]byte("")), + HeaderRoot: crypto.Hasher([]byte("")), + } + bh2 := &BlockHeader{ + BClaims: bc2, + } + + equal, err := BClaimsEqual(bh1, bh2) + if err != nil { + t.Fatal(err) + } + if equal { + t.Fatal("Should not be equal") + } + equal, err = BClaimsEqual(bh1, bh1) + if err != nil { + t.Fatal(err) + } + if !equal { + t.Fatal("Should be equal") + } + + bcBad := &BClaims{} + bhBad := &BlockHeader{ + BClaims: bcBad, + } + _, err = BClaimsEqual(bh1, bhBad) + if err == nil { + t.Fatal("Should raise an error (1)") + } + _, err = BClaimsEqual(bhBad, bh1) + if err == nil { + t.Fatal("Should raise an error (2)") + } +} diff --git a/consensus/objs/v.go b/consensus/objs/v.go index aee32e93..7b0c0e09 100644 --- a/consensus/objs/v.go +++ b/consensus/objs/v.go @@ -4,7 +4,7 @@ import ( mdefs "github.com/MadBase/MadNet/consensus/objs/capn" "github.com/MadBase/MadNet/consensus/objs/validator" "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -31,8 +31,8 @@ func (b *Validator) UnmarshalCapn(bh mdefs.Validator) error { if err != nil { return err } - b.VAddr = gUtils.CopySlice(bh.VAddr()) - b.GroupShare = gUtils.CopySlice(bh.GroupShare()) + b.VAddr = utils.CopySlice(bh.VAddr()) + b.GroupShare = utils.CopySlice(bh.GroupShare()) return nil } diff --git a/consensus/objs/vs.go b/consensus/objs/vs.go index 86655d55..0fa6134e 100644 --- a/consensus/objs/vs.go +++ b/consensus/objs/vs.go @@ -3,11 +3,10 @@ package objs import ( "bytes" - "github.com/MadBase/MadNet/errorz" - mdefs "github.com/MadBase/MadNet/consensus/objs/capn" "github.com/MadBase/MadNet/consensus/objs/vset" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/errorz" + "github.com/MadBase/MadNet/utils" capnp "zombiezen.com/go/capnproto2" ) @@ -41,7 +40,7 @@ func (b *ValidatorSet) UnmarshalCapn(bh mdefs.ValidatorSet) error { return err } b.NotBefore = bh.NotBefore() - b.GroupKey = gUtils.CopySlice(bh.GroupKey()) + b.GroupKey = utils.CopySlice(bh.GroupKey()) valList, err := bh.Validators() if err != nil { return err @@ -136,7 +135,7 @@ func (b *ValidatorSet) GroupShareIdx(groupShare []byte) (bool, int) { } func (b *ValidatorSet) IsVAddrValidator(addr []byte) bool { - addrCopy := gUtils.CopySlice(addr) + addrCopy := utils.CopySlice(addr) return b.ValidatorVAddrSet[string(addrCopy)] } diff --git a/consensus/objs/vskey.go b/consensus/objs/vskey.go index 6afab7d6..9e2feb43 100644 --- a/consensus/objs/vskey.go +++ b/consensus/objs/vskey.go @@ -2,7 +2,7 @@ package objs import ( "github.com/MadBase/MadNet/errorz" - gUtils "github.com/MadBase/MadNet/utils" + "github.com/MadBase/MadNet/utils" ) // ValidatorSetKey ... @@ -20,8 +20,8 @@ func (b *ValidatorSetKey) UnmarshalBinary(data []byte) error { if len(data) != 6 { return errorz.ErrInvalid{}.New("data incorrect length for unmarshalling ValidatorSetKey") } - b.Prefix = gUtils.CopySlice(data[0:2]) - nb, _ := gUtils.UnmarshalUint32(data[2:6]) + b.Prefix = utils.CopySlice(data[0:2]) + nb, _ := utils.UnmarshalUint32(data[2:6]) if nb == 0 { return errorz.ErrInvalid{}.New("invalid NotBefore for unmarshalling ValidatorSetKey; NotBefore == 0") } @@ -36,8 +36,8 @@ func (b *ValidatorSetKey) MarshalBinary() ([]byte, error) { return nil, errorz.ErrInvalid{}.New("not initialized") } key := []byte{} - Prefix := gUtils.CopySlice(b.Prefix) - nb := gUtils.MarshalUint32(b.NotBefore) + Prefix := utils.CopySlice(b.Prefix) + nb := utils.MarshalUint32(b.NotBefore) key = append(key, Prefix...) key = append(key, nb...) return key, nil diff --git a/consensus/request/client.go b/consensus/request/client.go index e52cbd19..7ccc4f83 100644 --- a/consensus/request/client.go +++ b/consensus/request/client.go @@ -6,6 +6,7 @@ import ( "github.com/MadBase/MadNet/consensus/objs" "github.com/MadBase/MadNet/constants" "github.com/MadBase/MadNet/crypto" + "github.com/MadBase/MadNet/dynamics" "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/logging" "github.com/MadBase/MadNet/middleware" @@ -22,14 +23,16 @@ type Client struct { logger *logrus.Logger secpVal *crypto.Secp256k1Validator groupVal *crypto.BNGroupValidator + storage dynamics.StorageGetter } // Init initializes the object -func (rb *Client) Init(client pb.P2PClient) { +func (rb *Client) Init(client pb.P2PClient, storage dynamics.StorageGetter) { rb.logger = logging.GetLogger(constants.LoggerConsensus) rb.client = client rb.groupVal = &crypto.BNGroupValidator{} rb.secpVal = &crypto.Secp256k1Validator{} + rb.storage = storage } // RequestP2PGetSnapShotNode implements the client for the P2P method diff --git a/consensus/request/handlers.go b/consensus/request/handlers.go index c90645ca..670f3f14 100644 --- a/consensus/request/handlers.go +++ b/consensus/request/handlers.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/dynamics" "github.com/MadBase/MadNet/consensus/db" "github.com/MadBase/MadNet/interfaces" @@ -36,10 +37,11 @@ type Handler struct { database *db.Database logger *logrus.Logger app appHandler + storage dynamics.StorageGetter } // Init initializes the object -func (rb *Handler) Init(database *db.Database, app appHandler) { +func (rb *Handler) Init(database *db.Database, app appHandler, storage dynamics.StorageGetter) { rb.logger = logging.GetLogger(constants.LoggerConsensus) background := context.Background() ctx, cf := context.WithCancel(background) @@ -48,6 +50,7 @@ func (rb *Handler) Init(database *db.Database, app appHandler) { rb.wg = sync.WaitGroup{} rb.app = app rb.database = database + rb.storage = storage } // Done will trInger when both of the gossip busses have stopped @@ -111,7 +114,7 @@ func (rb *Handler) HandleP2PGetBlockHeaders(ctx context.Context, r *pb.GetBlockH if err != nil { return err } - if len(hdrbytes)+byteCount < constants.MaxBytes { + if len(hdrbytes)+byteCount < int(rb.storage.GetMaxBytes()) { byteCount = byteCount + len(hdrbytes) hdrs = append(hdrs, hdrbytes) } else { diff --git a/consensus/synchronizer.go b/consensus/synchronizer.go index 69cb335a..40e56502 100644 --- a/consensus/synchronizer.go +++ b/consensus/synchronizer.go @@ -12,6 +12,7 @@ import ( "github.com/MadBase/MadNet/consensus/gossip" "github.com/MadBase/MadNet/consensus/lstate" "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/dynamics" "github.com/MadBase/MadNet/errorz" "github.com/MadBase/MadNet/logging" "github.com/MadBase/MadNet/peering" @@ -211,10 +212,12 @@ type Synchronizer struct { peerMinThresh *remoteVar ethSyncDone *remoteVar madSyncDone *resetVar + + storage dynamics.StorageGetter } // Init initializes the struct -func (s *Synchronizer) Init(cdb *db.Database, mdb *badger.DB, tdb *badger.DB, gc *gossip.Client, gh *gossip.Handlers, ep *evidence.Pool, eng *lstate.Engine, app *application.Application, ah *admin.Handlers, pman *peering.PeerManager) { +func (s *Synchronizer) Init(cdb *db.Database, mdb *badger.DB, tdb *badger.DB, gc *gossip.Client, gh *gossip.Handlers, ep *evidence.Pool, eng *lstate.Engine, app *application.Application, ah *admin.Handlers, pman *peering.PeerManager, storage dynamics.StorageGetter) { s.logger = logging.GetLogger(constants.LoggerConsensus) s.cdb = cdb s.mdb = mdb @@ -234,6 +237,7 @@ func (s *Synchronizer) Init(cdb *db.Database, mdb *badger.DB, tdb *badger.DB, gc s.ethSyncDone = newRemoteVar(s.adminHandler.IsSynchronized) s.peerMinThresh = newRemoteVar(s.peerMan.PeeringComplete) s.madSyncDone = newResetVar() + s.storage = storage } func (s *Synchronizer) CloseChan() <-chan struct{} { @@ -414,7 +418,7 @@ func (s *Synchronizer) adminInteruptLoop() { func (s *Synchronizer) setupLoops() { stateLoopInSyncConfig := newLoopConfig(). withName("StateLoop-InSync"). - withInitialDelay(9*constants.MsgTimeout). + withInitialDelay(9*s.storage.GetMsgTimeout()). withFn2(s.stateHandler.UpdateLocalState, s.madSyncDone.set). withFreq(200 * time.Millisecond). withDelayOnConditionFailure(200 * time.Millisecond). @@ -461,10 +465,10 @@ func (s *Synchronizer) setupLoops() { reGossipLoopConfig := newLoopConfig(). withName("ReGossipLoop"). - withInitialDelay(9 * constants.MsgTimeout). + withInitialDelay(9 * s.storage.GetMsgTimeout()). withFn(s.gossipClient.ReGossip). - withFreq(9 * constants.MsgTimeout). - withDelayOnConditionFailure(constants.MsgTimeout). + withFreq(9 * s.storage.GetMsgTimeout()). + withDelayOnConditionFailure(s.storage.GetMsgTimeout()). withLockFreeCondition(s.isNotClosing). withLockFreeCondition(s.initialized.isSet). withLockFreeCondition(s.ethSyncDone.isSet). diff --git a/constants/consensus.go b/constants/consensus.go index b0ba82fe..8ff719c6 100644 --- a/constants/consensus.go +++ b/constants/consensus.go @@ -6,15 +6,8 @@ import "time" const ( DEADBLOCKROUND uint32 = 5 DEADBLOCKROUNDNR = DEADBLOCKROUND - 1 - MaxBytes = 3000000 - MaxProposalSize = MaxBytes SrvrMsgTimeout = 3 * time.Second // Do not go lower than 2 seconds! MsgTimeout = 4 * time.Second // Do not go lower than 2 seconds! - ProposalStepTO = 4 * time.Second //4 * time.Second - PreVoteStepTO = 3 * time.Second //4 * time.Second - PreCommitStepTO = 3 * time.Second //4 * time.Second - DBRNRTO = 24 * time.Second - DownloadTO = ProposalStepTO + PreVoteStepTO + PreCommitStepTO ) // AdminHandlerKid returns a constant byte slice to be used as Key ID diff --git a/constants/dbprefix/consensus.go b/constants/dbprefix/consensus.go index 8f639027..8c9b0ac8 100644 --- a/constants/dbprefix/consensus.go +++ b/constants/dbprefix/consensus.go @@ -122,6 +122,14 @@ func PrefixStagedBlockHeaderKey() []byte { return []byte("a3") } +func PrefixRawStorageKey() []byte { + return []byte("a4") +} + +func PrefixStorageNodeKey() []byte { + return []byte("a5") +} + func PrefixPendingNodeKeyCount() []byte { return []byte("Ay") } diff --git a/crypto/bn.go b/crypto/bn.go index 9ec1b729..67fd54b5 100644 --- a/crypto/bn.go +++ b/crypto/bn.go @@ -13,24 +13,28 @@ type BNSigner struct { } // SetPrivk sets the private key of the BNSigner. -func (bns *BNSigner) SetPrivk(privk []byte) { +func (bns *BNSigner) SetPrivk(privk []byte) error { + if bns == nil { + return ErrInvalid + } bns.privk = new(big.Int).SetBytes(privk) bns.privk.Mod(bns.privk, bn256.Order) bns.pubk = new(bn256.G2).ScalarBaseMult(bns.privk) + return nil } // Pubkey returns the marshalled public key of the BNSigner. func (bns *BNSigner) Pubkey() ([]byte, error) { - if bns.privk != nil { - return bns.pubk.Marshal(), nil + if bns == nil || bns.privk == nil { + return nil, ErrPrivkNotSet } - return nil, ErrPrivkNotSet + return bns.pubk.Marshal(), nil } // Sign will generate a signature for msg using the private key of the // BNSigner. func (bns *BNSigner) Sign(msg []byte) ([]byte, error) { - if bns.privk == nil { + if bns == nil || bns.privk == nil { return nil, ErrPrivkNotSet } pubkbytes := bns.pubk.Marshal() diff --git a/crypto/bn_test.go b/crypto/bn_test.go index 16e392a3..4408d604 100644 --- a/crypto/bn_test.go +++ b/crypto/bn_test.go @@ -17,7 +17,10 @@ func TestSetPrivK(t *testing.T) { privkBig, _ := new(big.Int).SetString("1234567890", 10) privkBig.Mod(privkBig, cloudflare.Order) privk := privkBig.Bytes() - s.SetPrivk(privk) + err = s.SetPrivk(privk) + if err != nil { + t.Fatal(err) + } } func TestSignAndValidate(t *testing.T) { @@ -29,7 +32,10 @@ func TestSignAndValidate(t *testing.T) { if err != ErrPrivkNotSet { t.Fatal("Should raise private key not set error!") } - s.SetPrivk(Hasher([]byte("secret"))) + err = s.SetPrivk(Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } signature, err := s.Sign(msg) if err != nil { t.Fatal(err) @@ -67,7 +73,10 @@ func TestSignerPubkeyFromSig(t *testing.T) { // Valid signature and validation s := &BNSigner{} - s.SetPrivk(Hasher([]byte("secret"))) + err := s.SetPrivk(Hasher([]byte("secret"))) + if err != nil { + t.Fatal(err) + } signature, err := s.Sign(msg) if err != nil { t.Fatal(err) diff --git a/crypto/bngroup.go b/crypto/bngroup.go index 6a0422db..8935c86c 100644 --- a/crypto/bngroup.go +++ b/crypto/bngroup.go @@ -15,15 +15,22 @@ type BNGroupSigner struct { } // SetPrivk sets the private key of the BNGroupSigner. -func (bns *BNGroupSigner) SetPrivk(privk []byte) { +func (bns *BNGroupSigner) SetPrivk(privk []byte) error { + if bns == nil { + return ErrInvalid + } bns.privk = new(big.Int).SetBytes(privk) bns.privk.Mod(bns.privk, bn256.Order) bns.pubk = new(bn256.G2).ScalarBaseMult(bns.privk) + return nil } // SetGroupPubk will set the public key of the entire group; // this is also called the master public key. func (bns *BNGroupSigner) SetGroupPubk(groupPubk []byte) error { + if bns == nil { + return ErrInvalid + } pubkpoint := new(bn256.G2) _, err := pubkpoint.Unmarshal(groupPubk) if err != nil { @@ -64,25 +71,28 @@ func (bns *BNGroupSigner) VerifyGroupShares(groupShares [][]byte) error { // PubkeyShare returns the marshalled public key of the BNGroupSigner func (bns *BNGroupSigner) PubkeyShare() ([]byte, error) { - if bns.privk != nil { - return bns.pubk.Marshal(), nil + if bns == nil || bns.privk == nil { + return nil, ErrPrivkNotSet } - return nil, ErrPrivkNotSet + return bns.pubk.Marshal(), nil } // PubkeyGroup returns the marshalled public key of the group // (master public key). func (bns *BNGroupSigner) PubkeyGroup() ([]byte, error) { - if bns.groupPubk != nil { - return bns.groupPubk.Marshal(), nil + if bns == nil || bns.groupPubk == nil { + return nil, ErrPubkeyGroupNotSet } - return nil, ErrPubkeyGroupNotSet + return bns.groupPubk.Marshal(), nil } // Sign will generate a signature for msg using the private key of the // BNGroupSigner; this signature can be aggregated to form a valid // group signature. func (bns *BNGroupSigner) Sign(msg []byte) ([]byte, error) { + if bns == nil { + return nil, ErrInvalid + } sigpoint, err := bn256.Sign(msg, bns.privk, bn256.HashToG1) if err != nil { return nil, err @@ -93,6 +103,9 @@ func (bns *BNGroupSigner) Sign(msg []byte) ([]byte, error) { // Aggregate attempts to combine the slice of signatures in sigs into // a group signature. func (bns *BNGroupSigner) Aggregate(sigs [][]byte, groupShares [][]byte) ([]byte, error) { + if bns == nil { + return nil, ErrInvalid + } err := bns.VerifyGroupShares(groupShares) if err != nil { return nil, err diff --git a/crypto/bngroup_test.go b/crypto/bngroup_test.go index 1f03a199..fb7f8e4e 100644 --- a/crypto/bngroup_test.go +++ b/crypto/bngroup_test.go @@ -13,7 +13,10 @@ func TestGroupSignerSetPrivK(t *testing.T) { privkBig, _ := new(big.Int).SetString("1234567890", 10) privkBig.Mod(privkBig, bn256.Order) privk := privkBig.Bytes() - s.SetPrivk(privk) + err := s.SetPrivk(privk) + if err != nil { + t.Fatal(err) + } } func TestGroupSignerSetGroupPubk(t *testing.T) { @@ -48,7 +51,10 @@ func TestGroupSignerPubkeyShare(t *testing.T) { privk := privkBig.Bytes() pubkBN := new(bn256.G2).ScalarBaseMult(privkBig) pubkBNBytes := pubkBN.Marshal() - s.SetPrivk(privk) + err = s.SetPrivk(privk) + if err != nil { + t.Fatal(err) + } pubkBytes, err := s.PubkeyShare() if err != nil { t.Fatal("Error occurred when calling bns.PubkeyShare()") @@ -89,7 +95,10 @@ func TestGroupSignAndValidate(t *testing.T) { s := new(BNGroupSigner) privkBig := big.NewInt(1234567890) privk := privkBig.Bytes() - s.SetPrivk(privk) + err := s.SetPrivk(privk) + if err != nil { + t.Fatal(err) + } sig, err := s.Sign(msg) if err != nil { t.Fatal(err) @@ -131,7 +140,10 @@ func TestGroupSignerPubkeyFromSig(t *testing.T) { s := new(BNGroupSigner) privkBig := big.NewInt(1234567890) privk := privkBig.Bytes() - s.SetPrivk(privk) + err := s.SetPrivk(privk) + if err != nil { + t.Fatal(err) + } sig, err := s.Sign(msg) if err != nil { t.Fatal(err) @@ -314,7 +326,10 @@ func TestGroupSignerAggregate(t *testing.T) { gpk1 := new(bn256.G2).ScalarBaseMult(gsk1) groupShares[0] = gpk1.Marshal() s1 := new(BNGroupSigner) - s1.SetPrivk(gsk1.Bytes()) + err := s1.SetPrivk(gsk1.Bytes()) + if err != nil { + t.Fatal(err) + } sig1, err := s1.Sign(msg) if err != nil { t.Fatal(err) @@ -325,7 +340,10 @@ func TestGroupSignerAggregate(t *testing.T) { gpk2 := new(bn256.G2).ScalarBaseMult(gsk2) groupShares[1] = gpk2.Marshal() s2 := new(BNGroupSigner) - s2.SetPrivk(gsk2.Bytes()) + err = s2.SetPrivk(gsk2.Bytes()) + if err != nil { + t.Fatal(err) + } sig2, err := s2.Sign(msg) if err != nil { t.Fatal(err) @@ -336,7 +354,10 @@ func TestGroupSignerAggregate(t *testing.T) { gpk3 := new(bn256.G2).ScalarBaseMult(gsk3) groupShares[2] = gpk3.Marshal() s3 := new(BNGroupSigner) - s3.SetPrivk(gsk3.Bytes()) + err = s3.SetPrivk(gsk3.Bytes()) + if err != nil { + t.Fatal(err) + } sig3, err := s3.Sign(msg) if err != nil { t.Fatal(err) @@ -347,7 +368,10 @@ func TestGroupSignerAggregate(t *testing.T) { gpk4 := new(bn256.G2).ScalarBaseMult(gsk4) groupShares[3] = gpk4.Marshal() s4 := new(BNGroupSigner) - s4.SetPrivk(gsk4.Bytes()) + err = s4.SetPrivk(gsk4.Bytes()) + if err != nil { + t.Fatal(err) + } sig4, err := s4.Sign(msg) if err != nil { t.Fatal(err) diff --git a/crypto/errors.go b/crypto/errors.go index 39289f19..2428b73d 100644 --- a/crypto/errors.go +++ b/crypto/errors.go @@ -13,6 +13,10 @@ var ( // ErrInvalidSignature occurs when signature validation fails ErrInvalidSignature = errors.New("signature validation failed") + // ErrInvalid occurs when signer is not valid; + // this occurs when signer is not initialized. + ErrInvalid = errors.New("invalid signer") + // ErrInvalidPubkeyShares occurs when multiple copies of the same public // key are contained when attempting to set GroupShares. ErrInvalidPubkeyShares = errors.New("groupShares contains repeated public keys") diff --git a/crypto/secp.go b/crypto/secp.go index 28a495b3..93382bdf 100644 --- a/crypto/secp.go +++ b/crypto/secp.go @@ -17,15 +17,18 @@ type Secp256k1Signer struct { // Pubkey returns the marshalled public key of the Secp256k1Signer // (uncompressed format). func (secps *Secp256k1Signer) Pubkey() ([]byte, error) { - if secps.privk != nil { - return utils.CopySlice(secps.pubk), nil + if secps == nil || secps.privk == nil { + return nil, ErrPrivkNotSet } - return nil, ErrPrivkNotSet + return utils.CopySlice(secps.pubk), nil } // SetPrivk sets the private key of the Secp256k1Signer; // privk is required to be 32 bytes! func (secps *Secp256k1Signer) SetPrivk(privk []byte) error { + if secps == nil { + return ErrInvalid + } ecprivk, err := eth.ToECDSA(privk) if err != nil { return err @@ -40,7 +43,7 @@ func (secps *Secp256k1Signer) SetPrivk(privk []byte) error { // Secp256k1Signer; eth.Sign *assumes* we are signing the // *hash of the message* (digestHash) and *not* the message itself. func (secps *Secp256k1Signer) Sign(msg []byte) ([]byte, error) { - if secps.privk == nil { + if secps == nil || secps.privk == nil { return nil, ErrPrivkNotSet } digestHash := Hasher(msg) diff --git a/dynamics/consensus.go b/dynamics/consensus.go new file mode 100644 index 00000000..2d6a1c04 --- /dev/null +++ b/dynamics/consensus.go @@ -0,0 +1,18 @@ +package dynamics + +import ( + "time" +) + +// Original from constants/consensus.go +const ( + maxBytes = 3000000 + maxProposalSize = maxBytes // Parameterize: equal to maxBytes + msgTimeout = 4 * time.Second + srvrMsgTimeout = (3 * msgTimeout) / 4 // Parameterize: 0.75*MsgTimeout + proposalStepTO = 4 * time.Second + preVoteStepTO = 3 * time.Second + preCommitStepTO = 3 * time.Second + dBRNRTO = (5 * (proposalStepTO + preVoteStepTO + preCommitStepTO)) / 2 // Parameterize: make 2.5 times Prop, PV, PC timeouts + downloadTO = proposalStepTO + preVoteStepTO + preCommitStepTO // Parameterize: sum of Prop, PV, PC timeouts +) diff --git a/dynamics/db.go b/dynamics/db.go new file mode 100644 index 00000000..d57045b5 --- /dev/null +++ b/dynamics/db.go @@ -0,0 +1,108 @@ +package dynamics + +import ( + "sync" + + "github.com/dgraph-io/badger/v2" + "github.com/sirupsen/logrus" +) + +// Database is an abstraction for object storage +type Database struct { + sync.Mutex + rawDB rawDataBase + logger *logrus.Logger +} + +// SetNode stores Node in the database +func (db *Database) SetNode(txn *badger.Txn, node *Node) error { + if !node.IsValid() { + return ErrInvalidNode + } + nodeKey, err := makeNodeKey(node.thisEpoch) + if err != nil { + return err + } + key, err := nodeKey.Marshal() + if err != nil { + return err + } + nodeBytes, err := node.Marshal() + if err != nil { + return err + } + err = db.rawDB.SetValue(txn, key, nodeBytes) + if err != nil { + return err + } + return nil +} + +// GetNode retrieves Node from the database +func (db *Database) GetNode(txn *badger.Txn, epoch uint32) (*Node, error) { + nodeKey, err := makeNodeKey(epoch) + if err != nil { + return nil, err + } + key, err := nodeKey.Marshal() + if err != nil { + return nil, err + } + v, err := db.rawDB.GetValue(txn, key) + if err != nil { + return nil, err + } + node := &Node{} + err = node.Unmarshal(v) + if err != nil { + return nil, err + } + if !node.IsValid() { + return nil, ErrInvalidNode + } + return node, nil +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// SetLinkedList saves LinkedList to the database +func (db *Database) SetLinkedList(txn *badger.Txn, ll *LinkedList) error { + if !ll.IsValid() { + return ErrInvalid + } + value := ll.Marshal() + llKey := makeLinkedListKey() + key, err := llKey.Marshal() + if err != nil { + return err + } + err = db.rawDB.SetValue(txn, key, value) + if err != nil { + return err + } + return nil +} + +// GetLinkedList retrieves LinkedList from the database +func (db *Database) GetLinkedList(txn *badger.Txn) (*LinkedList, error) { + llKey := makeLinkedListKey() + key, err := llKey.Marshal() + if err != nil { + return nil, err + } + v, err := db.rawDB.GetValue(txn, key) + if err != nil { + return nil, err + } + ll := &LinkedList{} + err = ll.Unmarshal(v) + if err != nil { + return nil, err + } + if !ll.IsValid() { + return nil, ErrInvalid + } + return ll, nil +} diff --git a/dynamics/db_test.go b/dynamics/db_test.go new file mode 100644 index 00000000..4086cbf6 --- /dev/null +++ b/dynamics/db_test.go @@ -0,0 +1,166 @@ +package dynamics + +import ( + "bytes" + "testing" + + "github.com/dgraph-io/badger/v2" + "github.com/sirupsen/logrus" +) + +type mockRawDB struct { + rawDB map[string]string +} + +func (m *mockRawDB) GetValue(txn *badger.Txn, key []byte) ([]byte, error) { + strKey := string(key) + strValue, ok := m.rawDB[strKey] + if !ok { + return nil, ErrKeyNotPresent + } + value := []byte(strValue) + return value, nil +} + +func (m *mockRawDB) SetValue(txn *badger.Txn, key []byte, value []byte) error { + strKey := string(key) + strValue := string(value) + m.rawDB[strKey] = strValue + return nil +} + +func (m *mockRawDB) DeleteValue(key []byte) error { + strKey := string(key) + _, ok := m.rawDB[strKey] + if !ok { + return ErrKeyNotPresent + } + delete(m.rawDB, strKey) + return nil +} + +func (m *mockRawDB) View(fn func(txn *badger.Txn) error) error { + return fn(nil) +} + +func (m *mockRawDB) Update(fn func(txn *badger.Txn) error) error { + return fn(nil) +} + +func TestMock(t *testing.T) { + key := []byte("Key") + value := []byte("Key") + + m := &mockRawDB{} + m.rawDB = make(map[string]string) + + _, err := m.GetValue(nil, key) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + err = m.SetValue(nil, key, value) + if err != nil { + t.Fatal(err) + } + + retValue, err := m.GetValue(nil, key) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(retValue, value) { + t.Fatal("values do not match") + } + + err = m.DeleteValue(key) + if err != nil { + t.Fatal(err) + } + _, err = m.GetValue(nil, key) + if err == nil { + t.Fatal("Should have raised error (2)") + } +} + +func newLogger() *logrus.Logger { + logger := logrus.New() + return logger +} + +func initializeDB() *Database { + logger := newLogger() + db := &Database{} + db.logger = logger + mock := &mockRawDB{} + mock.rawDB = make(map[string]string) + db.rawDB = mock + return db +} + +func TestGetSetNode(t *testing.T) { + db := initializeDB() + + node := &Node{} + err := db.SetNode(nil, node) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + node.prevEpoch = 1 + node.thisEpoch = 1 + node.nextEpoch = 1 + node.rawStorage = &RawStorage{} + err = db.SetNode(nil, node) + if err != nil { + t.Fatal(err) + } + nodeBytes, err := node.Marshal() + if err != nil { + t.Fatal(err) + } + + // Should raise error + _, err = db.GetNode(nil, 0) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + node2, err := db.GetNode(nil, 1) + if err != nil { + t.Fatal(err) + } + node2Bytes, err := node2.Marshal() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(nodeBytes, node2Bytes) { + t.Fatal("nodes do not match") + } +} + +func TestGetSetLinkedList(t *testing.T) { + db := initializeDB() + + ll := &LinkedList{} + err := db.SetLinkedList(nil, ll) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + ll.epochLastUpdated = 1 + err = db.SetLinkedList(nil, ll) + if err != nil { + t.Fatal(err) + } + llBytes := ll.Marshal() + + ll2, err := db.GetLinkedList(nil) + if err != nil { + t.Fatal(err) + } + ll2Bytes := ll2.Marshal() + if !bytes.Equal(llBytes, ll2Bytes) { + t.Fatal("LinkedLists do not match") + } +} diff --git a/dynamics/errors.go b/dynamics/errors.go new file mode 100644 index 00000000..cb1c5545 --- /dev/null +++ b/dynamics/errors.go @@ -0,0 +1,41 @@ +package dynamics + +import ( + "errors" + + "github.com/dgraph-io/badger/v2" +) + +var ( + // ErrRawStorageNilPointer is an error which results from a + // RawStorage struct which has not been initialized. + ErrRawStorageNilPointer = errors.New("invalid RawStorage: nil pointer") + + // ErrZeroEpoch is an error which is raised whenever the epoch is given + // as zero; there is no zero epoch. + ErrZeroEpoch = errors.New("invalid epoch: no zero epoch") + + // ErrUnmarshalEmpty is an error which is raised whenever attempting + // to unmarshal an empty byte slice. + ErrUnmarshalEmpty = errors.New("invalid: attempting to unmarshal empty byte slice") + + // ErrKeyNotPresent is an error which is raised when a key is not present + // in the database. + ErrKeyNotPresent = badger.ErrKeyNotFound + + // ErrInvalidUpdateValue is an error which is returned when the data + // for updating rawStorage is invalid. + ErrInvalidUpdateValue = errors.New("invalid update value for storage") + + // ErrInvalidValue is an error which is returned when the value is invalid. + ErrInvalidValue = errors.New("invalid value") + + // ErrInvalid is an error which is returned when the struct is invalid. + ErrInvalid = errors.New("invalid value") + + // ErrInvalidNodeKey is an error which occurs when the NodeKey is invalid + ErrInvalidNodeKey = errors.New("invalid NodeKey") + + // ErrInvalidNode is an error which occurs when a Node is invalid + ErrInvalidNode = errors.New("invalid Node") +) diff --git a/dynamics/linkedlist.go b/dynamics/linkedlist.go new file mode 100644 index 00000000..e705c777 --- /dev/null +++ b/dynamics/linkedlist.go @@ -0,0 +1,81 @@ +package dynamics + +import ( + "github.com/MadBase/MadNet/constants/dbprefix" + "github.com/MadBase/MadNet/utils" +) + +// LinkedList is a doubly linked list which will store nodes corresponding +// to changes to dynamic parameters. +// We store the largest epoch which has been updated. +type LinkedList struct { + epochLastUpdated uint32 +} + +func makeLinkedListKey() *NodeKey { + nk := &NodeKey{ + prefix: dbprefix.PrefixStorageNodeKey(), + epoch: 0, + } + return nk +} + +// GetEpochLastUpdated returns highest epoch with changes +func (ll *LinkedList) GetEpochLastUpdated() uint32 { + return ll.epochLastUpdated +} + +// SetEpochLastUpdated returns highest epoch with changes +func (ll *LinkedList) SetEpochLastUpdated(epoch uint32) error { + if epoch == 0 { + return ErrZeroEpoch + } + ll.epochLastUpdated = epoch + return nil +} + +// Marshal marshals LinkedList +func (ll *LinkedList) Marshal() []byte { + eluBytes := utils.MarshalUint32(ll.epochLastUpdated) + return eluBytes +} + +// Unmarshal unmarshals LinkedList +func (ll *LinkedList) Unmarshal(v []byte) error { + if len(v) != 4 { + return ErrInvalidNode + } + elu, _ := utils.UnmarshalUint32(v[0:4]) + ll.epochLastUpdated = elu + return nil +} + +// IsValid returns true if LinkedList is valid +func (ll *LinkedList) IsValid() bool { + if ll.epochLastUpdated == 0 { + return false + } + return true +} + +// CreateLinkedList creates the first node in a LinkedList. +// These values can then be stored in the database. +func CreateLinkedList(epoch uint32, rs *RawStorage) (*Node, *LinkedList, error) { + if epoch == 0 { + return nil, nil, ErrZeroEpoch + } + rsCopy, err := rs.Copy() + if err != nil { + return nil, nil, err + } + node := &Node{ + thisEpoch: epoch, + prevEpoch: epoch, + nextEpoch: epoch, + rawStorage: rsCopy, + } + linkedList := &LinkedList{ + epochLastUpdated: epoch, + } + return node, linkedList, nil +} diff --git a/dynamics/linkedlist_test.go b/dynamics/linkedlist_test.go new file mode 100644 index 00000000..4630f568 --- /dev/null +++ b/dynamics/linkedlist_test.go @@ -0,0 +1,120 @@ +package dynamics + +import ( + "bytes" + "testing" + + "github.com/MadBase/MadNet/constants" + "github.com/MadBase/MadNet/constants/dbprefix" +) + +func TestLinkedListMakeKeys(t *testing.T) { + llk := makeLinkedListKey() + if llk.epoch != 0 { + t.Fatal("epoch should be 0") + } + if !bytes.Equal(llk.prefix, dbprefix.PrefixStorageNodeKey()) { + t.Fatal("prefixes do not match") + } + llkBytes, err := llk.Marshal() + if err != nil { + t.Fatal(err) + } + llkTrue := []byte{} + llkTrue = append(llkTrue, dbprefix.PrefixStorageNodeKey()...) + llkTrue = append(llkTrue, 0, 0, 0, 0) + if !bytes.Equal(llkBytes, llkTrue) { + t.Fatal("marshalled bytes do not match") + } +} + +func TestLinkedListMarshal(t *testing.T) { + ll := &LinkedList{} + if ll.IsValid() { + t.Fatal("Should not have valid LinkedList") + } + + invalidBytes := []byte{0, 1, 2, 3, 4} + err := ll.Unmarshal(invalidBytes) + if err == nil { + t.Fatal("Should have raised error (2)") + } + + invalidBytes2 := make([]byte, 4) + err = ll.Unmarshal(invalidBytes2) + if err != nil { + t.Fatal(err) + } + if ll.epochLastUpdated != 0 { + t.Fatal("Should have raised error (3)") + } + + v := []byte{255, 255, 255, 255} + err = ll.Unmarshal(v) + if err != nil { + t.Fatal(err) + } + if ll.epochLastUpdated != constants.MaxUint32 { + t.Fatal("Invalid LinkedList (1)") + } + + retBytes := ll.Marshal() + if !bytes.Equal(retBytes, v) { + t.Fatal("invalid marshalled bytes") + } +} + +func TestLinkedListGetSet(t *testing.T) { + ll := &LinkedList{} + err := ll.SetEpochLastUpdated(0) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + elu := uint32(123456) + err = ll.SetEpochLastUpdated(elu) + if err != nil { + t.Fatal(err) + } + retElu := ll.GetEpochLastUpdated() + if retElu != elu { + t.Fatal("Invalid EpochLastUpdated") + } + + if !ll.IsValid() { + t.Fatal("LinkedList should be valid") + } +} + +func TestCreateLinkedList(t *testing.T) { + epoch := uint32(0) + _, _, err := CreateLinkedList(epoch, nil) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + epoch = 1 + _, _, err = CreateLinkedList(epoch, nil) + if err == nil { + t.Fatal("Should have raised error (2)") + } + + rs := &RawStorage{} + rs.standardParameters() + node, linkedlist, err := CreateLinkedList(epoch, rs) + if err != nil { + t.Fatal(err) + } + if node.thisEpoch != epoch { + t.Fatal("invalid thisEpoch") + } + if node.prevEpoch != epoch { + t.Fatal("invalid prevEpoch") + } + if node.nextEpoch != epoch { + t.Fatal("invalid nextEpoch") + } + if linkedlist.epochLastUpdated != epoch { + t.Fatal("invalid epochLastUpdated") + } +} diff --git a/dynamics/node.go b/dynamics/node.go new file mode 100644 index 00000000..b01a1315 --- /dev/null +++ b/dynamics/node.go @@ -0,0 +1,157 @@ +package dynamics + +import ( + "github.com/MadBase/MadNet/utils" +) + +// Node contains necessary information about RawStorage; +// it also points to the epoch of the previous node and next node +// in the doubly linked list. +type Node struct { + thisEpoch uint32 + prevEpoch uint32 + nextEpoch uint32 + rawStorage *RawStorage +} + +// Marshal marshals a Node +func (n *Node) Marshal() ([]byte, error) { + rsBytes, err := n.rawStorage.Marshal() + if err != nil { + return nil, err + } + teBytes := utils.MarshalUint32(n.thisEpoch) + peBytes := utils.MarshalUint32(n.prevEpoch) + neBytes := utils.MarshalUint32(n.nextEpoch) + v := []byte{} + v = append(v, teBytes...) + v = append(v, peBytes...) + v = append(v, neBytes...) + v = append(v, rsBytes...) + return v, nil +} + +// Unmarshal unmarshals a Node +func (n *Node) Unmarshal(v []byte) error { + if len(v) < 12 { + return ErrInvalid + } + thisEpoch, _ := utils.UnmarshalUint32(v[0:4]) + prevEpoch, _ := utils.UnmarshalUint32(v[4:8]) + nextEpoch, _ := utils.UnmarshalUint32(v[8:12]) + n.thisEpoch = thisEpoch + n.prevEpoch = prevEpoch + n.nextEpoch = nextEpoch + n.rawStorage = &RawStorage{} + err := n.rawStorage.Unmarshal(v[12:]) + if err != nil { + return err + } + return nil +} + +// IsValid returns true if Node is valid +func (n *Node) IsValid() bool { + if n == nil { + return false + } + if n.thisEpoch == 0 || n.prevEpoch == 0 || n.nextEpoch == 0 { + // node has not set values; invalid + return false + } + if n.prevEpoch > n.thisEpoch || n.thisEpoch > n.nextEpoch { + // node has not been correctly defined; invalid + return false + } + if !n.rawStorage.IsValid() { + return false + } + return true +} + +// IsPreValid returns true if Node is ready to be added to database +// but prevEpoch and nextEpoch are not yet set +func (n *Node) IsPreValid() bool { + if n == nil { + return false + } + if n.thisEpoch == 0 || n.prevEpoch != 0 || n.nextEpoch != 0 { + // Only thisEpoch should be set; invalid + return false + } + if !n.rawStorage.IsValid() { + return false + } + return true +} + +// Copy makes a copy of Node +func (n *Node) Copy() (*Node, error) { + nodeBytes, err := n.Marshal() + if err != nil { + return nil, err + } + nodeCopy := &Node{} + err = nodeCopy.Unmarshal(nodeBytes) + if err != nil { + return nil, err + } + return nodeCopy, nil +} + +// SetEpochs sets n.prevEpoch and n.nextEpoch. +func (n *Node) SetEpochs(prevNode *Node, nextNode *Node) error { + if !n.IsPreValid() { + return ErrInvalid + } + if prevNode.IsValid() && nextNode.IsValid() && prevNode.thisEpoch < n.thisEpoch && n.thisEpoch < nextNode.thisEpoch { + // In this setting, we want to add a new node in between prevNode and nextNode + // + // Update prevNode; + // must point forward to n + prevNode.nextEpoch = n.thisEpoch + // Update epochs for n; + // must point backward to prevNode and forward to nextNode + n.prevEpoch = prevNode.thisEpoch + n.nextEpoch = nextNode.thisEpoch + // Update nextNode; + // must point backward to n + nextNode.prevEpoch = n.thisEpoch + return nil + } + if prevNode.IsValid() && nextNode == nil && prevNode.thisEpoch < n.thisEpoch && prevNode.IsHead() { + // n is the new Head + // Update prevNode.nextEpoch + prevNode.nextEpoch = n.thisEpoch + // Update epochs for n; + // must point backward to prevNode and forward to self + n.prevEpoch = prevNode.thisEpoch + n.nextEpoch = n.thisEpoch + return nil + } + return ErrInvalid +} + +// IsHead returns true if Node is end of linked list; +// in this case, n.nextEpoch == n.thisEpoch +func (n *Node) IsHead() bool { + if !n.IsValid() { + return false + } + if n.thisEpoch == n.nextEpoch { + return true + } + return false +} + +// IsTail returns true if Node is beginning of linked list; +// in this case, n.prevEpoch == n.thisEpoch +func (n *Node) IsTail() bool { + if !n.IsValid() { + return false + } + if n.thisEpoch == n.prevEpoch { + return true + } + return false +} diff --git a/dynamics/node_test.go b/dynamics/node_test.go new file mode 100644 index 00000000..e2cfc8e4 --- /dev/null +++ b/dynamics/node_test.go @@ -0,0 +1,584 @@ +package dynamics + +import ( + "bytes" + "testing" +) + +func TestNodeMarshal(t *testing.T) { + node := &Node{} + _, err := node.Marshal() + if err == nil { + t.Fatal("Should have raied error (1)") + } + + epoch := uint32(1) + rs := &RawStorage{} + rs.standardParameters() + node, _, err = CreateLinkedList(epoch, rs) + if err != nil { + t.Fatal(err) + } + + nodeBytes, err := node.Marshal() + if err != nil { + t.Fatal(err) + } + node2 := &Node{} + err = node2.Unmarshal(nodeBytes) + if err != nil { + t.Fatal(err) + } + if node.thisEpoch != node2.thisEpoch { + t.Fatal("invalid thisEpoch") + } + if node.prevEpoch != node2.prevEpoch { + t.Fatal("invalid prevEpoch") + } + if node.nextEpoch != node2.nextEpoch { + t.Fatal("invalid nextEpoch") + } + rsBytes, err := node.rawStorage.Marshal() + if err != nil { + t.Fatal(err) + } + rs2Bytes, err := node2.rawStorage.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(rsBytes, rs2Bytes) { + t.Fatal("invalid RawStroage") + } + + v := []byte{} + err = node.Unmarshal(v) + if err == nil { + t.Fatal("Should have raised error (2)") + } + + v = make([]byte, 12) + err = node.Unmarshal(v) + if err == nil { + t.Fatal("Should have raised error (3)") + } + + v = make([]byte, 13) + err = node.Unmarshal(v) + if err == nil { + t.Fatal("Should have raised error (4)") + } +} + +func TestNodeCopy(t *testing.T) { + n := &Node{} + _, err := n.Copy() + if err == nil { + t.Fatal("Should have raised error (1)") + } + + n.prevEpoch = 1 + n.thisEpoch = 1 + n.nextEpoch = 1 + n.rawStorage = &RawStorage{} + n2, err := n.Copy() + if err != nil { + t.Fatal(err) + } + + nBytes, err := n.Marshal() + if err != nil { + t.Fatal(err) + } + n2Bytes, err := n2.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(nBytes, n2Bytes) { + t.Fatal("nodes do not match") + } +} + +type wNode struct { + node *Node +} + +func TestNodeIsValid(t *testing.T) { + wNode := &wNode{} + if wNode.node.IsValid() { + t.Fatal("Node should not be valid (0)") + } + + node := &Node{} + if node.IsValid() { + t.Fatal("Node should not be valid (1)") + } + + node.prevEpoch = 3 + node.thisEpoch = 2 + node.nextEpoch = 3 + if node.IsValid() { + t.Fatal("Node should not be valid (2)") + } + + node.prevEpoch = 1 + node.thisEpoch = 3 + node.nextEpoch = 2 + if node.IsValid() { + t.Fatal("Node should not be valid (3)") + } + + node.prevEpoch = 1 + node.thisEpoch = 2 + node.nextEpoch = 3 + if node.IsValid() { + t.Fatal("Node should not be valid (4)") + } + + node.rawStorage = &RawStorage{} + if !node.IsValid() { + t.Fatal("Node should be valid") + } +} + +func TestNodeIsPreValid(t *testing.T) { + wNode := &wNode{} + if wNode.node.IsPreValid() { + t.Fatal("Node should not be prevalid (0)") + } + + node := &Node{} + if node.IsPreValid() { + t.Fatal("Node should not be prevalid (1)") + } + + node.prevEpoch = 0 + node.thisEpoch = 0 + node.nextEpoch = 0 + if node.IsPreValid() { + t.Fatal("Node should not be prevalid (2)") + } + + node.prevEpoch = 1 + node.thisEpoch = 1 + node.nextEpoch = 0 + if node.IsPreValid() { + t.Fatal("Node should not be prevalid (3)") + } + + node.prevEpoch = 0 + node.thisEpoch = 1 + node.nextEpoch = 1 + if node.IsPreValid() { + t.Fatal("Node should not be prevalid (4)") + } + + node.prevEpoch = 0 + node.thisEpoch = 1 + node.nextEpoch = 0 + if node.IsPreValid() { + t.Fatal("Node should not be prevalid (5)") + } + + node.rawStorage = &RawStorage{} + if !node.IsPreValid() { + t.Fatal("Node should be prevalid") + } +} + +func TestNodeIsHead(t *testing.T) { + node := &Node{} + if node.IsHead() { + t.Fatal("Node invalid; should be false") + } + + node.prevEpoch = 1 + node.thisEpoch = 1 + node.nextEpoch = 1 + node.rawStorage = &RawStorage{} + if !node.IsHead() { + t.Fatal("Should be Head") + } + + node.nextEpoch = 2 + if node.IsHead() { + t.Fatal("Should not be Head") + } +} + +func TestNodeIsTail(t *testing.T) { + node := &Node{} + if node.IsTail() { + t.Fatal("Node invalid; should be false") + } + + node.prevEpoch = 1 + node.thisEpoch = 1 + node.nextEpoch = 1 + node.rawStorage = &RawStorage{} + if !node.IsTail() { + t.Fatal("Should be Tail") + } + + node.thisEpoch = 2 + node.nextEpoch = 2 + if node.IsTail() { + t.Fatal("Should not be Tail") + } +} + +// SetNode with prevNode at Head +func TestNodeSetEpochsGood1(t *testing.T) { + rs := &RawStorage{} + rs.standardParameters() + nodeEpoch := uint32(25519) + node := &Node{ + prevEpoch: 0, + thisEpoch: nodeEpoch, + nextEpoch: 0, + rawStorage: rs, + } + if !node.IsPreValid() { + t.Fatal("node should be preValid") + } + rsNew, err := rs.Copy() + if err != nil { + t.Fatal(err) + } + rsNew.MaxBytes = 1234567890 + first := uint32(1) + last := uint32(257) + prevEpoch := last + prevNode := &Node{ + prevEpoch: first, + thisEpoch: prevEpoch, + nextEpoch: last, + rawStorage: rsNew, + } + if !prevNode.IsValid() { + t.Fatal("prevNode should be Valid") + } + if prevNode.thisEpoch >= node.thisEpoch { + t.Fatal("Should have prevNode.thisEpoch < node.thisEpoch") + } + err = node.SetEpochs(prevNode, nil) + if err != nil { + t.Fatal(err) + } + + // Now need to confirm all epochs are good. + if prevNode.prevEpoch != first { + t.Fatal("prevNode.prevEpoch is incorrect") + } + if prevNode.thisEpoch != prevEpoch { + t.Fatal("prevNode.thisEpoch is incorrect") + } + if prevNode.nextEpoch != nodeEpoch { + t.Fatal("prevNode.nextEpoch is incorrect; it does not point to new nodeEpoch") + } + if node.prevEpoch != prevEpoch { + t.Fatal("prevNode.prevEpoch is incorrect; it does not equal prevEpoch") + } + if node.thisEpoch != nodeEpoch { + t.Fatal("node.thisEpoch is incorrect") + } + if node.nextEpoch != nodeEpoch { + t.Fatal("node.nextEpoch is incorrect; it does not point to self") + } +} + +// SetNode in between prevNode and nextNode +func TestNodeSetEpochsGood2(t *testing.T) { + rs := &RawStorage{} + rs.standardParameters() + nodeEpoch := uint32(25519) + node := &Node{ + prevEpoch: 0, + thisEpoch: nodeEpoch, + nextEpoch: 0, + rawStorage: rs, + } + if !node.IsPreValid() { + t.Fatal("node should be preValid") + } + + rsNew, err := rs.Copy() + if err != nil { + t.Fatal(err) + } + rsNew.MaxBytes = 1234567890 + + first := uint32(1) + last := uint32(1234567890) + prevNode := &Node{ + prevEpoch: first, + thisEpoch: first, + nextEpoch: last, + rawStorage: rsNew, + } + if !prevNode.IsValid() { + t.Fatal("prevNode should be Valid") + } + if node.thisEpoch < prevNode.thisEpoch { + t.Fatal("Should have node.thisEpoch < nextNode.thisEpoch") + } + + nextNode := &Node{ + prevEpoch: first, + thisEpoch: last, + nextEpoch: last, + rawStorage: rsNew, + } + if !nextNode.IsValid() { + t.Fatal("nextNode should be Valid") + } + if node.thisEpoch >= nextNode.thisEpoch { + t.Fatal("Should have node.thisEpoch < nextNode.thisEpoch") + } + + err = node.SetEpochs(prevNode, nextNode) + if err != nil { + t.Fatal(err) + } + + // Now need to confirm all epochs are good. + if prevNode.prevEpoch != first { + t.Fatal("nextNode.prevEpoch is incorrect") + } + if prevNode.thisEpoch != first { + t.Fatal("nextNode.thisEpoch is incorrect") + } + if prevNode.nextEpoch != nodeEpoch { + t.Fatal("nextNode.nextEpoch is incorrect") + } + + if node.prevEpoch != first { + t.Fatal("node.prevEpoch is incorrect") + } + if node.thisEpoch != nodeEpoch { + t.Fatal("node.thisEpoch is incorrect") + } + if node.nextEpoch != last { + t.Fatal("node.nextEpoch is incorrect") + } + + if nextNode.prevEpoch != nodeEpoch { + t.Fatal("nextNode.prevEpoch is incorrect") + } + if nextNode.thisEpoch != last { + t.Fatal("nextNode.thisEpoch is incorrect") + } + if nextNode.nextEpoch != last { + t.Fatal("nextNode.nextEpoch is incorrect") + } +} + +// We should raise an error when having node not PreValid +func TestNodeSetEpochsBad1(t *testing.T) { + node := &Node{} + err := node.SetEpochs(nil, nil) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// We should raise error for prevNode being invalid +func TestNodeSetEpochsBad2(t *testing.T) { + rs := &RawStorage{} + rs.standardParameters() + node := &Node{ + prevEpoch: 0, + thisEpoch: 25519, + nextEpoch: 0, + rawStorage: rs, + } + if !node.IsPreValid() { + t.Fatal("node should be preValid") + } + prevNode := &Node{} + if prevNode.IsValid() { + t.Fatal("prevNode should not be valid") + } + err := node.SetEpochs(prevNode, nil) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// We should raise error for nextNode not nil +func TestNodeSetEpochsBad3(t *testing.T) { + rs := &RawStorage{} + rs.standardParameters() + node := &Node{ + prevEpoch: 0, + thisEpoch: 25519, + nextEpoch: 0, + rawStorage: rs, + } + if !node.IsPreValid() { + t.Fatal("node should be preValid") + } + prevNode := &Node{ + prevEpoch: 1, + thisEpoch: 257, + nextEpoch: 123456789, + rawStorage: rs, + } + if !prevNode.IsValid() { + t.Fatal("prevNode should be Valid") + } + nextNode := &Node{} + if nextNode == nil { + t.Fatal("nextNode should not be nil") + } + err := node.SetEpochs(prevNode, nextNode) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// We should raise error for prevNode.thisEpoch >= node.thisEpoch +func TestNodeSetEpochsBad4(t *testing.T) { + rs := &RawStorage{} + rs.standardParameters() + node := &Node{ + prevEpoch: 0, + thisEpoch: 25519, + nextEpoch: 0, + rawStorage: rs, + } + if !node.IsPreValid() { + t.Fatal("node should be preValid") + } + prevNode := &Node{ + prevEpoch: 1, + thisEpoch: 25519, + nextEpoch: 123456789, + rawStorage: rs, + } + if !prevNode.IsValid() { + t.Fatal("prevNode should be Valid") + } + if prevNode.thisEpoch < node.thisEpoch { + t.Fatal("Should have prevNode.thisEpoch >= node.thisEpoch") + } + err := node.SetEpochs(prevNode, nil) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// We should raise error for prevNode not nil +func TestNodeSetEpochsBad5(t *testing.T) { + rs := &RawStorage{} + rs.standardParameters() + node := &Node{ + prevEpoch: 0, + thisEpoch: 25519, + nextEpoch: 0, + rawStorage: rs, + } + if !node.IsPreValid() { + t.Fatal("node should be preValid") + } + prevNode := &Node{} + if prevNode.IsValid() { + t.Fatal("prevNode should not be valid") + } + if prevNode == nil { + t.Fatal("prevNode should not be nil") + } + nextNode := &Node{} + err := node.SetEpochs(prevNode, nextNode) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// We should raise error for prevNode not nil +func TestNodeSetEpochsBad6(t *testing.T) { + rs := &RawStorage{} + rs.standardParameters() + node := &Node{ + prevEpoch: 0, + thisEpoch: 25519, + nextEpoch: 0, + rawStorage: rs, + } + nextNode := &Node{} + if nextNode.IsValid() { + t.Fatal("nextNode should not be valid") + } + err := node.SetEpochs(nil, nextNode) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// We should raise error for prevNode not nil +func TestNodeSetEpochsBad7(t *testing.T) { + rs := &RawStorage{} + rs.standardParameters() + node := &Node{ + prevEpoch: 0, + thisEpoch: 25519, + nextEpoch: 0, + rawStorage: rs, + } + rsNew, err := rs.Copy() + if err != nil { + t.Fatal(err) + } + rsNew.MaxBytes = 1234567890 + nextNode := &Node{ + prevEpoch: 1, + thisEpoch: 257, + nextEpoch: 123456789, + rawStorage: rsNew, + } + if !nextNode.IsValid() { + t.Fatal("nextNode should not be valid") + } + if node.thisEpoch < nextNode.thisEpoch { + t.Fatal("We should not have node.thisEpoch >= nextNode.thisEpoch to raise error") + } + err = node.SetEpochs(nil, nextNode) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// We should raise error for prevNode not nil +func TestNodeSetEpochsBad8(t *testing.T) { + rs := &RawStorage{} + rs.standardParameters() + node := &Node{ + prevEpoch: 0, + thisEpoch: 25519, + nextEpoch: 0, + rawStorage: rs, + } + rsNew, err := rs.Copy() + if err != nil { + t.Fatal(err) + } + rsNew.MaxBytes = 1234567890 + nextNode := &Node{ + prevEpoch: 1, + thisEpoch: 123456, + nextEpoch: 123456789, + rawStorage: rsNew, + } + if !nextNode.IsValid() { + t.Fatal("nextNode should not be valid") + } + if node.thisEpoch >= nextNode.thisEpoch { + t.Fatal("We should have node.thisEpoch >= nextNode.thisEpoch") + } + if nextNode.IsTail() { + t.Fatal("We should not have nextNode is tail to raise error") + } + err = node.SetEpochs(nil, nextNode) + if err == nil { + t.Fatal("Should have raised error") + } +} diff --git a/dynamics/nodekey.go b/dynamics/nodekey.go new file mode 100644 index 00000000..34e1388f --- /dev/null +++ b/dynamics/nodekey.go @@ -0,0 +1,37 @@ +package dynamics + +import ( + "bytes" + + "github.com/MadBase/MadNet/constants/dbprefix" + "github.com/MadBase/MadNet/utils" +) + +// NodeKey stores the necessary information to load a Node +type NodeKey struct { + prefix []byte + epoch uint32 +} + +func makeNodeKey(epoch uint32) (*NodeKey, error) { + if epoch == 0 { + return nil, ErrZeroEpoch + } + nk := &NodeKey{ + prefix: dbprefix.PrefixStorageNodeKey(), + epoch: epoch, + } + return nk, nil +} + +// Marshal converts NodeKey into the byte slice +func (nk *NodeKey) Marshal() ([]byte, error) { + if !bytes.Equal(nk.prefix, dbprefix.PrefixStorageNodeKey()) { + return nil, ErrInvalidNodeKey + } + epochBytes := utils.MarshalUint32(nk.epoch) + key := []byte{} + key = append(key, nk.prefix...) + key = append(key, epochBytes...) + return key, nil +} diff --git a/dynamics/nodekey_test.go b/dynamics/nodekey_test.go new file mode 100644 index 00000000..c2b5bc28 --- /dev/null +++ b/dynamics/nodekey_test.go @@ -0,0 +1,55 @@ +package dynamics + +import ( + "bytes" + "errors" + "testing" + + "github.com/MadBase/MadNet/constants/dbprefix" + "github.com/MadBase/MadNet/utils" +) + +func TestNodeMakeKeys(t *testing.T) { + epoch := uint32(0) + _, err := makeNodeKey(epoch) + if !errors.Is(err, ErrZeroEpoch) { + t.Fatal("Should have returned error for zero epoch") + } + + nk := &NodeKey{} + _, err = nk.Marshal() + if err == nil { + t.Fatal("Should have raised error") + } + + epoch = 1 + nk, err = makeNodeKey(epoch) + if err != nil { + t.Fatal(err) + } + if nk.epoch != epoch { + t.Fatal("epochs do not match") + } + if !bytes.Equal(nk.prefix, dbprefix.PrefixStorageNodeKey()) { + t.Fatal("prefixes do not match (1)") + } + nkBytes, err := nk.Marshal() + if err != nil { + t.Fatal(err) + } + nkTrue := []byte{} + nkTrue = append(nkTrue, dbprefix.PrefixStorageNodeKey()...) + epochBytes := utils.MarshalUint32(epoch) + nkTrue = append(nkTrue, epochBytes...) + if !bytes.Equal(nkBytes, nkTrue) { + t.Fatal("invalid marshalling") + } + + llk := makeLinkedListKey() + if llk.epoch != 0 { + t.Fatal("epoch should be 0") + } + if !bytes.Equal(nk.prefix, dbprefix.PrefixStorageNodeKey()) { + t.Fatal("prefixes do not match (2)") + } +} diff --git a/dynamics/rawdb.go b/dynamics/rawdb.go new file mode 100644 index 00000000..34b01752 --- /dev/null +++ b/dynamics/rawdb.go @@ -0,0 +1,12 @@ +package dynamics + +import ( + "github.com/dgraph-io/badger/v2" +) + +type rawDataBase interface { + GetValue(txn *badger.Txn, key []byte) ([]byte, error) + SetValue(txn *badger.Txn, key []byte, value []byte) error + Update(func(txn *badger.Txn) error) error + View(func(txn *badger.Txn) error) error +} diff --git a/dynamics/rawstorage.go b/dynamics/rawstorage.go new file mode 100644 index 00000000..4628dcec --- /dev/null +++ b/dynamics/rawstorage.go @@ -0,0 +1,416 @@ +package dynamics + +import ( + "encoding/json" + "math/big" + "time" +) + +// RawStorage is the struct which stores dynamic values; +// these values may change from epoch to epoch +type RawStorage struct { + MaxBytes uint32 `json:"maxBytes,omitempty"` + MaxProposalSize uint32 `json:"maxProposalSize,omitempty"` + ProposalStepTimeout time.Duration `json:"proposalStepTimeout,omitempty"` + PreVoteStepTimeout time.Duration `json:"preVoteStepTimeout,omitempty"` + PreCommitStepTimeout time.Duration `json:"preCommitStepTimeout,omitempty"` + DeadBlockRoundNextRoundTimeout time.Duration `json:"deadBlockRoundNextRoundTimeout,omitempty"` + DownloadTimeout time.Duration `json:"downloadTimeout,omitempty"` + SrvrMsgTimeout time.Duration `json:"srvrMsgTimeout,omitempty"` + MsgTimeout time.Duration `json:"msgTimeout,omitempty"` + + MinTxFee *big.Int `json:"minTxFee,omitempty"` + TxValidVersion uint32 `json:"txValidVersion,omitempty"` + + ValueStoreFee *big.Int `json:"valueStoreFee,omitempty"` + ValueStoreValidVersion uint32 `json:"valueStoreValidVersion,omitempty"` + + AtomicSwapFee *big.Int `json:"atomicSwapFee,omitempty"` + AtomicSwapValidStopEpoch uint32 `json:"atomicSwapValidStopEpoch,omitempty"` + + DataStoreEpochFee *big.Int `json:"dataStoreEpochFee,omitempty"` + DataStoreValidVersion uint32 `json:"dataStoreValidVersion,omitempty"` +} + +// Marshal performs json.Marshal on the RawStorage struct. +func (rs *RawStorage) Marshal() ([]byte, error) { + if rs == nil { + return nil, ErrRawStorageNilPointer + } + return json.Marshal(rs) +} + +// Unmarshal performs json.Unmarshal on the RawStorage struct. +func (rs *RawStorage) Unmarshal(v []byte) error { + if rs == nil { + return ErrRawStorageNilPointer + } + if len(v) == 0 { + return ErrUnmarshalEmpty + } + return json.Unmarshal(v, rs) +} + +// Copy makes a complete copy of RawStorage struct. +func (rs *RawStorage) Copy() (*RawStorage, error) { + rsBytes, err := rs.Marshal() + if err != nil { + return nil, err + } + c := &RawStorage{} + err = c.Unmarshal(rsBytes) + if err != nil { + return nil, err + } + return c, nil +} + +// IsValid returns true if we can successfully make a copy +func (rs *RawStorage) IsValid() bool { + _, err := rs.Copy() + if err != nil { + return false + } + return true +} + +// UpdateValue updates the field with the appropriate value. +func (rs *RawStorage) UpdateValue(update Updater) error { + value := update.Value() + switch update.Type() { + case MaxBytesType: + // uint32 + v, err := stringToUint32(value) + if err != nil { + return err + } + rs.SetMaxBytes(v) + case ProposalStepTimeoutType: + // time.Duration + v, err := stringToTimeDuration(value) + if err != nil { + return err + } + rs.SetProposalStepTimeout(v) + case PreVoteStepTimeoutType: + // time.Duration + v, err := stringToTimeDuration(value) + if err != nil { + return err + } + rs.SetPreVoteStepTimeout(v) + case PreCommitStepTimeoutType: + // time.Duration + v, err := stringToTimeDuration(value) + if err != nil { + return err + } + rs.SetPreCommitStepTimeout(v) + case MsgTimeoutType: + // time.Duration + v, err := stringToTimeDuration(value) + if err != nil { + return err + } + rs.SetMsgTimeout(v) + case MinTxFeeType: + // *big.Int + v, err := stringToBigInt(value) + if err != nil { + return err + } + err = rs.SetMinTxFee(v) + if err != nil { + return err + } + case TxValidVersionType: + // uint32 + v, err := stringToUint32(value) + if err != nil { + return err + } + rs.SetTxValidVersion(v) + case ValueStoreFeeType: + // *big.Int + v, err := stringToBigInt(value) + if err != nil { + return err + } + err = rs.SetValueStoreFee(v) + if err != nil { + return err + } + case ValueStoreValidVersionType: + // uint32 + v, err := stringToUint32(value) + if err != nil { + return err + } + rs.SetValueStoreValidVersion(v) + case AtomicSwapFeeType: + // *big.Int + v, err := stringToBigInt(value) + if err != nil { + return err + } + err = rs.SetAtomicSwapFee(v) + if err != nil { + return err + } + case AtomicSwapValidStopEpochType: + // uint32 + v, err := stringToUint32(value) + if err != nil { + return err + } + rs.SetAtomicSwapValidStopEpoch(v) + case DataStoreEpochFeeType: + // *big.Int + v, err := stringToBigInt(value) + if err != nil { + return err + } + err = rs.SetDataStoreEpochFee(v) + if err != nil { + return err + } + case DataStoreValidVersionType: + // uint32 + v, err := stringToUint32(value) + if err != nil { + return err + } + rs.SetDataStoreValidVersion(v) + default: + return ErrInvalidUpdateValue + } + return nil +} + +// standardParameters initializes RawStorage with the standard (original) +// parameters for the system. +func (rs *RawStorage) standardParameters() { + rs.MaxBytes = maxBytes + rs.MaxProposalSize = maxProposalSize + rs.ProposalStepTimeout = proposalStepTO + rs.PreVoteStepTimeout = preVoteStepTO + rs.PreCommitStepTimeout = preCommitStepTO + rs.DeadBlockRoundNextRoundTimeout = dBRNRTO + rs.DownloadTimeout = downloadTO + rs.SrvrMsgTimeout = srvrMsgTimeout + rs.MsgTimeout = msgTimeout +} + +// GetMaxBytes returns the maximum allowed bytes +func (rs *RawStorage) GetMaxBytes() uint32 { + return rs.MaxBytes +} + +// SetMaxBytes sets the maximum allowed bytes +func (rs *RawStorage) SetMaxBytes(value uint32) { + rs.MaxBytes = value + rs.MaxProposalSize = value +} + +// GetMaxProposalSize returns the maximum size of bytes allowed in a proposal +func (rs *RawStorage) GetMaxProposalSize() uint32 { + return rs.MaxProposalSize +} + +// GetSrvrMsgTimeout returns the time before timeout of server message +func (rs *RawStorage) GetSrvrMsgTimeout() time.Duration { + return rs.SrvrMsgTimeout +} + +// GetMsgTimeout returns the timeout to receive a message +func (rs *RawStorage) GetMsgTimeout() time.Duration { + return rs.MsgTimeout +} + +// SetMsgTimeout sets the timeout to receive a message +func (rs *RawStorage) SetMsgTimeout(value time.Duration) { + rs.MsgTimeout = value + rs.SrvrMsgTimeout = (3 * value) / 4 +} + +// GetProposalStepTimeout returns the proposal step timeout +func (rs *RawStorage) GetProposalStepTimeout() time.Duration { + return rs.ProposalStepTimeout +} + +// SetProposalStepTimeout sets the proposal step timeout +func (rs *RawStorage) SetProposalStepTimeout(value time.Duration) { + rs.ProposalStepTimeout = value + sum := rs.ProposalStepTimeout + rs.PreVoteStepTimeout + rs.PreCommitStepTimeout + rs.DownloadTimeout = sum + rs.DeadBlockRoundNextRoundTimeout = (5 * sum) / 2 +} + +// GetPreVoteStepTimeout returns the prevote step timeout +func (rs *RawStorage) GetPreVoteStepTimeout() time.Duration { + return rs.PreVoteStepTimeout +} + +// SetPreVoteStepTimeout sets the prevote step timeout +func (rs *RawStorage) SetPreVoteStepTimeout(value time.Duration) { + rs.PreVoteStepTimeout = value + sum := rs.ProposalStepTimeout + rs.PreVoteStepTimeout + rs.PreCommitStepTimeout + rs.DownloadTimeout = sum + rs.DeadBlockRoundNextRoundTimeout = (5 * sum) / 2 +} + +// GetPreCommitStepTimeout returns the precommit step timeout +func (rs *RawStorage) GetPreCommitStepTimeout() time.Duration { + return rs.PreCommitStepTimeout +} + +// SetPreCommitStepTimeout sets the precommit step timeout +func (rs *RawStorage) SetPreCommitStepTimeout(value time.Duration) { + rs.PreCommitStepTimeout = value + sum := rs.ProposalStepTimeout + rs.PreVoteStepTimeout + rs.PreCommitStepTimeout + rs.DownloadTimeout = sum + rs.DeadBlockRoundNextRoundTimeout = (5 * sum) / 2 +} + +// GetDeadBlockRoundNextRoundTimeout returns the timeout required before +// moving into the DeadBlockRound +func (rs *RawStorage) GetDeadBlockRoundNextRoundTimeout() time.Duration { + return rs.DeadBlockRoundNextRoundTimeout +} + +// GetDownloadTimeout returns the timeout for downloads +func (rs *RawStorage) GetDownloadTimeout() time.Duration { + return rs.DownloadTimeout +} + +// GetMinTxFee returns the minimun tx burned fee +func (rs *RawStorage) GetMinTxFee() *big.Int { + if rs.MinTxFee == nil { + rs.MinTxFee = new(big.Int) + } + return rs.MinTxFee +} + +// SetMinTxFee sets the minimun tx burned fee +func (rs *RawStorage) SetMinTxFee(value *big.Int) error { + if value == nil { + return ErrInvalidValue + } + if rs.MinTxFee == nil { + rs.MinTxFee = new(big.Int) + } + if value.Sign() < 0 { + return ErrInvalidValue + } + rs.MinTxFee.Set(value) + return nil +} + +// GetTxValidVersion returns the valid version of tx +func (rs *RawStorage) GetTxValidVersion() uint32 { + return rs.TxValidVersion +} + +// SetTxValidVersion sets the minimun tx burned fee +func (rs *RawStorage) SetTxValidVersion(value uint32) { + rs.TxValidVersion = value +} + +// GetDataStoreEpochFee returns the minimun ValueStore burned fee +func (rs *RawStorage) GetDataStoreEpochFee() *big.Int { + if rs.DataStoreEpochFee == nil { + rs.DataStoreEpochFee = new(big.Int) + } + return rs.DataStoreEpochFee +} + +// SetDataStoreEpochFee sets the minimun ValueStore burned fee +func (rs *RawStorage) SetDataStoreEpochFee(value *big.Int) error { + if value == nil { + return ErrInvalidValue + } + if rs.DataStoreEpochFee == nil { + rs.DataStoreEpochFee = new(big.Int) + } + if value.Sign() < 0 { + return ErrInvalidValue + } + rs.DataStoreEpochFee.Set(value) + return nil +} + +// GetValueStoreFee returns the minimun ValueStore burned fee +func (rs *RawStorage) GetValueStoreFee() *big.Int { + if rs.ValueStoreFee == nil { + rs.ValueStoreFee = new(big.Int) + } + return rs.ValueStoreFee +} + +// SetValueStoreFee sets the minimun ValueStore burned fee +func (rs *RawStorage) SetValueStoreFee(value *big.Int) error { + if value == nil { + return ErrInvalidValue + } + if rs.ValueStoreFee == nil { + rs.ValueStoreFee = new(big.Int) + } + if value.Sign() < 0 { + return ErrInvalidValue + } + rs.ValueStoreFee.Set(value) + return nil +} + +// GetValueStoreValidVersion returns the valid version of ValueStore +func (rs *RawStorage) GetValueStoreValidVersion() uint32 { + return rs.ValueStoreValidVersion +} + +// SetValueStoreValidVersion sets the valid version of ValueStore +func (rs *RawStorage) SetValueStoreValidVersion(value uint32) { + rs.ValueStoreValidVersion = value +} + +// GetAtomicSwapFee returns the minimun AtomicSwap burned fee +func (rs *RawStorage) GetAtomicSwapFee() *big.Int { + if rs.AtomicSwapFee == nil { + rs.AtomicSwapFee = new(big.Int) + } + return rs.AtomicSwapFee +} + +// SetAtomicSwapFee sets the minimun AtomicSwap burned fee +func (rs *RawStorage) SetAtomicSwapFee(value *big.Int) error { + if value == nil { + return ErrInvalidValue + } + if rs.AtomicSwapFee == nil { + rs.AtomicSwapFee = new(big.Int) + } + if value.Sign() < 0 { + return ErrInvalidValue + } + rs.AtomicSwapFee.Set(value) + return nil +} + +// GetAtomicSwapValidStopEpoch returns the valid version of AtomicSwap +func (rs *RawStorage) GetAtomicSwapValidStopEpoch() uint32 { + return rs.AtomicSwapValidStopEpoch +} + +// SetAtomicSwapValidStopEpoch sets the valid version of AtomicSwap +func (rs *RawStorage) SetAtomicSwapValidStopEpoch(value uint32) { + rs.AtomicSwapValidStopEpoch = value +} + +// GetDataStoreValidVersion returns the valid version of DataStore +func (rs *RawStorage) GetDataStoreValidVersion() uint32 { + return rs.DataStoreValidVersion +} + +// SetDataStoreValidVersion sets the valid version of AtomicSwap +func (rs *RawStorage) SetDataStoreValidVersion(value uint32) { + rs.DataStoreValidVersion = value +} diff --git a/dynamics/rawstorage_test.go b/dynamics/rawstorage_test.go new file mode 100644 index 00000000..b742de9b --- /dev/null +++ b/dynamics/rawstorage_test.go @@ -0,0 +1,1216 @@ +package dynamics + +import ( + "bytes" + "errors" + "math/big" + "strconv" + "testing" + "time" +) + +func TestRawStorageMarshal(t *testing.T) { + rs := &RawStorage{} + _, err := rs.Marshal() + if err != nil { + t.Fatal(err) + } + s := &Storage{} + _, err = s.rawStorage.Marshal() + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestRawStorageUnmarshal(t *testing.T) { + rs := &RawStorage{} + v, err := rs.Marshal() + if err != nil { + t.Fatal(err) + } + rs2 := &RawStorage{} + err = rs2.Unmarshal(v) + if err != nil { + t.Fatal(err) + } + + v = []byte{} + rs3 := &RawStorage{} + err = rs3.Unmarshal(v) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + s := &Storage{} + err = s.rawStorage.Unmarshal(v) + if err == nil { + t.Fatal("Should have raised error (2)") + } +} + +func TestRawStorageCopy(t *testing.T) { + // Copy empty RawStorage + rs1 := &RawStorage{} + rs2, err := rs1.Copy() + if err != nil { + t.Fatal(err) + } + rs1Bytes, err := rs1.Marshal() + if err != nil { + t.Fatal(err) + } + rs2Bytes, err := rs2.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(rs1Bytes, rs2Bytes) { + t.Fatal("Should have equal bytes (1)") + } + + // Copy RawStorage with parameters + rs1.standardParameters() + rs2, err = rs1.Copy() + if err != nil { + t.Fatal(err) + } + rs1Bytes, err = rs1.Marshal() + if err != nil { + t.Fatal(err) + } + rs2Bytes, err = rs2.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(rs1Bytes, rs2Bytes) { + t.Fatal("Should have equal bytes (2)") + } + + // Copy RawStorage with some parameters set to zero + rs1.MaxBytes = 0 + rs2, err = rs1.Copy() + if err != nil { + t.Fatal(err) + } + rs1Bytes, err = rs1.Marshal() + if err != nil { + t.Fatal(err) + } + rs2Bytes, err = rs2.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(rs1Bytes, rs2Bytes) { + t.Fatal("Should have equal bytes (3)") + } + + s := &Storage{} + _, err = s.rawStorage.Copy() + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestRawStorageStandardParameters(t *testing.T) { + rs := &RawStorage{} + rs.standardParameters() + + retMaxBytes := rs.GetMaxBytes() + if retMaxBytes != maxBytes { + t.Fatal("Should be equal (1)") + } + + retMaxProposalSize := rs.GetMaxProposalSize() + if retMaxProposalSize != maxProposalSize { + t.Fatal("Should be equal (2)") + } + + retSrvrMsgTimeout := rs.GetSrvrMsgTimeout() + if retSrvrMsgTimeout != srvrMsgTimeout { + t.Fatal("Should be equal (3)") + } + + retMsgTimeout := rs.GetMsgTimeout() + if retMsgTimeout != msgTimeout { + t.Fatal("Should be equal (4)") + } + + retProposalTimeout := rs.GetProposalStepTimeout() + if retProposalTimeout != proposalStepTO { + t.Fatal("Should be equal (5)") + } + + retPreVoteTimeout := rs.GetPreVoteStepTimeout() + if retPreVoteTimeout != preVoteStepTO { + t.Fatal("Should be equal (6)") + } + + retPreCommitTimeout := rs.GetPreCommitStepTimeout() + if retPreCommitTimeout != preCommitStepTO { + t.Fatal("Should be equal (7)") + } + + retDBRNRTO := rs.GetDeadBlockRoundNextRoundTimeout() + if retDBRNRTO != dBRNRTO { + t.Fatal("Should be equal (8)") + } + + retDownloadTO := rs.GetDownloadTimeout() + if retDownloadTO != downloadTO { + t.Fatal("Should be equal (9)") + } +} + +func TestRawStorageMaxBytes(t *testing.T) { + rs := &RawStorage{} + retMaxBytes0 := rs.GetMaxBytes() + if retMaxBytes0 != 0 { + t.Fatal("Should be zero") + } + + value := uint32(10000) + rs.SetMaxBytes(value) + retMaxBytes := rs.GetMaxBytes() + if retMaxBytes != value { + t.Fatal("Should be equal (1)") + } + + retMaxProposalSize := rs.GetMaxProposalSize() + if retMaxProposalSize != value { + t.Fatal("Should be equal (2)") + } +} + +func TestRawStorageMaxProposalSize(t *testing.T) { + rs := &RawStorage{} + retMaxProposalSize0 := rs.GetMaxProposalSize() + if retMaxProposalSize0 != 0 { + t.Fatal("Should be zero (2)") + } + + value := uint32(10000) + rs.SetMaxBytes(value) + retMaxProposalSize := rs.GetMaxProposalSize() + if retMaxProposalSize != value { + t.Fatal("Should be equal (2)") + } +} + +func TestRawStorageMsgTimeout(t *testing.T) { + rs := &RawStorage{} + retMsgTimeout0 := rs.GetMsgTimeout() + if retMsgTimeout0 != 0 { + t.Fatal("Should be zero") + } + + value := time.Second + rs.SetMsgTimeout(value) + retMsgTimeout := rs.GetMsgTimeout() + if retMsgTimeout != value { + t.Fatal("Should be equal (1)") + } + + valueSrvrMsg := (3 * value) / 4 + retSrvrMsgTimeout := rs.GetSrvrMsgTimeout() + if retSrvrMsgTimeout != valueSrvrMsg { + t.Fatal("Should be equal (2)") + } +} + +func TestRawStorageSrvrMsgTimeout(t *testing.T) { + rs := &RawStorage{} + retSrvrMsgTimeout0 := rs.GetSrvrMsgTimeout() + if retSrvrMsgTimeout0 != 0 { + t.Fatal("Should be zero") + } + + value := time.Second + rs.SetMsgTimeout(value) + valueSrvrMsg := (3 * value) / 4 + retSrvrMsgTimeout := rs.GetSrvrMsgTimeout() + if retSrvrMsgTimeout != valueSrvrMsg { + t.Fatal("Should be equal") + } +} + +func TestRawStorageConsensusTimeouts(t *testing.T) { + rs := &RawStorage{} + + retPropTOv0 := rs.GetProposalStepTimeout() + if retPropTOv0 != 0 { + t.Fatal("Should be zero (1)") + } + retPreVoteTOv0 := rs.GetPreVoteStepTimeout() + if retPreVoteTOv0 != 0 { + t.Fatal("Should be zero (2)") + } + retPreCommitTOv0 := rs.GetPreCommitStepTimeout() + if retPreCommitTOv0 != 0 { + t.Fatal("Should be zero (3)") + } + retDownloadTOv0 := rs.GetDownloadTimeout() + if retDownloadTOv0 != 0 { + t.Fatal("Should be zero (4)") + } + retDBRNRTOv0 := rs.GetDeadBlockRoundNextRoundTimeout() + if retDBRNRTOv0 != 0 { + t.Fatal("Should be zero (5)") + } + + propValue := 10 * time.Second + rs.SetProposalStepTimeout(propValue) + + retPropTOv1 := rs.GetProposalStepTimeout() + if retPropTOv1 != propValue { + t.Fatal("Should be equal (1)") + } + retPreVoteTOv1 := rs.GetPreVoteStepTimeout() + if retPreVoteTOv1 != 0 { + t.Fatal("Should be zero (6)") + } + retPreCommitTOv1 := rs.GetPreCommitStepTimeout() + if retPreCommitTOv1 != 0 { + t.Fatal("Should be zero (7)") + } + retDownloadTOv1 := rs.GetDownloadTimeout() + if retDownloadTOv1 != propValue { + t.Fatal("Should be equal (2)") + } + retDBRNRTOv1 := rs.GetDeadBlockRoundNextRoundTimeout() + if retDBRNRTOv1 != ((5 * propValue) / 2) { + t.Fatal("Should be equal (3)") + } + + preVoteValue := 20 * time.Second + rs.SetPreVoteStepTimeout(preVoteValue) + + retPropTOv2 := rs.GetProposalStepTimeout() + if retPropTOv2 != propValue { + t.Fatal("Should be equal (4)") + } + retPreVoteTOv2 := rs.GetPreVoteStepTimeout() + if retPreVoteTOv2 != preVoteValue { + t.Fatal("Should be equal (5)") + } + retPreCommitTOv2 := rs.GetPreCommitStepTimeout() + if retPreCommitTOv2 != 0 { + t.Fatal("Should be zero (8)") + } + retDownloadTOv2 := rs.GetDownloadTimeout() + if retDownloadTOv2 != (propValue + preVoteValue) { + t.Fatal("Should be equal (6)") + } + retDBRNRTOv2 := rs.GetDeadBlockRoundNextRoundTimeout() + if retDBRNRTOv2 != ((5 * (propValue + preVoteValue)) / 2) { + t.Fatal("Should be equal (7)") + } + + preCommitValue := 30 * time.Second + rs.SetPreCommitStepTimeout(preCommitValue) + + retPropTOv3 := rs.GetProposalStepTimeout() + if retPropTOv3 != propValue { + t.Fatal("Should be equal (8)") + } + retPreVoteTOv3 := rs.GetPreVoteStepTimeout() + if retPreVoteTOv3 != preVoteValue { + t.Fatal("Should be equal (9)") + } + retPreCommitTOv3 := rs.GetPreCommitStepTimeout() + if retPreCommitTOv3 != preCommitValue { + t.Fatal("Should be equal (10)") + } + retDownloadTOv3 := rs.GetDownloadTimeout() + if retDownloadTOv3 != (propValue + preVoteValue + preCommitValue) { + t.Fatal("Should be equal (11)") + } + retDBRNRTOv3 := rs.GetDeadBlockRoundNextRoundTimeout() + if retDBRNRTOv3 != ((5 * (propValue + preVoteValue + preCommitValue)) / 2) { + t.Fatal("Should be equal (12)") + } +} + +func TestRawStorageMinTxFee(t *testing.T) { + rs1 := &RawStorage{} + + v1 := rs1.GetMinTxFee() + if v1.Sign() != 0 { + t.Fatal("minTxBurnedFee should be 0") + } + + rs2 := &RawStorage{} + err := rs2.SetMinTxFee(nil) + if !errors.Is(err, ErrInvalidValue) { + t.Fatal("Should have raised ErrInvalidValue") + } + + rs3 := &RawStorage{} + value := new(big.Int).SetInt64(-1) + err = rs3.SetMinTxFee(value) + if !errors.Is(err, ErrInvalidValue) { + t.Fatal("Should have raised ErrInvalidValue") + } + + rs4 := &RawStorage{} + value4int := int64(1234567890) + value4 := new(big.Int).SetInt64(value4int) + err = rs4.SetMinTxFee(value4) + if err != nil { + t.Fatal(err) + } + v4 := rs4.GetMinTxFee() + if v4.Cmp(big.NewInt(value4int)) != 0 { + t.Fatal("incorrect minTxBurnedFee value") + } +} + +func TestRawStorageTxValidVersion(t *testing.T) { + rs1 := &RawStorage{} + v1 := rs1.GetTxValidVersion() + if v1 != 0 { + t.Fatal("invalid TxValidVersion") + } + + rs2 := &RawStorage{} + version2 := uint32(7919) + rs2.SetTxValidVersion(version2) + v2 := rs2.GetTxValidVersion() + if v2 != version2 { + t.Fatal("TxValidVersions do not match") + } +} + +func TestRawStorageValueStoreFee(t *testing.T) { + rs1 := &RawStorage{} + + v1 := rs1.GetValueStoreFee() + if v1.Sign() != 0 { + t.Fatal("minValueStoreBurnedFee should be 0") + } + + rs2 := &RawStorage{} + err := rs2.SetValueStoreFee(nil) + if !errors.Is(err, ErrInvalidValue) { + t.Fatal("Should have raised ErrInvalidValue") + } + + rs3 := &RawStorage{} + value := new(big.Int).SetInt64(-1) + err = rs3.SetValueStoreFee(value) + if !errors.Is(err, ErrInvalidValue) { + t.Fatal("Should have raised ErrInvalidValue") + } + + rs4 := &RawStorage{} + value4int := int64(1234567890) + value4 := new(big.Int).SetInt64(value4int) + err = rs4.SetValueStoreFee(value4) + if err != nil { + t.Fatal(err) + } + v4 := rs4.GetValueStoreFee() + if v4.Cmp(big.NewInt(value4int)) != 0 { + t.Fatal("incorrect minValueStoreBurnedFee value") + } +} + +func TestRawStorageValueStoreValidVersion(t *testing.T) { + rs1 := &RawStorage{} + v1 := rs1.GetValueStoreValidVersion() + if v1 != 0 { + t.Fatal("invalid ValueStoreValidVersion") + } + + rs2 := &RawStorage{} + version2 := uint32(7919) + rs2.SetValueStoreValidVersion(version2) + v2 := rs2.GetValueStoreValidVersion() + if v2 != version2 { + t.Fatal("ValueStoreTxValidVersions do not match") + } +} + +func TestRawStorageAtomicSwapFee(t *testing.T) { + rs1 := &RawStorage{} + + v1 := rs1.GetAtomicSwapFee() + if v1.Sign() != 0 { + t.Fatal("atomicSwapFee should be 0") + } + + rs2 := &RawStorage{} + err := rs2.SetAtomicSwapFee(nil) + if !errors.Is(err, ErrInvalidValue) { + t.Fatal("Should have raised ErrInvalidValue") + } + + rs3 := &RawStorage{} + value := new(big.Int).SetInt64(-1) + err = rs3.SetAtomicSwapFee(value) + if !errors.Is(err, ErrInvalidValue) { + t.Fatal("Should have raised ErrInvalidValue") + } + + rs4 := &RawStorage{} + value4int := int64(1234567890) + value4 := new(big.Int).SetInt64(value4int) + err = rs4.SetAtomicSwapFee(value4) + if err != nil { + t.Fatal(err) + } + v4 := rs4.GetAtomicSwapFee() + if v4.Cmp(big.NewInt(value4int)) != 0 { + t.Fatal("incorrect atomicSwapFee value") + } +} + +func TestRawStorageAtomicSwapValidStopEpoch(t *testing.T) { + rs1 := &RawStorage{} + v1 := rs1.GetAtomicSwapValidStopEpoch() + if v1 != 0 { + t.Fatal("invalid AtomicSwapValidStopEpoch") + } + + rs2 := &RawStorage{} + version2 := uint32(7919) + rs2.SetAtomicSwapValidStopEpoch(version2) + v2 := rs2.GetAtomicSwapValidStopEpoch() + if v2 != version2 { + t.Fatal("AtomicSwapValidStopEpochs do not match") + } +} + +func TestRawStorageDataStoreEpochFee(t *testing.T) { + rs1 := &RawStorage{} + + v1 := rs1.GetDataStoreEpochFee() + if v1.Sign() != 0 { + t.Fatal("dataStoreEpochFee should be 0") + } + + rs2 := &RawStorage{} + err := rs2.SetDataStoreEpochFee(nil) + if !errors.Is(err, ErrInvalidValue) { + t.Fatal("Should have raised ErrInvalidValue") + } + + rs3 := &RawStorage{} + value := new(big.Int).SetInt64(-1) + err = rs3.SetDataStoreEpochFee(value) + if !errors.Is(err, ErrInvalidValue) { + t.Fatal("Should have raised ErrInvalidValue") + } + + rs4 := &RawStorage{} + value4int := int64(1234567890) + value4 := new(big.Int).SetInt64(value4int) + err = rs4.SetDataStoreEpochFee(value4) + if err != nil { + t.Fatal(err) + } + v4 := rs4.GetDataStoreEpochFee() + if v4.Cmp(big.NewInt(value4int)) != 0 { + t.Fatal("incorrect dataStoreEpochFee value") + } +} + +func TestRawStorageDataStoreValidVersion(t *testing.T) { + rs1 := &RawStorage{} + v1 := rs1.GetDataStoreValidVersion() + if v1 != 0 { + t.Fatal("invalid DataStoreTxValidVersion") + } + + rs2 := &RawStorage{} + version2 := uint32(7919) + rs2.SetDataStoreValidVersion(version2) + v2 := rs2.GetDataStoreValidVersion() + if v2 != version2 { + t.Fatal("DataStoreTxValidVersions do not match") + } +} + +func TestRawStorageUpdateValueBad(t *testing.T) { + rs := &RawStorage{} + fieldBad := "invalid" + valueBad := "" + update := &Update{ + name: fieldBad, + value: valueBad, + } + err := rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestRawStorageUpdateValueMaxBytes(t *testing.T) { + rs := &RawStorage{} + field := "maxBytes" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + retValue := rs.GetMaxBytes() + if retValue != 0 { + t.Fatal("Incorrect MaxBytes (1)") + } + retValue = rs.GetMaxProposalSize() + if retValue != 0 { + t.Fatal("Incorrect MaxProposalSize (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (2)") + } + retValue = rs.GetMaxBytes() + if retValue != 0 { + t.Fatal("Incorrect MaxBytes (2)") + } + retValue = rs.GetMaxProposalSize() + if retValue != 0 { + t.Fatal("Incorrect MaxProposalSize (2)") + } + + valueGood := "1000" + valueTrue64, err := strconv.ParseUint(valueGood, 10, 32) + if err != nil { + t.Fatal(err) + } + valueTrue := uint32(valueTrue64) + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + retValue = rs.GetMaxBytes() + if retValue != valueTrue { + t.Fatal("Incorrect MaxBytes (3)") + } + retValue = rs.GetMaxProposalSize() + if retValue != valueTrue { + t.Fatal("Incorrect MaxProposalSize (3)") + } +} + +func TestRawStorageUpdateValueProposalStepTimeout(t *testing.T) { + rs := &RawStorage{} + + retProposalStepTO := rs.GetProposalStepTimeout() + if retProposalStepTO != 0 { + t.Fatal("Incorrect ProposalStepTO (1)") + } + + field := "proposalStepTimeout" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if !errors.Is(err, ErrInvalid) { + t.Fatal("Should have raised error (2)") + } + + valueGood := "1000000000" + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + + valueTrue64, err := strconv.ParseInt(valueGood, 10, 64) + if err != nil { + t.Fatal(err) + } + propTrue := time.Duration(valueTrue64) + retProposalStepTO = rs.GetProposalStepTimeout() + if retProposalStepTO != propTrue { + t.Fatal("Incorrect ProposalStepTO (2)") + } +} + +func TestRawStorageUpdateValuePreVoteStepTimeout(t *testing.T) { + rs := &RawStorage{} + + retPreVoteStepTO := rs.GetPreVoteStepTimeout() + if retPreVoteStepTO != 0 { + t.Fatal("Incorrect PreVoteStepTO (1)") + } + + field := "preVoteStepTimeout" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if !errors.Is(err, ErrInvalid) { + t.Fatal("Should have raised error (2)") + } + + valueGood := "1000000000" + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + + valueTrue64, err := strconv.ParseInt(valueGood, 10, 64) + if err != nil { + t.Fatal(err) + } + preVoteTrue := time.Duration(valueTrue64) + retPreVoteStepTO = rs.GetPreVoteStepTimeout() + if retPreVoteStepTO != preVoteTrue { + t.Fatal("Incorrect PreVoteStepTO (2)") + } +} + +func TestRawStorageUpdateValuePreCommitStepTimeout(t *testing.T) { + rs := &RawStorage{} + + retPreCommitStepTO := rs.GetPreCommitStepTimeout() + if retPreCommitStepTO != 0 { + t.Fatal("Incorrect PreCommitStepTO (1)") + } + + field := "preCommitStepTimeout" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if !errors.Is(err, ErrInvalid) { + t.Fatal("Should have raised error (2)") + } + + valueGood := "1000000000" + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + + valueTrue64, err := strconv.ParseInt(valueGood, 10, 64) + if err != nil { + t.Fatal(err) + } + preCommitTrue := time.Duration(valueTrue64) + retPreCommitStepTO = rs.GetPreCommitStepTimeout() + if retPreCommitStepTO != preCommitTrue { + t.Fatal("Incorrect PreCommitStepTO (2)") + } +} + +func TestRawStorageUpdateValueMsgTimeout(t *testing.T) { + rs := &RawStorage{} + + retMsgTO := rs.GetMsgTimeout() + if retMsgTO != 0 { + t.Fatal("Incorrect MsgTimeout (1)") + } + + field := "msgTimeout" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if !errors.Is(err, ErrInvalid) { + t.Fatal("Should have raised error (2)") + } + + valueGood := "1000000000" + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + + valueTrue64, err := strconv.ParseInt(valueGood, 10, 64) + if err != nil { + t.Fatal(err) + } + msgTrue := time.Duration(valueTrue64) + retMsgTO = rs.GetMsgTimeout() + if retMsgTO != msgTrue { + t.Fatal("Incorrect MsgTimeout (2)") + } +} + +func TestRawStorageUpdateMinTxBurnedFee(t *testing.T) { + rs := &RawStorage{} + + retMinTxFee := rs.GetMinTxFee() + if retMinTxFee.Sign() != 0 { + t.Fatal("Incorrect MinTxFee (1)") + } + + field := "minTxFee" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if !errors.Is(err, ErrInvalid) { + t.Fatal("Should have raised ErrInvalidUpdateValue error") + } + + valueGood := "1000000000" + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + + valueTrue, ok := new(big.Int).SetString(valueGood, 10) + if !ok { + t.Fatal("SetString failed") + } + retMinTxFee = rs.GetMinTxFee() + if retMinTxFee.Cmp(valueTrue) != 0 { + t.Fatal("Incorrect MinTxBurnedFee (2)") + } +} + +func TestRawStorageUpdateTxValidVersion(t *testing.T) { + rs := &RawStorage{} + + retTxValidVersion := rs.GetTxValidVersion() + if retTxValidVersion != 0 { + t.Fatal("Incorrect TxValidVersion (1)") + } + + field := "txValidVersion" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (2)") + } + + valueGood := "1000000000" + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + + valueTrue64, err := strconv.ParseInt(valueGood, 10, 32) + if err != nil { + t.Fatal(err) + } + txValidTrue := uint32(valueTrue64) + retTxValidVersion = rs.GetTxValidVersion() + if retTxValidVersion != txValidTrue { + t.Fatal("Incorrect TxValidVersion (2)") + } +} + +func TestRawStorageUpdateValueStoreFee(t *testing.T) { + rs := &RawStorage{} + + retMinVSFee := rs.GetValueStoreFee() + if retMinVSFee.Sign() != 0 { + t.Fatal("Incorrect MinValueStoreBurnedFee (1)") + } + + field := "valueStoreFee" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if !errors.Is(err, ErrInvalid) { + t.Fatal("Should have raised ErrInvalidUpdateValue error") + } + + valueGood := "1000000000" + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + + valueTrue, ok := new(big.Int).SetString(valueGood, 10) + if !ok { + t.Fatal("SetString failed") + } + retMinVSFee = rs.GetValueStoreFee() + if retMinVSFee.Cmp(valueTrue) != 0 { + t.Fatal("Incorrect MinValueStoreBurnedFee (2)") + } +} + +func TestRawStorageUpdateValueStoreValidVersion(t *testing.T) { + rs := &RawStorage{} + + retVSValidVersion := rs.GetValueStoreValidVersion() + if retVSValidVersion != 0 { + t.Fatal("Incorrect ValueStoreValidVersion (1)") + } + + field := "valueStoreValidVersion" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (2)") + } + + valueGood := "1000000000" + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + + valueTrue64, err := strconv.ParseInt(valueGood, 10, 32) + if err != nil { + t.Fatal(err) + } + vsTxValidTrue := uint32(valueTrue64) + retVSValidVersion = rs.GetValueStoreValidVersion() + if retVSValidVersion != vsTxValidTrue { + t.Fatal("Incorrect ValueStoreValidVersion (2)") + } +} + +func TestRawStorageUpdateAtomicSwapFee(t *testing.T) { + rs := &RawStorage{} + + retMinASFee := rs.GetAtomicSwapFee() + if retMinASFee.Sign() != 0 { + t.Fatal("Incorrect AtomicSwapFee (1)") + } + + field := "atomicSwapFee" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if !errors.Is(err, ErrInvalid) { + t.Fatal("Should have raised ErrInvalid error") + } + + valueGood := "1000000000" + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + + valueTrue, ok := new(big.Int).SetString(valueGood, 10) + if !ok { + t.Fatal("SetString failed") + } + retMinASFee = rs.GetAtomicSwapFee() + if retMinASFee.Cmp(valueTrue) != 0 { + t.Fatal("Incorrect AtomicSwapFee (2)") + } +} + +func TestRawStorageUpdateAtomicSwapStopEpoch(t *testing.T) { + rs := &RawStorage{} + + retAtomicSwapStopEpoch := rs.GetAtomicSwapValidStopEpoch() + if retAtomicSwapStopEpoch != 0 { + t.Fatal("Incorrect AtomicSwapValidStopEpoch (1)") + } + + field := "atomicSwapValidStopEpoch" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (2)") + } + + valueGood := "1000000000" + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + + valueTrue64, err := strconv.ParseInt(valueGood, 10, 32) + if err != nil { + t.Fatal(err) + } + asTrue := uint32(valueTrue64) + retAtomicSwapStopEpoch = rs.GetAtomicSwapValidStopEpoch() + if retAtomicSwapStopEpoch != asTrue { + t.Fatal("Incorrect AtomicSwapValidStopEpoch (2)") + } +} + +func TestRawStorageUpdateDataStoreEpochFee(t *testing.T) { + rs := &RawStorage{} + + dsEpochFee := rs.GetDataStoreEpochFee() + if dsEpochFee.Sign() != 0 { + t.Fatal("Incorrect DataStoreEpochFee (1)") + } + + field := "dataStoreEpochFee" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if !errors.Is(err, ErrInvalid) { + t.Fatal("Should have raised ErrInvalid error") + } + + valueGood := "1000000000" + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + + valueTrue, ok := new(big.Int).SetString(valueGood, 10) + if !ok { + t.Fatal("SetString failed") + } + dsEpochFee = rs.GetDataStoreEpochFee() + if dsEpochFee.Cmp(valueTrue) != 0 { + t.Fatal("Incorrect DataStoreEpochFee (2)") + } +} + +func TestRawStorageUpdateDataStoreValidVersion(t *testing.T) { + rs := &RawStorage{} + + retDSTxValidVersion := rs.GetDataStoreValidVersion() + if retDSTxValidVersion != 0 { + t.Fatal("Incorrect DataStoreValidVersion (1)") + } + + field := "dataStoreValidVersion" + valueBad1 := "" + epoch := uint32(1) + update, err := NewUpdate(field, valueBad1, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + valueBad2 := "-1" + update, err = NewUpdate(field, valueBad2, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err == nil { + t.Fatal("Should have raised error (2)") + } + + valueGood := "1000000000" + update, err = NewUpdate(field, valueGood, epoch) + if err != nil { + t.Fatal(err) + } + err = rs.UpdateValue(update) + if err != nil { + t.Fatal(err) + } + + valueTrue64, err := strconv.ParseInt(valueGood, 10, 32) + if err != nil { + t.Fatal(err) + } + dsTxValidTrue := uint32(valueTrue64) + retDSTxValidVersion = rs.GetDataStoreValidVersion() + if retDSTxValidVersion != dsTxValidTrue { + t.Fatal("Incorrect DataStoreTxValidVersion (2)") + } +} diff --git a/dynamics/storage.go b/dynamics/storage.go new file mode 100644 index 00000000..21293641 --- /dev/null +++ b/dynamics/storage.go @@ -0,0 +1,769 @@ +package dynamics + +import ( + "errors" + "math/big" + "sync" + "time" + + "github.com/MadBase/MadNet/utils" + "github.com/dgraph-io/badger/v2" + "github.com/sirupsen/logrus" +) + +// Ensuring interface check +var _ StorageGetter = (*Storage)(nil) + +/* +PROPOSAL ON CHAIN +PROPOSAL GETS VOTED ON +IF PROPOSAL PASSES IT BECOMES ACTIVE IN FUTURE ( EPOCH OF ACTIVE > EPOCH OF FINAL VOTE + 1 ) +WHEN PROPOSAL PASSES AN EVENT IS EMITTED FROM THE GOVERNANCE CONTRACT +THIS EVENT IS OBSERVED BY THE NODES +THE NODES FETCH THE NEW VALUES AND STORE IN THE DATABASE FOR FUTURE USE +ON THE EPOCH BOUNDARY OF NOT ACTIVE TO ACTIVE, THE STORAGE STRUCT MUST BE UPDATED IN MEMORY FROM + THE VALUES STORED IN THE DB +*/ + +// Dynamics contains the list of "constants" which may be changed +// dynamically to reflect protocol updates. +// The point is that these values are essentially constant but may be changed +// in future. + +// StorageGetter is the interface that all Storage structs must match +// to be valid. These will be used to store the constants which may change +// each epoch as governance determines. +type StorageGetter interface { + GetMaxBytes() uint32 + GetMaxProposalSize() uint32 + + GetProposalStepTimeout() time.Duration + GetPreVoteStepTimeout() time.Duration + GetPreCommitStepTimeout() time.Duration + GetDeadBlockRoundNextRoundTimeout() time.Duration + GetDownloadTimeout() time.Duration + GetSrvrMsgTimeout() time.Duration + GetMsgTimeout() time.Duration + + UpdateStorage(*badger.Txn, Updater) error + LoadStorage(*badger.Txn, uint32) error + + GetDataStoreEpochFee() *big.Int + GetDataStoreValidVersion() uint32 + + GetValueStoreFee() *big.Int + GetValueStoreValidVersion() uint32 + + GetAtomicSwapFee() *big.Int + GetAtomicSwapValidStopEpoch() uint32 + + GetMinTxFee() *big.Int + GetTxValidVersion() uint32 +} + +// Storage is the struct which will implement the StorageGetter interface. +type Storage struct { + sync.RWMutex + database *Database + startChan chan struct{} + startOnce sync.Once + rawStorage *RawStorage + logger *logrus.Logger +} + +// checkUpdate confirms the specified update is valid. +func checkUpdate(update Updater) error { + if update.Epoch() == 0 { + return ErrInvalidUpdateValue + } + rs := &RawStorage{} + err := rs.UpdateValue(update) + if err != nil { + return err + } + return nil +} + +// Init initializes the Storage structure. +func (s *Storage) Init(rawDB rawDataBase, logger *logrus.Logger) error { + // initialize channel + s.startChan = make(chan struct{}) + + // initialize database + s.database = &Database{rawDB: rawDB} + + // initialize logger + s.logger = logger + return nil +} + +// Start allows normal operations to begin. This MUST be called after Init +// and can only be called once. +func (s *Storage) Start() { + s.startOnce.Do(func() { + close(s.startChan) + }) + s.rawStorage = &RawStorage{} + s.rawStorage.standardParameters() +} + +// UpdateStorage updates the database to include changes that must be made +// to the database +func (s *Storage) UpdateStorage(txn *badger.Txn, update Updater) error { + select { + case <-s.startChan: + } + s.Lock() + defer s.Unlock() + + err := checkUpdate(update) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + + // Need to add code to check if initialization has been performed; + // that is, is this the first call to UpdateStorage? + _, err = s.database.GetLinkedList(txn) + if err != nil { + if !errors.Is(err, ErrKeyNotPresent) { + utils.DebugTrace(s.logger, err) + return err + } + rs := &RawStorage{} + rs.standardParameters() + node, ll, err := CreateLinkedList(1, rs) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + err = s.database.SetLinkedList(txn, ll) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + err = s.database.SetNode(txn, node) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + } + + err = s.updateStorageValue(txn, update) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + return nil +} + +// updateStorageValue updates the stored RawStorage values. +// +// We start at the Head of LinkedList and find the farthest point +// at which we need to update nodes. +// Once we find the beginning, we iterate forward and update all forward nodes. +func (s *Storage) updateStorageValue(txn *badger.Txn, update Updater) error { + select { + case <-s.startChan: + } + epoch := update.Epoch() + ll, err := s.database.GetLinkedList(txn) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + elu := ll.GetEpochLastUpdated() + iterNode, err := s.database.GetNode(txn, elu) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + + // firstNode denotes where we will begin looping forward + // including the updated value; + // this will not be used when we have a new Head + firstNode := &Node{} + // duplicateNode is used when we will copy values from the previous node + // and then updating it to form a new node + duplicateNode := &Node{} + // newHead denotes if we need to update the Head of the LinkedList; + // that is, if we need to update EpochLastUpdated + newHead := false + // addNode denotes whether we must add a node. + // This will not occur if our update is on another node, + // which could happen if multiple updates occur on one epoch. + addNode := true + + // Loop backwards through the LinkedList to find firstNode and duplicateNode + for { + if epoch >= iterNode.thisEpoch { + // We will use + // + // I = iterNode + // U = updateNode + // F = firstNode + // D = duplicateNode + // H = Head + // + // in our diagrams below. + // + // the update occurs in the current range + if epoch == iterNode.thisEpoch { + // the update occurs on a node; we do not need to add a node + // + // U + // F + // I + // |---|---|---|---|---|---|---|---|---|---|---|---| + firstNode, err = iterNode.Copy() + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + addNode = false + } else { + // epoch > iterNode.thisEpoch + if iterNode.IsHead() { + // we will add a new node further in the future; + // there will be no iteration. + // + // H + // D + // I U + // |---|---|---|---|---|---|---|---|---|---|---|---| + newHead = true + } else { + // we start iterating at the node ahead. + // + // D + // I U F + // |---|---|---|---|---|---|---|---|---|---|---|---| + firstNode, err = s.database.GetNode(txn, iterNode.nextEpoch) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + } + duplicateNode, err = iterNode.Copy() + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + } + break + } + // If we have reached the tail node, then we do not have a node + // for this specific epoch; we raise an error. + if iterNode.IsTail() { + // We cannot add an update before the first node + return ErrInvalidUpdateValue + } + // We proceed backward in the linked list of nodes + prevEpoch := iterNode.prevEpoch + iterNode, err = s.database.GetNode(txn, prevEpoch) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + } + + if addNode { + // We need to add a new node, so we prepare + node := &Node{ + thisEpoch: epoch, + } + // We compute the correct RawStorage value + // We grab the RawStorage from duplicateNode and then update the value. + rs, err := duplicateNode.rawStorage.Copy() + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + err = rs.UpdateValue(update) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + node.rawStorage = rs + // We add the node to the database + err = s.addNode(txn, node) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + } + + if newHead { + // We added a new Head, so we need to store this information + // before we exit. + err = ll.SetEpochLastUpdated(epoch) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + err = s.database.SetLinkedList(txn, ll) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + return nil + } + + // We now iterate forward from firstNode and update all the nodes + // to reflect the new values. + iterNode, err = firstNode.Copy() + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + + for { + err = iterNode.rawStorage.UpdateValue(update) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + err = s.database.SetNode(txn, iterNode) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + if iterNode.IsHead() { + break + } + nextEpoch := iterNode.nextEpoch + iterNode, err = s.database.GetNode(txn, nextEpoch) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + } + + return nil +} + +// LoadStorage updates RawStorage to the correct value defined by the epoch. +// +// We will attempt to load the correct storage struct. +// If we receive ErrKeyNotPresent, then we return RawStorage +// with the standard parameters. +// +// We use Lock and Unlock rather than RLock and RUnlock because +// we modify Storage. +func (s *Storage) LoadStorage(txn *badger.Txn, epoch uint32) error { + select { + case <-s.startChan: + } + s.Lock() + defer s.Unlock() + rs, err := s.loadStorage(txn, epoch) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + s.rawStorage, err = rs.Copy() + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + return nil +} + +// loadStorage wraps loadRawStorage and ensures that a valid RawStorage +// value is returned if possible. +// +// When loadStorage is called by other functions, we should only need +// those functions to have RLock and RUnlock because we do not modify Storage. +func (s *Storage) loadStorage(txn *badger.Txn, epoch uint32) (*RawStorage, error) { + rs, err := s.loadRawStorage(txn, epoch) + if err != nil { + if !errors.Is(err, ErrKeyNotPresent) { + utils.DebugTrace(s.logger, err) + return nil, err + } + rs = &RawStorage{} + rs.standardParameters() + } + return rs, nil +} + +// loadRawStorage looks for the appropriate RawStorage value in the database +// and returns that value. +// +// We start at the most updated epoch and proceed backwards until we arrive +// at the node with +// epoch >= node.thisEpoch +func (s *Storage) loadRawStorage(txn *badger.Txn, epoch uint32) (*RawStorage, error) { + if epoch == 0 { + return nil, ErrZeroEpoch + } + ll, err := s.database.GetLinkedList(txn) + if err != nil { + utils.DebugTrace(s.logger, err) + return nil, err + } + elu := ll.GetEpochLastUpdated() + currentNode, err := s.database.GetNode(txn, elu) + if err != nil { + utils.DebugTrace(s.logger, err) + return nil, err + } + + // Loop backwards through the LinkedList + for { + if epoch >= currentNode.thisEpoch { + rs, err := currentNode.rawStorage.Copy() + if err != nil { + utils.DebugTrace(s.logger, err) + return nil, err + } + return rs, nil + } + // If we have reached the tail node, then we do not have a node + // for this specific epoch; we raise an error. + if currentNode.IsTail() { + utils.DebugTrace(s.logger, ErrInvalid) + return nil, ErrInvalid + } + // We proceed backward in the linked list of nodes + prevEpoch := currentNode.prevEpoch + currentNode, err = s.database.GetNode(txn, prevEpoch) + if err != nil { + utils.DebugTrace(s.logger, err) + return nil, err + } + } +} + +// addNode adds an additional node to the database. +// This node can be added anywhere. +// If the node is added at the head, then LinkedList must be updated +// to reflect this change. +func (s *Storage) addNode(txn *badger.Txn, node *Node) error { + select { + case <-s.startChan: + } + + // Ensure node.rawStorage and node.thisEpoch are valid; + // other parameters should not be set. + // This ensure that node.thisEpoch != 0 + if !node.IsPreValid() { + return ErrInvalid + } + + // Get LinkedList and Head + ll, err := s.database.GetLinkedList(txn) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + elu := ll.GetEpochLastUpdated() + currentNode, err := s.database.GetNode(txn, elu) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + + if node.thisEpoch > currentNode.thisEpoch { + // node to be added is strictly ahead of ELU + err = s.addNodeHead(txn, node, currentNode) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + return nil + } + + if node.thisEpoch == currentNode.thisEpoch { + // Node is already present; raise error + return ErrInvalid + } + + if currentNode.IsTail() { + // The first node is always at epoch 1; + // we cannot add a node before that. + return ErrInvalid + } + + prevNode := &Node{} + + // Loop backwards through the LinkedList + for { + // Get previous node + prevNode, err = s.database.GetNode(txn, currentNode.prevEpoch) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + if prevNode.thisEpoch < node.thisEpoch && node.thisEpoch < currentNode.thisEpoch { + // We need to add node in between prevNode and currentNode + err = s.addNodeSplit(txn, node, prevNode, currentNode) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + return nil + } + if node.thisEpoch == prevNode.thisEpoch { + // Node is already present; raise error + return ErrInvalid + } + if prevNode.IsTail() { + // The first node is always at epoch 1; + // we cannot add a node before that. + return ErrInvalid + } + currentNode, err = prevNode.Copy() + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + } +} + +func (s *Storage) addNodeHead(txn *badger.Txn, node, headNode *Node) error { + if !node.IsPreValid() || !headNode.IsValid() { + return ErrInvalid + } + if !headNode.IsHead() || node.thisEpoch <= headNode.thisEpoch { + // We require headNode to be head and node.thisEpoch < headNode.thisEpoch + return ErrInvalid + } + err := node.SetEpochs(headNode, nil) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + // Store the nodes after changes have been made + err = s.database.SetNode(txn, headNode) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + err = s.database.SetNode(txn, node) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + + // Update EpochLastUpdated + ll, err := s.database.GetLinkedList(txn) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + // We need to update EpochLastUpdated + err = ll.SetEpochLastUpdated(node.thisEpoch) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + err = s.database.SetLinkedList(txn, ll) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + return nil +} + +func (s *Storage) addNodeSplit(txn *badger.Txn, node, prevNode, nextNode *Node) error { + if !node.IsPreValid() || !prevNode.IsValid() || !nextNode.IsValid() { + return ErrInvalid + } + if (prevNode.thisEpoch >= node.thisEpoch) || (node.thisEpoch >= nextNode.thisEpoch) { + return ErrInvalid + } + err := node.SetEpochs(prevNode, nextNode) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + // Store the nodes after changes have been made + err = s.database.SetNode(txn, prevNode) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + err = s.database.SetNode(txn, nextNode) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + err = s.database.SetNode(txn, node) + if err != nil { + utils.DebugTrace(s.logger, err) + return err + } + return nil +} + +// GetMaxBytes returns the maximum allowed bytes +func (s *Storage) GetMaxBytes() uint32 { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetMaxBytes() +} + +// GetMaxProposalSize returns the maximum size of bytes allowed in a proposal +func (s *Storage) GetMaxProposalSize() uint32 { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetMaxProposalSize() +} + +// GetSrvrMsgTimeout returns the time before timeout of server message +func (s *Storage) GetSrvrMsgTimeout() time.Duration { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetSrvrMsgTimeout() +} + +// GetMsgTimeout returns the timeout to receive a message +func (s *Storage) GetMsgTimeout() time.Duration { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetMsgTimeout() +} + +// GetProposalStepTimeout returns the proposal step timeout +func (s *Storage) GetProposalStepTimeout() time.Duration { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetProposalStepTimeout() +} + +// GetPreVoteStepTimeout returns the prevote step timeout +func (s *Storage) GetPreVoteStepTimeout() time.Duration { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetPreVoteStepTimeout() +} + +// GetPreCommitStepTimeout returns the precommit step timeout +func (s *Storage) GetPreCommitStepTimeout() time.Duration { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetPreCommitStepTimeout() +} + +// GetDeadBlockRoundNextRoundTimeout returns the timeout required before +// moving into the DeadBlockRound +func (s *Storage) GetDeadBlockRoundNextRoundTimeout() time.Duration { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetDeadBlockRoundNextRoundTimeout() +} + +// GetDownloadTimeout returns the timeout for downloads +func (s *Storage) GetDownloadTimeout() time.Duration { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetDownloadTimeout() +} + +// GetMinTxFee returns the minimum transaction fee. +func (s *Storage) GetMinTxFee() *big.Int { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetMinTxFee() +} + +// GetTxValidVersion returns the transaction valid version +func (s *Storage) GetTxValidVersion() uint32 { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetTxValidVersion() +} + +// GetValueStoreFee returns the transaction fee for ValueStore +func (s *Storage) GetValueStoreFee() *big.Int { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetValueStoreFee() +} + +// GetValueStoreValidVersion returns the ValueStore valid version +func (s *Storage) GetValueStoreValidVersion() uint32 { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetValueStoreValidVersion() +} + +// GetAtomicSwapFee returns the transaction fee for AtomicSwap +func (s *Storage) GetAtomicSwapFee() *big.Int { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetAtomicSwapFee() +} + +// GetAtomicSwapValidStopEpoch returns the last epoch at which AtomicSwap is valid +func (s *Storage) GetAtomicSwapValidStopEpoch() uint32 { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetAtomicSwapValidStopEpoch() +} + +// GetDataStoreEpochFee returns the DataStore fee per epoch +func (s *Storage) GetDataStoreEpochFee() *big.Int { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetDataStoreEpochFee() +} + +// GetDataStoreValidVersion returns the DataStore valid version +func (s *Storage) GetDataStoreValidVersion() uint32 { + select { + case <-s.startChan: + } + s.RLock() + defer s.RUnlock() + return s.rawStorage.GetDataStoreValidVersion() +} diff --git a/dynamics/storage_test.go b/dynamics/storage_test.go new file mode 100644 index 00000000..3af09e50 --- /dev/null +++ b/dynamics/storage_test.go @@ -0,0 +1,1473 @@ +package dynamics + +import ( + "bytes" + "errors" + "strconv" + "testing" + "time" +) + +func initializeStorage() *Storage { + storageLogger := newLogger() + mock := &mockRawDB{} + mock.rawDB = make(map[string]string) + + s := &Storage{} + err := s.Init(mock, storageLogger) + if err != nil { + panic(err) + } + s.Start() + return s +} + +func initializeStorageWithFirstNode() *Storage { + s := initializeStorage() + field := "maxBytes" + value := "3000000" + epoch := uint32(1) + update, err := NewUpdate(field, value, epoch) + if err != nil { + panic(err) + } + err = s.UpdateStorage(nil, update) + if err != nil { + panic(err) + } + return s +} + +// Test Storage Init with nothing initialized +func TestStorageInit1(t *testing.T) { + storageLogger := newLogger() + mock := &mockRawDB{} + mock.rawDB = make(map[string]string) + + s := &Storage{} + err := s.Init(mock, storageLogger) + if err != nil { + t.Fatal(err) + } + s.Start() + + rs := &RawStorage{} + rs.standardParameters() + rsBytes, err := rs.Marshal() + if err != nil { + t.Fatal(err) + } + + // Check rawStorage == standardParameters + storageRSBytes, err := s.rawStorage.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(rsBytes, storageRSBytes) { + t.Fatal("rawStorage values do not match") + } +} + +func TestStorageStartGood(t *testing.T) { + storageLogger := newLogger() + mock := &mockRawDB{} + mock.rawDB = make(map[string]string) + + s := &Storage{} + err := s.Init(mock, storageLogger) + if err != nil { + t.Fatal(err) + } + s.Start() +} + +// Test ensures we panic when running Start before Init. +// This happens from attempting to close a closed channel. +func TestStorageStartFail(t *testing.T) { + s := &Storage{} + defer func() { + if r := recover(); r == nil { + t.Fatal("Should panic") + } + }() + s.Start() +} + +// Test ensures storage has is initialized to the correct values. +func TestStorageInitialized(t *testing.T) { + s := initializeStorage() + + maxBytesReturned := s.GetMaxBytes() + if maxBytesReturned != maxBytes { + t.Fatal("Incorrect MaxBytes") + } + + maxProposalSizeReturned := s.GetMaxProposalSize() + if maxProposalSizeReturned != maxProposalSize { + t.Fatal("Incorrect MaxProposalSize") + } + + srvrMsgTimeoutReturned := s.GetSrvrMsgTimeout() + if srvrMsgTimeoutReturned != srvrMsgTimeout { + t.Fatal("Incorrect srvrMsgTimeout") + } + + msgTimeoutReturned := s.GetMsgTimeout() + if msgTimeoutReturned != msgTimeout { + t.Fatal("Incorrect msgTimeout") + } + + proposalStepTimeoutReturned := s.GetProposalStepTimeout() + if proposalStepTimeoutReturned != proposalStepTO { + t.Fatal("Incorrect proposalStepTO") + } + + preVoteStepTimeoutReturned := s.GetPreVoteStepTimeout() + if preVoteStepTimeoutReturned != preVoteStepTO { + t.Fatal("Incorrect preVoteStepTO") + } + + preCommitStepTimeoutReturned := s.GetPreCommitStepTimeout() + if preCommitStepTimeoutReturned != preCommitStepTO { + t.Fatal("Incorrect preCommitStepTO") + } + + deadBlockRoundNextRoundTimeoutReturned := s.GetDeadBlockRoundNextRoundTimeout() + if deadBlockRoundNextRoundTimeoutReturned != dBRNRTO { + t.Fatal("Incorrect deadBlockRoundNextRoundTimeout") + } + + downloadTimeoutReturned := s.GetDownloadTimeout() + if downloadTimeoutReturned != downloadTO { + t.Fatal("Incorrect downloadTimeout") + } + + minTxFee := s.GetMinTxFee() + if minTxFee.Sign() != 0 { + t.Fatal("Incorrect minTxFee") + } + + txValidVersion := s.GetTxValidVersion() + if txValidVersion != 0 { + t.Fatal("Incorrect txValidVersion") + } + + vsFee := s.GetValueStoreFee() + if vsFee.Sign() != 0 { + t.Fatal("Incorrect valueStoreFee") + } + + vsValidVersion := s.GetValueStoreValidVersion() + if vsValidVersion != 0 { + t.Fatal("Incorrect valueStoreValidVersion") + } + + asFee := s.GetAtomicSwapFee() + if asFee.Sign() != 0 { + t.Fatal("Incorrect atomicSwapFee") + } + + asStopEpoch := s.GetAtomicSwapValidStopEpoch() + if asStopEpoch != 0 { + t.Fatal("Incorrect atomicSwapValidStopEpoch") + } + + dsEpochFee := s.GetDataStoreEpochFee() + if dsEpochFee.Sign() != 0 { + t.Fatal("Incorrect dataStoreEpochFee") + } + + dsValidVersion := s.GetDataStoreValidVersion() + if dsValidVersion != 0 { + t.Fatal("Incorrect dataStoreValidVersion") + } +} + +func TestStorageCheckUpdate(t *testing.T) { + fieldBad := "invalid" + valueBad := "invalid" + epochGood := uint32(25519) + _, err := NewUpdate(fieldBad, valueBad, epochGood) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + fieldGood := "maxBytes" + valueGood := "1234567890" + update, err := NewUpdate(fieldGood, valueGood, epochGood) + if err != nil { + t.Fatal(err) + } + err = checkUpdate(update) + if err != nil { + t.Fatal(err) + } + + epochBad := uint32(0) + update, err = NewUpdate(fieldGood, valueGood, epochBad) + if err != nil { + t.Fatal(err) + } + err = checkUpdate(update) + if !errors.Is(err, ErrInvalidUpdateValue) { + t.Fatal("Should have raised error (2)") + } + + update = &Update{epoch: 1} + err = checkUpdate(update) + if !errors.Is(err, ErrInvalidUpdateValue) { + t.Fatal("Should have raised error (3)") + } +} + +// Test success of LoadStorage +func TestStorageLoadStorageGood1(t *testing.T) { + s := initializeStorage() + epoch := uint32(25519) + + rsTrue := &RawStorage{} + rsTrue.standardParameters() + rsTrueBytes, err := rsTrue.Marshal() + if err != nil { + t.Fatal(err) + } + + err = s.LoadStorage(nil, epoch) + if err != nil { + t.Fatal(err) + } + rsBytes, err := s.rawStorage.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(rsBytes, rsTrueBytes) { + t.Fatal("rawStorage values do not match") + } +} + +// Test success of LoadStorage again +func TestStorageLoadStorageGood2(t *testing.T) { + s := initializeStorageWithFirstNode() + epoch := uint32(25519) + + rsTrue := &RawStorage{} + rsTrue.standardParameters() + rsTrueBytes, err := rsTrue.Marshal() + if err != nil { + t.Fatal(err) + } + + err = s.LoadStorage(nil, epoch) + if err != nil { + t.Fatal(err) + } + rsBytes, err := s.rawStorage.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(rsBytes, rsTrueBytes) { + t.Fatal("rawStorage values do not match") + } +} + +// Test failure of LoadStorage +func TestStorageLoadStorageBad1(t *testing.T) { + s := initializeStorage() + // We attempt to load the zero epoch; + // this should raise an error. + err := s.LoadStorage(nil, 0) + if !errors.Is(err, ErrZeroEpoch) { + t.Fatal("Should have raised ErrZeroEpoch") + } +} + +// Test success of loadRawStorage. +func TestStorageLoadRawStorageGood1(t *testing.T) { + s := initializeStorageWithFirstNode() + rsTrue := &RawStorage{} + rsTrue.standardParameters() + rsTrueBytes, err := rsTrue.Marshal() + if err != nil { + t.Fatal(err) + } + + // We attempt to load an epoch from an empty database; + // this should raise an error. + rs, err := s.loadRawStorage(nil, 1) + if err != nil { + t.Fatal(err) + } + rsBytes, err := rs.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(rsBytes, rsTrueBytes) { + t.Fatal("RawStorage values do not match") + } +} + +// Test success of loadRawStorage again +func TestStorageLoadRawStorageGood2(t *testing.T) { + s := initializeStorageWithFirstNode() + rsTrue := &RawStorage{} + rsTrue.standardParameters() + rsTrueBytes, err := rsTrue.Marshal() + if err != nil { + t.Fatal(err) + } + + field := "maxBytes" + value := "123456789" + epoch := uint32(257) + update, err := NewUpdate(field, value, epoch) + if err != nil { + t.Fatal(err) + } + err = s.UpdateStorage(nil, update) + if err != nil { + t.Fatal(err) + } + + // We attempt to load an epoch from an empty database; + // this should raise an error. + rs, err := s.loadRawStorage(nil, 1) + if err != nil { + t.Fatal(err) + } + rsBytes, err := rs.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(rsBytes, rsTrueBytes) { + t.Fatal("RawStorage values do not match") + } +} + +// Test failure of loadRawStorage. +// We raise an error for attempting to load epoch 0 +func TestStorageLoadRawStorageBad1(t *testing.T) { + s := initializeStorageWithFirstNode() + + // We attempt to load an epoch from an empty database; + // this should raise an error. + _, err := s.loadRawStorage(nil, 0) + if !errors.Is(err, ErrZeroEpoch) { + t.Fatal("Should have raised ErrZeroEpoch") + } +} + +// Test failure of loadRawStorage. +// We raise an error for not having LinkedList present. +func TestStorageLoadRawStorageBad2(t *testing.T) { + storageLogger := newLogger() + database := initializeDB() + s := &Storage{} + s.startChan = make(chan struct{}) + s.database = database + s.logger = storageLogger + s.Start() + + // We attempt to load an epoch from an empty database; + // this should raise an error. + _, err := s.loadRawStorage(nil, 1) + if !errors.Is(err, ErrKeyNotPresent) { + t.Fatal("Should have raised ErrKeyNotPresent") + } +} + +// Test failure of loadRawStorage again. +// It should not be possible to reach this configuration. +func TestStorageLoadStorageBad3(t *testing.T) { + storageLogger := newLogger() + database := initializeDB() + ll := &LinkedList{ + epochLastUpdated: 1, + } + err := database.SetLinkedList(nil, ll) + if err != nil { + t.Fatal(err) + } + + s := &Storage{} + s.startChan = make(chan struct{}) + s.database = database + s.logger = storageLogger + s.Start() + // We attempt to load an epoch from an empty database (without nodes but LinkedList set); + // this should raise an error. + _, err = s.loadRawStorage(nil, 1) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestStorageAddNodeHeadGood(t *testing.T) { + // Initialize storage and have standard node at epoch 1 + s := initializeStorageWithFirstNode() + epoch := uint32(1) + headNode, err := s.database.GetNode(nil, epoch) + if err != nil { + t.Fatal(err) + } + + newEpoch := epoch + 1 + rsNew := &RawStorage{} + rsNew.standardParameters() + rsBytes, err := rsNew.Marshal() + if err != nil { + t.Fatal(err) + } + newMaxBytes := uint32(1234567) + rsNew.SetMaxBytes(newMaxBytes) + node := &Node{ + thisEpoch: newEpoch, + rawStorage: rsNew, + } + if !node.IsPreValid() { + t.Fatal("node should be prevalid") + } + rsNewBytes, err := rsNew.Marshal() + if err != nil { + t.Fatal(err) + } + + err = s.addNodeHead(nil, node, headNode) + if err != nil { + t.Fatal(err) + } + + origNode, err := s.database.GetNode(nil, epoch) + if err != nil { + t.Fatal(err) + } + if origNode.prevEpoch != epoch || origNode.thisEpoch != epoch || origNode.nextEpoch != newEpoch { + t.Fatal("origNode invalid (1)") + } + rsOrigBytes, err := origNode.rawStorage.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(rsOrigBytes, rsBytes) { + t.Fatal("origNode invalid (2)") + } + + retNode, err := s.database.GetNode(nil, newEpoch) + if err != nil { + t.Fatal(err) + } + if retNode.prevEpoch != epoch || retNode.thisEpoch != newEpoch || retNode.nextEpoch != newEpoch { + t.Fatal("retNode invalid (1)") + } + retBytes, err := retNode.rawStorage.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(retBytes, rsNewBytes) { + t.Fatal("retNode invalid (2)") + } +} + +func TestStorageAddNodeHeadBad1(t *testing.T) { + origEpoch := uint32(1) + s := initializeStorageWithFirstNode() + headNode, err := s.database.GetNode(nil, origEpoch) + if err != nil { + t.Fatal(err) + } + if !headNode.IsValid() { + t.Fatal("headNode should be valid") + } + + node := &Node{} + if node.IsPreValid() { + t.Fatal("node should not be prevalid") + } + + err = s.addNodeHead(nil, node, headNode) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestStorageAddNodeHeadBad2(t *testing.T) { + origEpoch := uint32(1) + s := initializeStorageWithFirstNode() + headNode, err := s.database.GetNode(nil, origEpoch) + if err != nil { + t.Fatal(err) + } + if !headNode.IsValid() { + t.Fatal("headNode should be valid") + } + + rs := &RawStorage{} + node := &Node{ + thisEpoch: 1, + rawStorage: rs, + } + if !node.IsPreValid() { + t.Fatal("node should be prevalid") + } + + err = s.addNodeHead(nil, node, headNode) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestStorageAddNodeSplitGood(t *testing.T) { + first := uint32(1) + s := initializeStorageWithFirstNode() + prevNode, err := s.database.GetNode(nil, first) + if err != nil { + t.Fatal(err) + } + if !prevNode.IsValid() { + t.Fatal("prevNode should be valid") + } + last := uint32(10) + prevNode.nextEpoch = last + err = s.database.SetNode(nil, prevNode) + if err != nil { + t.Fatal(err) + } + + // Set up nextnode + rs := &RawStorage{} + rs.standardParameters() + rsBytes, err := rs.Marshal() + if err != nil { + t.Fatal(err) + } + nextNode := &Node{ + prevEpoch: first, + thisEpoch: last, + nextEpoch: last, + rawStorage: rs, + } + if !nextNode.IsValid() { + t.Fatal("nextNode should be valid") + } + err = s.database.SetNode(nil, nextNode) + if err != nil { + t.Fatal(err) + } + + // Set up node + rsNew, err := rs.Copy() + if err != nil { + t.Fatal(err) + } + rsNew.MaxBytes = 123456 + rsNewBytes, err := rsNew.Marshal() + if err != nil { + t.Fatal(err) + } + newEpoch := uint32(5) + node := &Node{ + thisEpoch: newEpoch, + rawStorage: rsNew, + } + if !node.IsPreValid() { + t.Fatal("node should be preValid") + } + + err = s.addNodeSplit(nil, node, prevNode, nextNode) + if err != nil { + t.Fatal(err) + } + + // Check everything + firstNode, err := s.database.GetNode(nil, first) + if err != nil { + t.Fatal(err) + } + if firstNode.prevEpoch != first || firstNode.thisEpoch != first || firstNode.nextEpoch != newEpoch { + t.Fatal("firstNode invalid (1)") + } + retBytes, err := firstNode.rawStorage.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(retBytes, rsBytes) { + t.Fatal("firstNode invalid (2)") + } + + middleNode, err := s.database.GetNode(nil, newEpoch) + if err != nil { + t.Fatal(err) + } + if middleNode.prevEpoch != first || middleNode.thisEpoch != newEpoch || middleNode.nextEpoch != last { + t.Fatal("middleNode invalid (1)") + } + retBytes, err = middleNode.rawStorage.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(retBytes, rsNewBytes) { + t.Fatal("middleNode invalid (2)") + } + + lastNode, err := s.database.GetNode(nil, last) + if err != nil { + t.Fatal(err) + } + if lastNode.prevEpoch != newEpoch || lastNode.thisEpoch != last || lastNode.nextEpoch != last { + t.Fatal("lastNode invalid (1)") + } + retBytes, err = lastNode.rawStorage.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(retBytes, rsBytes) { + t.Fatal("lastNode invalid (2)") + } +} + +func TestStorageAddNodeSplitBad1(t *testing.T) { + first := uint32(1) + s := initializeStorageWithFirstNode() + prevNode, err := s.database.GetNode(nil, first) + if err != nil { + t.Fatal(err) + } + if !prevNode.IsValid() { + t.Fatal("prevNode should be valid") + } + last := uint32(10) + prevNode.nextEpoch = last + err = s.database.SetNode(nil, prevNode) + if err != nil { + t.Fatal(err) + } + + // Set up nodes + rs := &RawStorage{} + rs.standardParameters() + nextNode := &Node{ + prevEpoch: first, + thisEpoch: last, + nextEpoch: last, + rawStorage: rs, + } + if !nextNode.IsValid() { + t.Fatal("nextNode should be valid") + } + err = s.database.SetNode(nil, nextNode) + if err != nil { + t.Fatal(err) + } + + node := &Node{} + + err = s.addNodeSplit(nil, node, prevNode, nextNode) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestStorageAddNodeSplitBad2(t *testing.T) { + first := uint32(1) + s := initializeStorageWithFirstNode() + prevNode, err := s.database.GetNode(nil, first) + if err != nil { + t.Fatal(err) + } + if !prevNode.IsValid() { + t.Fatal("prevNode should be valid") + } + last := uint32(10) + prevNode.nextEpoch = last + err = s.database.SetNode(nil, prevNode) + if err != nil { + t.Fatal(err) + } + + // Set up nodes + rs := &RawStorage{} + rs.standardParameters() + nextNode := &Node{ + prevEpoch: first, + thisEpoch: last, + nextEpoch: last, + rawStorage: rs, + } + if !nextNode.IsValid() { + t.Fatal("nextNode should be valid") + } + err = s.database.SetNode(nil, nextNode) + if err != nil { + t.Fatal(err) + } + + rsNew, err := rs.Copy() + if err != nil { + t.Fatal(err) + } + rsNew.MaxBytes = 123456 + newEpoch := uint32(100) + node := &Node{ + thisEpoch: newEpoch, + rawStorage: rsNew, + } + if !node.IsPreValid() { + t.Fatal("node should be preValid") + } + + err = s.addNodeSplit(nil, node, prevNode, nextNode) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// Test addNode when adding to Head +func TestStorageAddNodeGood1(t *testing.T) { + origEpoch := uint32(1) + s := initializeStorageWithFirstNode() + rs := &RawStorage{} + rs.standardParameters() + rsStandardBytes, err := rs.Marshal() + if err != nil { + t.Fatal(err) + } + newMaxBytes := uint32(12345) + rs.MaxBytes = newMaxBytes + epoch := uint32(10) + newNode := &Node{ + prevEpoch: 0, + thisEpoch: epoch, + nextEpoch: 0, + rawStorage: rs, + } + err = s.addNode(nil, newNode) + if err != nil { + t.Fatal(err) + } + rsNewBytes, err := rs.Marshal() + if err != nil { + t.Fatal(err) + } + + // Check everything + origNode, err := s.database.GetNode(nil, origEpoch) + if err != nil { + t.Fatal(err) + } + if origNode.prevEpoch != origEpoch { + t.Fatal("origNode.prevEpoch is invalid") + } + if origNode.thisEpoch != origEpoch { + t.Fatal("origNode.thisEpoch is invalid") + } + if origNode.nextEpoch != epoch { + t.Fatal("origNode.nextEpoch is invalid") + } + retRS, err := origNode.rawStorage.Copy() + if err != nil { + t.Fatal(err) + } + retRSBytes, err := retRS.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(retRSBytes, rsStandardBytes) { + t.Fatal("invalid RawStorage") + } + + addedNode, err := s.database.GetNode(nil, epoch) + if err != nil { + t.Fatal(err) + } + if addedNode.prevEpoch != origEpoch { + t.Fatal("addedNode.prevEpoch is invalid") + } + if addedNode.thisEpoch != epoch { + t.Fatal("addedNode.thisEpoch is invalid") + } + if addedNode.nextEpoch != epoch { + t.Fatal("addedNode.nextEpoch is invalid") + } + retRS, err = addedNode.rawStorage.Copy() + if err != nil { + t.Fatal(err) + } + retRSBytes, err = retRS.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(retRSBytes, rsNewBytes) { + t.Fatal("invalid RawStorage (2)") + } +} + +// Test addNode when adding to Head and then in between +func TestStorageAddNodeGood2(t *testing.T) { + origEpoch := uint32(1) + s := initializeStorageWithFirstNode() + rs := &RawStorage{} + rs.standardParameters() + rsStandardBytes, err := rs.Marshal() + if err != nil { + t.Fatal(err) + } + newEpoch := uint32(100) + newNode := &Node{ + prevEpoch: 0, + thisEpoch: newEpoch, + nextEpoch: 0, + rawStorage: rs, + } + err = s.addNode(nil, newNode) + if err != nil { + t.Fatal(err) + } + + newEpoch2 := uint32(10) + newMaxBytes := uint32(12345689) + rs.SetMaxBytes(newMaxBytes) + newNode2 := &Node{ + prevEpoch: 0, + thisEpoch: newEpoch2, + nextEpoch: 0, + rawStorage: rs, + } + err = s.addNode(nil, newNode2) + if err != nil { + t.Fatal(err) + } + rsNewBytes, err := rs.Marshal() + if err != nil { + t.Fatal(err) + } + + // Check everything + origNode, err := s.database.GetNode(nil, origEpoch) + if err != nil { + t.Fatal(err) + } + if origNode.prevEpoch != origEpoch { + t.Fatal("origNode.prevEpoch is invalid") + } + if origNode.thisEpoch != origEpoch { + t.Fatal("origNode.thisEpoch is invalid") + } + if origNode.nextEpoch != newEpoch2 { + t.Fatal("origNode.nextEpoch is invalid") + } + retRS, err := origNode.rawStorage.Copy() + if err != nil { + t.Fatal(err) + } + retRSBytes, err := retRS.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(retRSBytes, rsStandardBytes) { + t.Fatal("invalid RawStorage") + } + + addedNode, err := s.database.GetNode(nil, newEpoch) + if err != nil { + t.Fatal(err) + } + if addedNode.prevEpoch != newEpoch2 { + t.Fatal("addedNode.prevEpoch is invalid") + } + if addedNode.thisEpoch != newEpoch { + t.Fatal("addedNode.thisEpoch is invalid") + } + if addedNode.nextEpoch != newEpoch { + t.Fatal("addedNode.nextEpoch is invalid") + } + retRS, err = addedNode.rawStorage.Copy() + if err != nil { + t.Fatal(err) + } + retRSBytes, err = retRS.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(retRSBytes, rsStandardBytes) { + t.Fatal("invalid RawStorage (2)") + } + + addedNode2, err := s.database.GetNode(nil, newEpoch2) + if err != nil { + t.Fatal(err) + } + if addedNode2.prevEpoch != origEpoch { + t.Fatal("addedNode2.prevEpoch is invalid") + } + if addedNode2.thisEpoch != newEpoch2 { + t.Fatal("addedNode2.thisEpoch is invalid") + } + if addedNode2.nextEpoch != newEpoch { + t.Fatal("addedNode2.nextEpoch is invalid") + } + retRS, err = addedNode2.rawStorage.Copy() + if err != nil { + t.Fatal(err) + } + retRSBytes, err = retRS.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(retRSBytes, rsNewBytes) { + t.Fatal("invalid RawStorage (3)") + } +} + +func TestStorageAddNodeBad1(t *testing.T) { + s := initializeStorage() + rs := &RawStorage{} + newNode := &Node{ + prevEpoch: 0, + thisEpoch: 0, + nextEpoch: 0, + rawStorage: rs, + } + err := s.addNode(nil, newNode) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestStorageAddNodeBad2(t *testing.T) { + s := initializeStorage() + rs := &RawStorage{} + newNode := &Node{ + prevEpoch: 0, + thisEpoch: 1, + nextEpoch: 0, + rawStorage: rs, + } + err := s.addNode(nil, newNode) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestStorageAddNodeBad3(t *testing.T) { + s := initializeStorageWithFirstNode() + rs := &RawStorage{} + newNode := &Node{ + prevEpoch: 0, + thisEpoch: 1, + nextEpoch: 0, + rawStorage: rs, + } + err := s.addNode(nil, newNode) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestStorageAddNodeBad4(t *testing.T) { + s := initializeStorageWithFirstNode() + rs := &RawStorage{} + newNode := &Node{ + prevEpoch: 0, + thisEpoch: 257, + nextEpoch: 0, + rawStorage: rs, + } + err := s.addNode(nil, newNode) + if err != nil { + t.Fatal(err) + } + + newNode2 := &Node{ + prevEpoch: 0, + thisEpoch: 1, + nextEpoch: 0, + rawStorage: rs, + } + err = s.addNode(nil, newNode2) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestStorageAddNodeBad5(t *testing.T) { + s := initializeStorageWithFirstNode() + rs := &RawStorage{} + newNode := &Node{ + prevEpoch: 0, + thisEpoch: 257, + nextEpoch: 0, + rawStorage: rs, + } + err := s.addNode(nil, newNode) + if err != nil { + t.Fatal(err) + } + + newNode2 := &Node{ + prevEpoch: 0, + thisEpoch: 25519, + nextEpoch: 0, + rawStorage: rs, + } + err = s.addNode(nil, newNode2) + if err != nil { + t.Fatal(err) + } + + newNode3 := &Node{ + prevEpoch: 0, + thisEpoch: 1, + nextEpoch: 0, + rawStorage: rs, + } + err = s.addNode(nil, newNode3) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestStorageAddNodeBad6(t *testing.T) { + s := initializeStorage() + ll := &LinkedList{25519} + err := s.database.SetLinkedList(nil, ll) + if err != nil { + t.Fatal(err) + } + + rs := &RawStorage{} + newNode := &Node{ + prevEpoch: 0, + thisEpoch: 1, + nextEpoch: 0, + rawStorage: rs, + } + err = s.addNode(nil, newNode) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// Test failure of UpdateStorage +func TestStorageUpdateStorageBad(t *testing.T) { + s := initializeStorage() + epoch := uint32(25519) + field := "invalid" + value := "" + update := &Update{ + name: field, + value: value, + epoch: epoch, + } + err := s.UpdateStorage(nil, update) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// Test success of UpdateStorage +func TestStorageUpdateStorageGood1(t *testing.T) { + s := initializeStorage() + epoch := uint32(1) + field := "maxBytes" + value := "123456789" + update, err := NewUpdate(field, value, epoch) + if err != nil { + t.Fatal(err) + } + err = s.UpdateStorage(nil, update) + if err != nil { + t.Fatal(err) + } + + rs := &RawStorage{} + rs.standardParameters() + i, err := strconv.Atoi(value) + if err != nil { + t.Fatal(err) + } + rs.MaxBytes = uint32(i) + rs.MaxProposalSize = uint32(i) + rsBytes, err := rs.Marshal() + if err != nil { + t.Fatal(err) + } + + err = s.LoadStorage(nil, epoch) + if err != nil { + t.Fatal(err) + } + retRS, err := s.rawStorage.Copy() + if err != nil { + t.Fatal(err) + } + retRSBytes, err := retRS.Marshal() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(rsBytes, retRSBytes) { + t.Log(string(rsBytes)) + t.Log(string(retRSBytes)) + t.Fatal("invalid RawStorage") + } +} + +// Test success of UpdateStorage +func TestStorageUpdateStorageGood2(t *testing.T) { + s := initializeStorage() + epoch := uint32(25519) + field := "maxBytes" + value := "123456789" + update, err := NewUpdate(field, value, epoch) + if err != nil { + t.Fatal(err) + } + err = s.UpdateStorage(nil, update) + if err != nil { + t.Fatal(err) + } + + rs := &RawStorage{} + rs.standardParameters() + i, err := strconv.Atoi(value) + if err != nil { + t.Fatal(err) + } + rs.SetMaxBytes(uint32(i)) + rsBytes, err := rs.Marshal() + if err != nil { + t.Fatal(err) + } + + err = s.LoadStorage(nil, epoch) + if err != nil { + t.Fatal(err) + } + retRS, err := s.rawStorage.Copy() + if err != nil { + t.Fatal(err) + } + retRSBytes, err := retRS.Marshal() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(rsBytes, retRSBytes) { + t.Log(string(rsBytes)) + t.Log(string(retRSBytes)) + t.Fatal("invalid RawStorage") + } +} + +// Test success of UpdateStorage +func TestStorageUpdateStorageGood3(t *testing.T) { + s := initializeStorageWithFirstNode() + + // Add another epoch in the future + epoch2 := uint32(25519) + rs2 := &RawStorage{} + rs2.standardParameters() + newPropTO := 13 * time.Second + rs2.SetProposalStepTimeout(newPropTO) + node2 := &Node{ + thisEpoch: epoch2, + rawStorage: rs2, + } + err := s.addNode(nil, node2) + if err != nil { + t.Fatal(err) + } + + // Update initial storage value + epoch := uint32(1) + field := "maxBytes" + value := "123456789" + update, err := NewUpdate(field, value, epoch) + if err != nil { + t.Fatal(err) + } + err = s.UpdateStorage(nil, update) + if err != nil { + t.Fatal(err) + } + rsTrue := &RawStorage{} + rsTrue.standardParameters() + newMaxBytesInt, err := strconv.Atoi(value) + if err != nil { + t.Fatal(err) + } + rsTrue.SetMaxBytes(uint32(newMaxBytesInt)) + rsTrueBytes, err := rsTrue.Marshal() + if err != nil { + t.Fatal(err) + } + + // Test Epoch1 + err = s.LoadStorage(nil, epoch) + if err != nil { + t.Fatal(err) + } + retRS, err := s.rawStorage.Copy() + if err != nil { + t.Fatal(err) + } + retRSBytes, err := retRS.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(rsTrueBytes, retRSBytes) { + t.Log(string(rsTrueBytes)) + t.Log(string(retRSBytes)) + t.Fatal("invalid RawStorage (1)") + } + + rsTrue2 := &RawStorage{} + rsTrue2.standardParameters() + rsTrue2.SetMaxBytes(uint32(newMaxBytesInt)) + rsTrue2.SetProposalStepTimeout(newPropTO) + rsTrue2Bytes, err := rsTrue2.Marshal() + if err != nil { + t.Fatal(err) + } + + // Test Epoch2 + err = s.LoadStorage(nil, epoch2) + if err != nil { + t.Fatal(err) + } + retRS, err = s.rawStorage.Copy() + if err != nil { + t.Fatal(err) + } + retRSBytes, err = retRS.Marshal() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(rsTrue2Bytes, retRSBytes) { + t.Log(string(rsTrue2Bytes)) + t.Log(string(retRSBytes)) + t.Fatal("invalid RawStorage (2)") + } +} + +// Test failure of UpdateStorageValue +// Attempt to perform invalid update at future epoch +func TestStorageUpdateStorageValueBad1(t *testing.T) { + s := initializeStorage() + epoch := uint32(25519) + field := "invalid" + value := "" + update := &Update{ + name: field, + value: value, + epoch: epoch, + } + err := s.updateStorageValue(nil, update) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// Test failure of UpdateStorageValue +// Attempt to perform invalid update at current epoch +func TestStorageUpdateStorageValueBad2(t *testing.T) { + s := initializeStorage() + epoch := uint32(1) + field := "invalid" + value := "" + update := &Update{ + name: field, + value: value, + epoch: epoch, + } + err := s.updateStorageValue(nil, update) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// Test failure of UpdateStorageValue +// Attempt to perform invalid update at previous epoch +func TestStorageUpdateStorageValueBad3(t *testing.T) { + s := initializeStorage() + epoch := uint32(1) + field := "invalid" + value := "" + update := &Update{ + name: field, + value: value, + epoch: epoch, + } + err := s.updateStorageValue(nil, update) + if err == nil { + t.Fatal("Should have raised error") + } +} + +// Test failure of UpdateStorageValue +// Attempt to perform invalid update in between two valid storage values +func TestStorageUpdateStorageValueBad4(t *testing.T) { + s := initializeStorageWithFirstNode() + rs := &RawStorage{} + rs.standardParameters() + node := &Node{ + thisEpoch: 25519, + rawStorage: rs, + } + + err := s.addNode(nil, node) + if err != nil { + t.Fatal(err) + } + + epoch := uint32(257) + field := "invalid" + value := "" + update := &Update{ + name: field, + value: value, + epoch: epoch, + } + err = s.updateStorageValue(nil, update) + if err == nil { + t.Fatal("Should have raised error") + } +} + +func TestStorageGetMinTxFee(t *testing.T) { + s := initializeStorageWithFirstNode() + txFee := s.GetMinTxFee() + if txFee.Sign() != 0 { + t.Fatal("txFee should be zero") + } + + epoch := uint32(25519) + field := "minTxFee" + value := "123456789" + update, err := NewUpdate(field, value, epoch) + if err != nil { + t.Fatal(err) + } + err = s.UpdateStorage(nil, update) + if err != nil { + t.Fatal(err) + } + err = s.LoadStorage(nil, epoch) + if err != nil { + t.Fatal(err) + } + + valueTrue, err := stringToBigInt(value) + if err != nil { + t.Fatal(err) + } + + txFee = s.GetMinTxFee() + if err != nil { + t.Fatal(err) + } + if txFee.Cmp(valueTrue) != 0 { + t.Fatal("incorrect txFee") + } +} + +func TestStorageGetDataStoreEpochFee(t *testing.T) { + s := initializeStorageWithFirstNode() + dsEpochFee := s.GetDataStoreEpochFee() + if dsEpochFee.Sign() != 0 { + t.Fatal("dsEpochFee should be zero") + } + + epoch := uint32(25519) + field := "dataStoreEpochFee" + value := "123456789" + update, err := NewUpdate(field, value, epoch) + if err != nil { + t.Fatal(err) + } + err = s.UpdateStorage(nil, update) + if err != nil { + t.Fatal(err) + } + err = s.LoadStorage(nil, epoch) + if err != nil { + t.Fatal(err) + } + + valueTrue, err := stringToBigInt(value) + if err != nil { + t.Fatal(err) + } + + dsEpochFee = s.GetDataStoreEpochFee() + if dsEpochFee.Cmp(valueTrue) != 0 { + t.Fatal("incorrect dataStoreEpochFee") + } +} + +func TestStorageGetValueStoreFee(t *testing.T) { + s := initializeStorageWithFirstNode() + vsFee := s.GetValueStoreFee() + if vsFee.Sign() != 0 { + t.Fatal("vsFee should be zero") + } + + epoch := uint32(25519) + field := "valueStoreFee" + value := "123456789" + update, err := NewUpdate(field, value, epoch) + if err != nil { + t.Fatal(err) + } + err = s.UpdateStorage(nil, update) + if err != nil { + t.Fatal(err) + } + err = s.LoadStorage(nil, epoch) + if err != nil { + t.Fatal(err) + } + + valueTrue, err := stringToBigInt(value) + if err != nil { + t.Fatal(err) + } + + vsFee = s.GetValueStoreFee() + if vsFee.Cmp(valueTrue) != 0 { + t.Fatal("incorrect valueStoreFee") + } +} + +func TestStorageGetAtomicSwapFee(t *testing.T) { + s := initializeStorageWithFirstNode() + asFee := s.GetAtomicSwapFee() + if asFee.Sign() != 0 { + t.Fatal("asFee should be zero") + } + + epoch := uint32(25519) + field := "atomicSwapFee" + value := "123456789" + update, err := NewUpdate(field, value, epoch) + if err != nil { + t.Fatal(err) + } + err = s.UpdateStorage(nil, update) + if err != nil { + t.Fatal(err) + } + err = s.LoadStorage(nil, epoch) + if err != nil { + t.Fatal(err) + } + + valueTrue, err := stringToBigInt(value) + if err != nil { + t.Fatal(err) + } + + asFee = s.GetAtomicSwapFee() + if asFee.Cmp(valueTrue) != 0 { + t.Fatal("incorrect atomicSwapFee") + } +} diff --git a/dynamics/update.go b/dynamics/update.go new file mode 100644 index 00000000..8c1e3091 --- /dev/null +++ b/dynamics/update.go @@ -0,0 +1,192 @@ +package dynamics + +import ( + "math/big" + "strconv" + "time" +) + +// Ensuring interface check +var _ Updater = (*Update)(nil) + +// UpdateType specifies which field will be updated +type UpdateType int + +const ( + // MaxBytesType is the UpdateType for updating MaxBytes and MaxProposalSize + MaxBytesType UpdateType = iota + 1 + + // ProposalStepTimeoutType is the UpdateType for updating ProposalStepTimeout; + // this also parameterizes DownloadTimeout and DeadBlockRoundNextRoundTimeout + ProposalStepTimeoutType + + // PreVoteStepTimeoutType is the UpdateType for updating PreVoteStepTimeout; + // this also parameterizes DownloadTimeout and DeadBlockRoundNextRoundTimeout + PreVoteStepTimeoutType + + // PreCommitStepTimeoutType is the UpdateType for updating PreCommitStepTimeout; + // this also parameterizes DownloadTimeout and DeadBlockRoundNextRoundTimeout + PreCommitStepTimeoutType + + // MsgTimeoutType is the UpdateType for updating MsgTimeout; + // this also parameterizes SrvrMsgTimeout + MsgTimeoutType + + // MinTxFeeType is the UpdateType for updating MinTxFee + MinTxFeeType + + // TxValidVersionType is the UpdateType for updating TxValidVersion + TxValidVersionType + + // ValueStoreFeeType is the UpdateType for updating ValueStoreFee + ValueStoreFeeType + + // ValueStoreValidVersionType is the UpdateType for updating ValueStoreValidVersion + ValueStoreValidVersionType + + // AtomicSwapFeeType is the UpdateType for updating AtomicSwapFee + AtomicSwapFeeType + + // AtomicSwapValidStopEpochType is the UpdateType for updating AtomicSwapValidStopEpoch + AtomicSwapValidStopEpochType + + // DataStoreEpochFeeType is the UpdateType for updating DataStoreEpochFee + DataStoreEpochFeeType + + // DataStoreValidVersionType is the UpdateType for updating DataStoreValidVersion + DataStoreValidVersionType +) + +// Updater specifies the interface we use for updating Storage +type Updater interface { + Name() string + Type() UpdateType + Value() string + Epoch() uint32 +} + +// Update is an implementation of Updater interface +type Update struct { + name string + key UpdateType + value string + epoch uint32 +} + +// Name returns the name of Update +func (u *Update) Name() string { + return u.name +} + +// Type returns the type of Update +func (u *Update) Type() UpdateType { + return u.key +} + +// Value returns the value of Update +func (u *Update) Value() string { + return u.value +} + +// Epoch returns the epoch of Update +func (u *Update) Epoch() uint32 { + return u.epoch +} + +// NewUpdate makes a valid valid Update struct which is then used +func NewUpdate(field, value string, epoch uint32) (*Update, error) { + keyType, err := convertFieldToType(field) + if err != nil { + return nil, err + } + u := &Update{ + name: field, + key: keyType, + value: value, + epoch: epoch, + } + return u, nil +} + +// stringToInt32 converts a string into an int32 +func stringToInt32(value string) (int32, error) { + v64, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return 0, err + } + v := int32(v64) + return v, nil +} + +// stringToUint32 converts a string into a uint32 +func stringToUint32(value string) (uint32, error) { + v64, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return 0, err + } + v := uint32(v64) + return v, nil +} + +// stringToTimeDuration converts a string into time.Duration. +// This conversion is particular for our situation, which is why +// we do not allow negative time.Duration values. +func stringToTimeDuration(value string) (time.Duration, error) { + v64, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return 0, err + } + if v64 < 0 { + return 0, ErrInvalid + } + v := time.Duration(v64) + return v, nil +} + +// stringToBigInt converts a string into *big.Int +// This conversion is particular for our situation, which is why +// we do not allow negative big.Int values. +func stringToBigInt(value string) (*big.Int, error) { + v, valid := new(big.Int).SetString(value, 10) + if !valid { + return nil, ErrInvalid + } + if v.Sign() < 0 { + return nil, ErrInvalid + } + return v, nil +} + +// convertFieldToType returns the type associated types from the field +func convertFieldToType(field string) (UpdateType, error) { + switch field { + case "maxBytes": + return MaxBytesType, nil + case "proposalStepTimeout": + return ProposalStepTimeoutType, nil + case "preVoteStepTimeout": + return PreVoteStepTimeoutType, nil + case "preCommitStepTimeout": + return PreCommitStepTimeoutType, nil + case "msgTimeout": + return MsgTimeoutType, nil + case "minTxFee": + return MinTxFeeType, nil + case "txValidVersion": + return TxValidVersionType, nil + case "valueStoreFee": + return ValueStoreFeeType, nil + case "valueStoreValidVersion": + return ValueStoreValidVersionType, nil + case "atomicSwapFee": + return AtomicSwapFeeType, nil + case "atomicSwapValidStopEpoch": + return AtomicSwapValidStopEpochType, nil + case "dataStoreEpochFee": + return DataStoreEpochFeeType, nil + case "dataStoreValidVersion": + return DataStoreValidVersionType, nil + default: + return UpdateType(0), ErrInvalid + } +} diff --git a/dynamics/update_test.go b/dynamics/update_test.go new file mode 100644 index 00000000..cfa02610 --- /dev/null +++ b/dynamics/update_test.go @@ -0,0 +1,378 @@ +package dynamics + +import ( + "math/big" + "testing" + "time" + + "github.com/MadBase/MadNet/constants" +) + +func TestUpdateGet(t *testing.T) { + update := &Update{} + if update.Name() != "" { + t.Fatal("Should have raised error (1)") + } + if update.Type() != UpdateType(0) { + t.Fatal("Should have raised error (2)") + } + if update.Value() != "" { + t.Fatal("Should have raised error (3)") + } + if update.Epoch() != 0 { + t.Fatal("Should have raised error (4)") + } +} + +func TestUpdateNewUpdate(t *testing.T) { + fieldBad := "" + value := "123456789" + epoch := uint32(1) + _, err := NewUpdate(fieldBad, value, epoch) + if err == nil { + t.Fatal("Should have raised error") + } + + fieldGood := "maxBytes" + update, err := NewUpdate(fieldGood, value, epoch) + if err != nil { + t.Fatal(err) + } + if update.Name() != fieldGood { + t.Fatal("invalid update name") + } + if update.Type() != MaxBytesType { + t.Fatal("invalid update type") + } + if update.Value() != value { + t.Fatal("invalid update value") + } + if update.Epoch() != epoch { + t.Fatal("invalid update epoch") + } +} + +func TestStringToInt32(t *testing.T) { + value := "" + _, err := stringToInt32(value) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + value = "0" + v, err := stringToInt32(value) + if err != nil { + t.Fatal(err) + } + if v != 0 { + t.Fatal("invalid conversion (1)") + } + + value = "1" + v, err = stringToInt32(value) + if err != nil { + t.Fatal(err) + } + if v != 1 { + t.Fatal("invalid conversion (2)") + } + + value = "2147483647" // maxInt32 + v, err = stringToInt32(value) + if err != nil { + t.Fatal(err) + } + if v != 2147483647 { + t.Fatal("invalid conversion (3)") + } + + value = "2147483648" + _, err = stringToInt32(value) + if err == nil { + t.Fatal("Should have raised error (2)") + } +} + +func TestStringToUint32(t *testing.T) { + value := "" + _, err := stringToUint32(value) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + value = "0" + v, err := stringToUint32(value) + if err != nil { + t.Fatal(err) + } + if v != 0 { + t.Fatal("invalid conversion (1)") + } + + value = "1" + v, err = stringToUint32(value) + if err != nil { + t.Fatal(err) + } + if v != 1 { + t.Fatal("invalid conversion (2)") + } + + value = "4294967295" + v, err = stringToUint32(value) + if err != nil { + t.Fatal(err) + } + if v != constants.MaxUint32 { + t.Fatal("invalid conversion (3)") + } + + value = "4294967296" + _, err = stringToUint32(value) + if err == nil { + t.Fatal("Should have raised error (2)") + } +} + +func TestStringToTimeDuration(t *testing.T) { + value := "" + _, err := stringToTimeDuration(value) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + value = "0" + v, err := stringToTimeDuration(value) + if err != nil { + t.Fatal(err) + } + if v != 0*time.Second { + t.Fatal("invalid conversion (1)") + } + + value = "1" + v, err = stringToTimeDuration(value) + if err != nil { + t.Fatal(err) + } + if v != 1*time.Nanosecond { + t.Fatal("invalid conversion (2)") + } + + value = "1000" + v, err = stringToTimeDuration(value) + if err != nil { + t.Fatal(err) + } + if v != 1*time.Microsecond { + t.Fatal("invalid conversion (3)") + } + + value = "1000000" + v, err = stringToTimeDuration(value) + if err != nil { + t.Fatal(err) + } + if v != 1*time.Millisecond { + t.Fatal("invalid conversion (4)") + } + + value = "1000000000" + v, err = stringToTimeDuration(value) + if err != nil { + t.Fatal(err) + } + if v != 1*time.Second { + t.Fatal("invalid conversion (5)") + } + + value = "9223372036854775807" + v, err = stringToTimeDuration(value) + if err != nil { + t.Fatal(err) + } + if v != (9223372036*time.Second + 854775807*time.Nanosecond) { + t.Fatal("invalid conversion (6)") + } + + value = "9223372036854775808" + _, err = stringToTimeDuration(value) + if err == nil { + t.Fatal("Should have raised error (2)") + } + + value = "-1" + _, err = stringToTimeDuration(value) + if err == nil { + t.Fatal("Should have raised error (3)") + } +} + +func TestStringToBigInt(t *testing.T) { + value := "" + _, err := stringToBigInt(value) + if err == nil { + t.Fatal("Should have raised error (1)") + } + + vTrue := big.NewInt(0) + value = "0" + v, err := stringToBigInt(value) + if err != nil { + t.Fatal(err) + } + if v.Cmp(vTrue) != 0 { + t.Fatal("invalid conversion (1)") + } + + vTrue = big.NewInt(1) + value = "1" + v, err = stringToBigInt(value) + if err != nil { + t.Fatal(err) + } + if v.Cmp(vTrue) != 0 { + t.Fatal("invalid conversion (2)") + } + + vTrue = big.NewInt(25519) + value = "25519" + v, err = stringToBigInt(value) + if err != nil { + t.Fatal(err) + } + if v.Cmp(vTrue) != 0 { + t.Fatal("invalid conversion (3)") + } + + value = "-1" + _, err = stringToBigInt(value) + if err == nil { + t.Fatal("Should have raised error (3)") + } +} + +func TestConvertFieldToType(t *testing.T) { + field := "" + _, err := convertFieldToType(field) + if err == nil { + t.Fatal("Should have raised error (0)") + } + + field = "maxBytes" + uType, err := convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != MaxBytesType { + t.Fatal("Incorrect UpdateType (1)") + } + + field = "proposalStepTimeout" + uType, err = convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != ProposalStepTimeoutType { + t.Fatal("Incorrect UpdateType (2)") + } + + field = "preVoteStepTimeout" + uType, err = convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != PreVoteStepTimeoutType { + t.Fatal("Incorrect UpdateType (3)") + } + + field = "preCommitStepTimeout" + uType, err = convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != PreCommitStepTimeoutType { + t.Fatal("Incorrect UpdateType (4)") + } + + field = "msgTimeout" + uType, err = convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != MsgTimeoutType { + t.Fatal("Incorrect UpdateType (5)") + } + + field = "minTxFee" + uType, err = convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != MinTxFeeType { + t.Fatal("Incorrect UpdateType (6)") + } + + field = "txValidVersion" + uType, err = convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != TxValidVersionType { + t.Fatal("Incorrect UpdateType (7)") + } + + field = "valueStoreFee" + uType, err = convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != ValueStoreFeeType { + t.Fatal("Incorrect UpdateType (8)") + } + + field = "valueStoreValidVersion" + uType, err = convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != ValueStoreValidVersionType { + t.Fatal("Incorrect UpdateType (9)") + } + + field = "atomicSwapFee" + uType, err = convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != AtomicSwapFeeType { + t.Fatal("Incorrect UpdateType (10)") + } + + field = "atomicSwapValidStopEpoch" + uType, err = convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != AtomicSwapValidStopEpochType { + t.Fatal("Incorrect UpdateType (11)") + } + + field = "dataStoreEpochFee" + uType, err = convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != DataStoreEpochFeeType { + t.Fatal("Incorrect UpdateType (12)") + } + + field = "dataStoreValidVersion" + uType, err = convertFieldToType(field) + if err != nil { + t.Fatal(err) + } + if uType != DataStoreValidVersionType { + t.Fatal("Incorrect UpdateType (13)") + } +} diff --git a/go.mod b/go.mod index ad1c0bdf..9c3a6251 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.15 require ( github.com/MadBase/bridge v0.7.0 github.com/dgraph-io/badger/v2 v2.0.3 - github.com/dgraph-io/ristretto v0.0.2 // indirect github.com/elazarl/go-bindata-assetfs v1.0.1 github.com/emicklei/proto v1.9.0 github.com/ethereum/go-ethereum v1.10.6 diff --git a/go.sum b/go.sum index bc69c93c..98473de6 100644 --- a/go.sum +++ b/go.sum @@ -124,9 +124,8 @@ github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/dgraph-io/badger/v2 v2.0.3 h1:inzdf6VF/NZ+tJ8RwwYMjJMvsOALTHYdozn0qSl6XJI= github.com/dgraph-io/badger/v2 v2.0.3/go.mod h1:3KY8+bsP8wI0OEnQJAKpd4wIJW/Mm32yw2j/9FUVnIM= +github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3 h1:MQLRM35Pp0yAyBYksjbj1nZI/w6eyRY/mWoM1sFf4kU= github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po= -github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= diff --git a/localrpc/translateAobjsFwd.go b/localrpc/translateAobjsFwd.go index f48d7ac3..8f34702b 100644 --- a/localrpc/translateAobjsFwd.go +++ b/localrpc/translateAobjsFwd.go @@ -7,6 +7,16 @@ import ( to "github.com/MadBase/MadNet/proto" ) +// TODO: ForwardTranslateTXOut and ReverseTranslateTXOut need to be updated +// to include TxFee option in switch case. There is no option at this +// point. +// +// The challenge is that there is nothing in particular that should +// require this information to be fully transmitted, as anyone is +// able to make a related TxFee object. This is because Tx objects +// will eventually be transmitted and will *include* TxFee objects. +// How this is specified as compactly as possible should be investigated. + func ForwardTranslateDataStore(f *from.DataStore) (*to.DataStore, error) { t := &to.DataStore{} if f == nil { @@ -99,6 +109,10 @@ func ForwardTranslateVSPreImage(f *from.VSPreImage) (*to.VSPreImage, error) { if err != nil { return nil, err } + t.Fee, err = f.Fee.MarshalString() + if err != nil { + return nil, err + } return t, nil } @@ -130,6 +144,10 @@ func ForwardTranslateASPreImage(f *from.ASPreImage) (*to.ASPreImage, error) { if err != nil { return nil, err } + t.Fee, err = f.Fee.MarshalString() + if err != nil { + return nil, err + } return t, nil } @@ -291,6 +309,10 @@ func ForwardTranslateDSPreImage(f *from.DSPreImage) (*to.DSPreImage, error) { if err != nil { return nil, err } + t.Fee, err = f.Fee.MarshalString() + if err != nil { + return nil, err + } newIndex, err := ForwardTranslateByte(f.Index) if err != nil { return nil, err diff --git a/localrpc/translateAobjsRev.go b/localrpc/translateAobjsRev.go index 654215fc..7fa34cfa 100644 --- a/localrpc/translateAobjsRev.go +++ b/localrpc/translateAobjsRev.go @@ -86,6 +86,11 @@ func ReverseTranslateVSPreImage(f *from.VSPreImage) (*to.VSPreImage, error) { if err != nil { return nil, err } + t.Fee = &uint256.Uint256{} + err = t.Fee.UnmarshalString(f.Fee) + if err != nil { + return nil, err + } b, err := t.MarshalBinary() if err != nil { return nil, err @@ -120,6 +125,11 @@ func ReverseTranslateASPreImage(f *from.ASPreImage) (*to.ASPreImage, error) { if err != nil { return nil, err } + t.Fee = &uint256.Uint256{} + err = t.Fee.UnmarshalString(f.Fee) + if err != nil { + return nil, err + } b, err := t.MarshalBinary() if err != nil { return nil, err @@ -283,6 +293,11 @@ func ReverseTranslateDSPreImage(f *from.DSPreImage) (*to.DSPreImage, error) { if err != nil { return nil, err } + t.Fee = &uint256.Uint256{} + err = t.Fee.UnmarshalString(f.Fee) + if err != nil { + return nil, err + } newIndex, err := ReverseTranslateByte(f.Index) if err != nil { return nil, err diff --git a/proto/aobjs.pb.go b/proto/aobjs.pb.go index a2f2cc9a..b39eee91 100644 --- a/proto/aobjs.pb.go +++ b/proto/aobjs.pb.go @@ -1,13 +1,12 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0 +// protoc-gen-go v1.26.0 // protoc v3.11.2 // source: aobjs.proto package proto import ( - proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -21,10 +20,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - // Protobuf message implementation for struct Tx type Tx struct { state protoimpl.MessageState @@ -91,6 +86,7 @@ type TXOut struct { // *TXOut_AtomicSwap // *TXOut_ValueStore // *TXOut_DataStore + // *TXOut_TxFee Utxo isTXOut_Utxo `protobuf_oneof:"utxo"` } @@ -154,6 +150,13 @@ func (x *TXOut) GetDataStore() *DataStore { return nil } +func (x *TXOut) GetTxFee() *TxFee { + if x, ok := x.GetUtxo().(*TXOut_TxFee); ok { + return x.TxFee + } + return nil +} + type isTXOut_Utxo interface { isTXOut_Utxo() } @@ -170,12 +173,18 @@ type TXOut_DataStore struct { DataStore *DataStore `protobuf:"bytes,3,opt,name=DataStore,proto3,oneof"` } +type TXOut_TxFee struct { + TxFee *TxFee `protobuf:"bytes,4,opt,name=TxFee,proto3,oneof"` +} + func (*TXOut_AtomicSwap) isTXOut_Utxo() {} func (*TXOut_ValueStore) isTXOut_Utxo() {} func (*TXOut_DataStore) isTXOut_Utxo() {} +func (*TXOut_TxFee) isTXOut_Utxo() {} + // Protobuf message implementation for struct TXIn type TXIn struct { state protoimpl.MessageState @@ -420,6 +429,7 @@ type ASPreImage struct { IssuedAt uint32 `protobuf:"varint,4,opt,name=IssuedAt,proto3" json:"IssuedAt,omitempty"` Exp uint32 `protobuf:"varint,5,opt,name=Exp,proto3" json:"Exp,omitempty"` Owner string `protobuf:"bytes,6,opt,name=Owner,proto3" json:"Owner,omitempty"` + Fee string `protobuf:"bytes,7,opt,name=Fee,proto3" json:"Fee,omitempty"` } func (x *ASPreImage) Reset() { @@ -496,6 +506,13 @@ func (x *ASPreImage) GetOwner() string { return "" } +func (x *ASPreImage) GetFee() string { + if x != nil { + return x.Fee + } + return "" +} + // Protobuf message implementation for struct ValueStore type ValueStore struct { state protoimpl.MessageState @@ -562,6 +579,7 @@ type VSPreImage struct { Value string `protobuf:"bytes,2,opt,name=Value,proto3" json:"Value,omitempty"` TXOutIdx uint32 `protobuf:"varint,3,opt,name=TXOutIdx,proto3" json:"TXOutIdx,omitempty"` Owner string `protobuf:"bytes,4,opt,name=Owner,proto3" json:"Owner,omitempty"` + Fee string `protobuf:"bytes,5,opt,name=Fee,proto3" json:"Fee,omitempty"` } func (x *VSPreImage) Reset() { @@ -624,6 +642,13 @@ func (x *VSPreImage) GetOwner() string { return "" } +func (x *VSPreImage) GetFee() string { + if x != nil { + return x.Fee + } + return "" +} + // Protobuf message implementation for struct DataStore type DataStore struct { state protoimpl.MessageState @@ -749,6 +774,7 @@ type DSPreImage struct { RawData string `protobuf:"bytes,5,opt,name=RawData,proto3" json:"RawData,omitempty"` TXOutIdx uint32 `protobuf:"varint,6,opt,name=TXOutIdx,proto3" json:"TXOutIdx,omitempty"` Owner string `protobuf:"bytes,7,opt,name=Owner,proto3" json:"Owner,omitempty"` + Fee string `protobuf:"bytes,8,opt,name=Fee,proto3" json:"Fee,omitempty"` } func (x *DSPreImage) Reset() { @@ -832,6 +858,133 @@ func (x *DSPreImage) GetOwner() string { return "" } +func (x *DSPreImage) GetFee() string { + if x != nil { + return x.Fee + } + return "" +} + +// Protobuf message implementation for struct TxFee +type TxFee struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TFPreImage *TFPreImage `protobuf:"bytes,1,opt,name=TFPreImage,proto3" json:"TFPreImage,omitempty"` + TxHash string `protobuf:"bytes,2,opt,name=TxHash,proto3" json:"TxHash,omitempty"` +} + +func (x *TxFee) Reset() { + *x = TxFee{} + if protoimpl.UnsafeEnabled { + mi := &file_aobjs_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TxFee) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TxFee) ProtoMessage() {} + +func (x *TxFee) ProtoReflect() protoreflect.Message { + mi := &file_aobjs_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TxFee.ProtoReflect.Descriptor instead. +func (*TxFee) Descriptor() ([]byte, []int) { + return file_aobjs_proto_rawDescGZIP(), []int{12} +} + +func (x *TxFee) GetTFPreImage() *TFPreImage { + if x != nil { + return x.TFPreImage + } + return nil +} + +func (x *TxFee) GetTxHash() string { + if x != nil { + return x.TxHash + } + return "" +} + +// Protobuf message implementation for struct TFPreImage +type TFPreImage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainID uint32 `protobuf:"varint,1,opt,name=ChainID,proto3" json:"ChainID,omitempty"` + TXOutIdx uint32 `protobuf:"varint,2,opt,name=TXOutIdx,proto3" json:"TXOutIdx,omitempty"` + Fee string `protobuf:"bytes,3,opt,name=Fee,proto3" json:"Fee,omitempty"` +} + +func (x *TFPreImage) Reset() { + *x = TFPreImage{} + if protoimpl.UnsafeEnabled { + mi := &file_aobjs_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TFPreImage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TFPreImage) ProtoMessage() {} + +func (x *TFPreImage) ProtoReflect() protoreflect.Message { + mi := &file_aobjs_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TFPreImage.ProtoReflect.Descriptor instead. +func (*TFPreImage) Descriptor() ([]byte, []int) { + return file_aobjs_proto_rawDescGZIP(), []int{13} +} + +func (x *TFPreImage) GetChainID() uint32 { + if x != nil { + return x.ChainID + } + return 0 +} + +func (x *TFPreImage) GetTXOutIdx() uint32 { + if x != nil { + return x.TXOutIdx + } + return 0 +} + +func (x *TFPreImage) GetFee() string { + if x != nil { + return x.Fee + } + return "" +} + var File_aobjs_proto protoreflect.FileDescriptor var file_aobjs_proto_rawDesc = []byte{ @@ -840,7 +993,7 @@ var file_aobjs_proto_rawDesc = []byte{ 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x58, 0x49, 0x6e, 0x52, 0x03, 0x56, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x04, 0x56, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x54, 0x58, 0x4f, 0x75, 0x74, 0x52, 0x04, 0x56, 0x6f, 0x75, 0x74, 0x22, 0xab, 0x01, 0x0a, 0x05, + 0x54, 0x58, 0x4f, 0x75, 0x74, 0x52, 0x04, 0x56, 0x6f, 0x75, 0x74, 0x22, 0xd1, 0x01, 0x0a, 0x05, 0x54, 0x58, 0x4f, 0x75, 0x74, 0x12, 0x33, 0x0a, 0x0a, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x53, 0x77, 0x61, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x53, 0x77, 0x61, 0x70, 0x48, 0x00, 0x52, 0x0a, @@ -851,78 +1004,96 @@ var file_aobjs_proto_rawDesc = []byte{ 0x30, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x48, 0x00, 0x52, 0x09, 0x44, 0x61, 0x74, 0x61, 0x53, 0x74, 0x6f, 0x72, - 0x65, 0x42, 0x06, 0x0a, 0x04, 0x75, 0x74, 0x78, 0x6f, 0x22, 0x57, 0x0a, 0x04, 0x54, 0x58, 0x49, - 0x6e, 0x12, 0x31, 0x0a, 0x0a, 0x54, 0x58, 0x49, 0x6e, 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x58, - 0x49, 0x6e, 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x52, 0x0a, 0x54, 0x58, 0x49, 0x6e, 0x4c, 0x69, - 0x6e, 0x6b, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x22, 0x5d, 0x0a, 0x0a, 0x54, 0x58, 0x49, 0x6e, 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72, - 0x12, 0x37, 0x0a, 0x0c, 0x54, 0x58, 0x49, 0x6e, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, - 0x58, 0x49, 0x6e, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x0c, 0x54, 0x58, 0x49, - 0x6e, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x54, 0x78, 0x48, - 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, - 0x68, 0x22, 0x76, 0x0a, 0x0c, 0x54, 0x58, 0x49, 0x6e, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x07, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x43, - 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x54, 0x78, 0x49, 0x64, 0x78, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0d, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x54, 0x78, 0x49, 0x64, - 0x78, 0x12, 0x26, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x54, 0x78, 0x48, - 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x43, 0x6f, 0x6e, 0x73, 0x75, - 0x6d, 0x65, 0x64, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x22, 0x57, 0x0a, 0x0a, 0x41, 0x74, 0x6f, - 0x6d, 0x69, 0x63, 0x53, 0x77, 0x61, 0x70, 0x12, 0x31, 0x0a, 0x0a, 0x41, 0x53, 0x50, 0x72, 0x65, + 0x65, 0x12, 0x24, 0x0a, 0x05, 0x54, 0x78, 0x46, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x78, 0x46, 0x65, 0x65, 0x48, 0x00, + 0x52, 0x05, 0x54, 0x78, 0x46, 0x65, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x75, 0x74, 0x78, 0x6f, 0x22, + 0x57, 0x0a, 0x04, 0x54, 0x58, 0x49, 0x6e, 0x12, 0x31, 0x0a, 0x0a, 0x54, 0x58, 0x49, 0x6e, 0x4c, + 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x58, 0x49, 0x6e, 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x52, 0x0a, + 0x54, 0x58, 0x49, 0x6e, 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x5d, 0x0a, 0x0a, 0x54, 0x58, 0x49, 0x6e, + 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x0c, 0x54, 0x58, 0x49, 0x6e, 0x50, 0x72, + 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x58, 0x49, 0x6e, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, + 0x65, 0x52, 0x0c, 0x54, 0x58, 0x49, 0x6e, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x22, 0x76, 0x0a, 0x0c, 0x54, 0x58, 0x49, 0x6e, 0x50, + 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, + 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x54, 0x78, 0x49, + 0x64, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, + 0x65, 0x64, 0x54, 0x78, 0x49, 0x64, 0x78, 0x12, 0x26, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x73, 0x75, + 0x6d, 0x65, 0x64, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x22, + 0x57, 0x0a, 0x0a, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x53, 0x77, 0x61, 0x70, 0x12, 0x31, 0x0a, + 0x0a, 0x41, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x53, 0x50, 0x72, 0x65, 0x49, + 0x6d, 0x61, 0x67, 0x65, 0x52, 0x0a, 0x41, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x22, 0xae, 0x01, 0x0a, 0x0a, 0x41, 0x53, 0x50, + 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, + 0x44, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x54, 0x58, 0x4f, 0x75, 0x74, + 0x49, 0x64, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x54, 0x58, 0x4f, 0x75, 0x74, + 0x49, 0x64, 0x78, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x73, 0x73, 0x75, 0x65, 0x64, 0x41, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x49, 0x73, 0x73, 0x75, 0x65, 0x64, 0x41, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x45, 0x78, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x45, 0x78, + 0x70, 0x12, 0x14, 0x0a, 0x05, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x46, 0x65, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x46, 0x65, 0x65, 0x22, 0x57, 0x0a, 0x0a, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x31, 0x0a, 0x0a, 0x56, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x0a, - 0x41, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x54, 0x78, + 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x0a, + 0x56, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x54, 0x78, 0x48, 0x61, - 0x73, 0x68, 0x22, 0x9c, 0x01, 0x0a, 0x0a, 0x41, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, + 0x73, 0x68, 0x22, 0x80, 0x01, 0x0a, 0x0a, 0x56, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x54, 0x58, 0x4f, 0x75, 0x74, 0x49, 0x64, 0x78, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x08, 0x54, 0x58, 0x4f, 0x75, 0x74, 0x49, 0x64, 0x78, 0x12, 0x1a, 0x0a, - 0x08, 0x49, 0x73, 0x73, 0x75, 0x65, 0x64, 0x41, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x08, 0x49, 0x73, 0x73, 0x75, 0x65, 0x64, 0x41, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x45, 0x78, 0x70, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x45, 0x78, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x4f, - 0x77, 0x6e, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4f, 0x77, 0x6e, 0x65, - 0x72, 0x22, 0x57, 0x0a, 0x0a, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, - 0x31, 0x0a, 0x0a, 0x56, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x53, 0x50, 0x72, - 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x0a, 0x56, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, - 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x22, 0x6e, 0x0a, 0x0a, 0x56, 0x53, - 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x54, 0x58, 0x4f, 0x75, - 0x74, 0x49, 0x64, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x54, 0x58, 0x4f, 0x75, - 0x74, 0x49, 0x64, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x22, 0x56, 0x0a, 0x09, 0x44, 0x61, - 0x74, 0x61, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x44, 0x53, 0x4c, 0x69, 0x6e, - 0x6b, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x44, 0x53, 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x52, 0x08, 0x44, 0x53, 0x4c, 0x69, - 0x6e, 0x6b, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x22, 0x55, 0x0a, 0x08, 0x44, 0x53, 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x12, 0x31, - 0x0a, 0x0a, 0x44, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x53, 0x50, 0x72, 0x65, - 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x0a, 0x44, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, - 0x65, 0x12, 0x16, 0x0a, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x22, 0xbe, 0x01, 0x0a, 0x0a, 0x44, 0x53, - 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x73, 0x73, 0x75, - 0x65, 0x64, 0x41, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x49, 0x73, 0x73, 0x75, - 0x65, 0x64, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x18, - 0x0a, 0x07, 0x52, 0x61, 0x77, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x52, 0x61, 0x77, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x54, 0x58, 0x4f, 0x75, - 0x74, 0x49, 0x64, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x54, 0x58, 0x4f, 0x75, - 0x74, 0x49, 0x64, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x01, 0x28, 0x0d, 0x52, 0x08, 0x54, 0x58, 0x4f, 0x75, 0x74, 0x49, 0x64, 0x78, 0x12, 0x14, 0x0a, + 0x05, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4f, 0x77, + 0x6e, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x46, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x46, 0x65, 0x65, 0x22, 0x56, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x53, 0x74, 0x6f, + 0x72, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x44, 0x53, 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x53, 0x4c, + 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x52, 0x08, 0x44, 0x53, 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x12, + 0x1c, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x55, 0x0a, + 0x08, 0x44, 0x53, 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x0a, 0x44, 0x53, 0x50, + 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, + 0x52, 0x0a, 0x44, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x54, 0x78, + 0x48, 0x61, 0x73, 0x68, 0x22, 0xd0, 0x01, 0x0a, 0x0a, 0x44, 0x53, 0x50, 0x72, 0x65, 0x49, 0x6d, + 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x44, 0x12, 0x14, 0x0a, + 0x05, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x73, 0x73, 0x75, 0x65, 0x64, 0x41, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x49, 0x73, 0x73, 0x75, 0x65, 0x64, 0x41, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x52, 0x61, 0x77, + 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x52, 0x61, 0x77, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x54, 0x58, 0x4f, 0x75, 0x74, 0x49, 0x64, 0x78, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x54, 0x58, 0x4f, 0x75, 0x74, 0x49, 0x64, 0x78, 0x12, + 0x14, 0x0a, 0x05, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x46, 0x65, 0x65, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x46, 0x65, 0x65, 0x22, 0x52, 0x0a, 0x05, 0x54, 0x78, 0x46, 0x65, 0x65, + 0x12, 0x31, 0x0a, 0x0a, 0x54, 0x46, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x46, 0x50, + 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x0a, 0x54, 0x46, 0x50, 0x72, 0x65, 0x49, 0x6d, + 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x22, 0x54, 0x0a, 0x0a, 0x54, + 0x46, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x43, 0x68, 0x61, 0x69, + 0x6e, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x54, 0x58, 0x4f, 0x75, 0x74, 0x49, 0x64, 0x78, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x54, 0x58, 0x4f, 0x75, 0x74, 0x49, 0x64, 0x78, 0x12, + 0x10, 0x0a, 0x03, 0x46, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x46, 0x65, + 0x65, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x4d, 0x61, 0x64, 0x42, 0x61, 0x73, 0x65, 0x2f, 0x4d, 0x61, 0x64, 0x4e, 0x65, 0x74, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -937,7 +1108,7 @@ func file_aobjs_proto_rawDescGZIP() []byte { return file_aobjs_proto_rawDescData } -var file_aobjs_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_aobjs_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_aobjs_proto_goTypes = []interface{}{ (*Tx)(nil), // 0: proto.Tx (*TXOut)(nil), // 1: proto.TXOut @@ -951,6 +1122,8 @@ var file_aobjs_proto_goTypes = []interface{}{ (*DataStore)(nil), // 9: proto.DataStore (*DSLinker)(nil), // 10: proto.DSLinker (*DSPreImage)(nil), // 11: proto.DSPreImage + (*TxFee)(nil), // 12: proto.TxFee + (*TFPreImage)(nil), // 13: proto.TFPreImage } var file_aobjs_proto_depIdxs = []int32{ 2, // 0: proto.Tx.Vin:type_name -> proto.TXIn @@ -958,17 +1131,19 @@ var file_aobjs_proto_depIdxs = []int32{ 5, // 2: proto.TXOut.AtomicSwap:type_name -> proto.AtomicSwap 7, // 3: proto.TXOut.ValueStore:type_name -> proto.ValueStore 9, // 4: proto.TXOut.DataStore:type_name -> proto.DataStore - 3, // 5: proto.TXIn.TXInLinker:type_name -> proto.TXInLinker - 4, // 6: proto.TXInLinker.TXInPreImage:type_name -> proto.TXInPreImage - 6, // 7: proto.AtomicSwap.ASPreImage:type_name -> proto.ASPreImage - 8, // 8: proto.ValueStore.VSPreImage:type_name -> proto.VSPreImage - 10, // 9: proto.DataStore.DSLinker:type_name -> proto.DSLinker - 11, // 10: proto.DSLinker.DSPreImage:type_name -> proto.DSPreImage - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 12, // 5: proto.TXOut.TxFee:type_name -> proto.TxFee + 3, // 6: proto.TXIn.TXInLinker:type_name -> proto.TXInLinker + 4, // 7: proto.TXInLinker.TXInPreImage:type_name -> proto.TXInPreImage + 6, // 8: proto.AtomicSwap.ASPreImage:type_name -> proto.ASPreImage + 8, // 9: proto.ValueStore.VSPreImage:type_name -> proto.VSPreImage + 10, // 10: proto.DataStore.DSLinker:type_name -> proto.DSLinker + 11, // 11: proto.DSLinker.DSPreImage:type_name -> proto.DSPreImage + 13, // 12: proto.TxFee.TFPreImage:type_name -> proto.TFPreImage + 13, // [13:13] is the sub-list for method output_type + 13, // [13:13] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } func init() { file_aobjs_proto_init() } @@ -1121,11 +1296,36 @@ func file_aobjs_proto_init() { return nil } } + file_aobjs_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TxFee); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aobjs_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TFPreImage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_aobjs_proto_msgTypes[1].OneofWrappers = []interface{}{ (*TXOut_AtomicSwap)(nil), (*TXOut_ValueStore)(nil), (*TXOut_DataStore)(nil), + (*TXOut_TxFee)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -1133,7 +1333,7 @@ func file_aobjs_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_aobjs_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 14, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/aobjs.proto b/proto/aobjs.proto index ac1b294c..c0129aa4 100644 --- a/proto/aobjs.proto +++ b/proto/aobjs.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package proto; -//option go_package = "github.com/MadBase/MadNet/proto"; +option go_package = "github.com/MadBase/MadNet/proto"; // Protobuf message implementation for struct Tx message Tx { @@ -16,6 +16,7 @@ message TXOut { AtomicSwap AtomicSwap = 1; ValueStore ValueStore = 2; DataStore DataStore = 3; + TxFee TxFee = 4; } } @@ -57,6 +58,7 @@ message ASPreImage { uint32 IssuedAt = 4; uint32 Exp = 5; string Owner = 6; + string Fee = 7; } // Protobuf message implementation for struct ValueStore @@ -71,7 +73,8 @@ message VSPreImage { uint32 ChainID = 1; string Value = 2; uint32 TXOutIdx = 3; - string Owner = 4; + string Owner = 4; + string Fee = 5; } @@ -98,4 +101,20 @@ message DSPreImage { string RawData = 5; uint32 TXOutIdx = 6; string Owner = 7; + string Fee = 8; +} + + +// Protobuf message implementation for struct TxFee +message TxFee { + TFPreImage TFPreImage = 1; + string TxHash = 2; +} + + +// Protobuf message implementation for struct TFPreImage +message TFPreImage { + uint32 ChainID = 1; + uint32 TXOutIdx = 2; + string Fee = 3; }