diff --git a/common/custodian.go b/common/custodian.go index 56f811e03..fba3ba6d0 100644 --- a/common/custodian.go +++ b/common/custodian.go @@ -2,13 +2,17 @@ package common import "fmt" -type CustodianInfo struct { +type CustodianNode struct { Custodian Address Payee Address Extra []byte } -func (ci *CustodianInfo) Validate() error { +func (ci *CustodianNode) validate() error { + panic(0) +} + +func (tx *Transaction) parseCustodianUpdateNodesExtra() (*Address, []*CustodianNode, error) { panic(0) } diff --git a/common/validation.go b/common/validation.go index 1eead0cdd..2b770779a 100644 --- a/common/validation.go +++ b/common/validation.go @@ -113,10 +113,14 @@ func (tx *SignedTransaction) getExtraLimit() int { if len(out.Keys) != 1 { return ExtraSizeGeneralLimit } - if out.Type != OutputTypeScript { + if out.Script.String() != "fffe40" { return ExtraSizeGeneralLimit } - if out.Script.String() != "fffe40" { + switch out.Type { + case OutputTypeScript: + case OutputTypeCustodianUpdateNodes: + return ExtraSizeStorageCapacity + default: return ExtraSizeGeneralLimit } step := NewIntegerFromString(ExtraStoragePriceStep) diff --git a/kernel/mint.go b/kernel/mint.go index 2026c34ed..1cd165ea7 100644 --- a/kernel/mint.go +++ b/kernel/mint.go @@ -109,7 +109,7 @@ func (node *Node) MintLoop() { case <-node.done: return case <-ticker.C: - ca, err := node.persistStore.ReadCustodianAccount() + ca, _, err := node.persistStore.ReadCustodianAccount() if err != nil { panic(err) } diff --git a/storage/badger_custodian.go b/storage/badger_custodian.go index 6423334b9..0df0c58a2 100644 --- a/storage/badger_custodian.go +++ b/storage/badger_custodian.go @@ -1,31 +1,76 @@ package storage import ( + "encoding/binary" + "github.com/MixinNetwork/mixin/common" "github.com/dgraph-io/badger/v4" ) -func (s *BadgerStore) writeCustodianNodes(txn *badger.Txn, snap *common.Snapshot, custodian *common.Address, nodes []*common.CustodianInfo) error { - panic(0) -} - -func (s *BadgerStore) ReadCustodianAccount() (*common.Address, error) { +func (s *BadgerStore) ReadCustodianAccount() (*common.Address, uint64, error) { txn := s.snapshotsDB.NewTransaction(false) defer txn.Discard() - item, err := txn.Get([]byte(graphPrefixCustodianAccount)) - if err == badger.ErrKeyNotFound { - return nil, nil - } else if err != nil { - return nil, err + return s.readCustodianAccount(txn) +} + +func (s *BadgerStore) readCustodianAccount(txn *badger.Txn) (*common.Address, uint64, error) { + opts := badger.DefaultIteratorOptions + opts.PrefetchValues = true + opts.Reverse = true + + it := txn.NewIterator(opts) + defer it.Close() + + it.Seek(graphCustodianAccountKey(^uint64(0))) + if it.ValidForPrefix([]byte(graphPrefixCustodianAccount)) { + key := it.Item().KeyCopy(nil) + ts := graphCustodianAccountTimestamp(key) + val, err := it.Item().ValueCopy(nil) + if err != nil { + return nil, 0, err + } + addr, err := common.NewAddressFromString(string(val)) + return &addr, ts, err } - val, err := item.ValueCopy(nil) + + return nil, 0, nil +} + +func (s *BadgerStore) writeCustodianNodes(txn *badger.Txn, snap *common.Snapshot, custodian *common.Address, nodes []*common.CustodianNode) error { + old, ts, err := s.readCustodianAccount(txn) if err != nil { - return nil, err + return err + } + switch { + case ts == 0 && old != nil: + panic(snap.Hash.String()) + case ts != 0 && old == nil: + panic(snap.Hash.String()) + case old == nil && ts == 0: + case ts > snap.Timestamp: + panic(snap.Hash.String()) + case ts == snap.Timestamp && custodian.String() == old.String(): + return nil + case ts == snap.Timestamp && custodian.String() != old.String(): + panic(snap.Hash.String()) + case ts < snap.Timestamp: } - addr, err := common.NewAddressFromString(string(val)) + + key := graphCustodianAccountKey(snap.Timestamp) + err = txn.Set(key, []byte(custodian.String())) if err != nil { - return nil, err + return err } - return &addr, nil + panic(0) +} + +func graphCustodianAccountKey(ts uint64) []byte { + key := []byte(graphPrefixCustodianAccount) + return binary.BigEndian.AppendUint64(key, ts) +} + +func graphCustodianAccountTimestamp(key []byte) uint64 { + ts := key[len(graphPrefixCustodianAccount):] + return binary.BigEndian.Uint64(ts) } diff --git a/storage/badger_graph.go b/storage/badger_graph.go index cf6392420..cfd2794a3 100644 --- a/storage/badger_graph.go +++ b/storage/badger_graph.go @@ -31,7 +31,7 @@ const ( graphPrefixWorkSnapshot = "WORKSNAPSHOT" graphPrefixSpaceCheckpoint = "SPACECHECKPOINT" graphPrefixSpaceQueue = "SPACEQUEUE" - graphPrefixCustodianAccount = "CUSTODIAN:ACCOUNT" + graphPrefixCustodianAccount = "CUSTODIAN:ACCOUNT:" ) func (s *BadgerStore) RemoveGraphEntries(prefix string) (int, error) { diff --git a/storage/badger_topology.go b/storage/badger_topology.go index ab19c53b5..e25c863c2 100644 --- a/storage/badger_topology.go +++ b/storage/badger_topology.go @@ -117,8 +117,6 @@ func (s *BadgerStore) ReadSnapshotsSinceTopology(topologyOffset, count uint64) ( } func (s *BadgerStore) TopologySequence() uint64 { - var sequence uint64 - txn := s.snapshotsDB.NewTransaction(false) defer txn.Discard() @@ -132,9 +130,9 @@ func (s *BadgerStore) TopologySequence() uint64 { it.Seek(graphTopologyKey(^uint64(0))) if it.ValidForPrefix([]byte(graphPrefixTopology)) { key := it.Item().KeyCopy(nil) - sequence = graphTopologyOrder(key) + return graphTopologyOrder(key) } - return sequence + return 0 } func writeTopology(txn *badger.Txn, snap *common.SnapshotWithTopologicalOrder) error { diff --git a/storage/interface.go b/storage/interface.go index 43ef7b85e..30d0500fb 100644 --- a/storage/interface.go +++ b/storage/interface.go @@ -32,7 +32,7 @@ type Store interface { ReadLink(from, to crypto.Hash) (uint64, error) WriteSnapshot(*common.SnapshotWithTopologicalOrder, []crypto.Hash) error ReadDomains() []*common.Domain - ReadCustodianAccount() (*common.Address, error) + ReadCustodianAccount() (*common.Address, uint64, error) CachePutTransaction(tx *common.VersionedTransaction) error CacheGetTransaction(hash crypto.Hash) (*common.VersionedTransaction, error)