Skip to content

Commit

Permalink
Added a precompile that stores locked coinbases compressed into tranches
Browse files Browse the repository at this point in the history
  • Loading branch information
jdowning100 committed Dec 17, 2024
1 parent 34a2705 commit fe3e8ac
Show file tree
Hide file tree
Showing 18 changed files with 978 additions and 320 deletions.
4 changes: 2 additions & 2 deletions cmd/utils/hierarchical_coordinator.go
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ func (hc *HierarchicalCoordinator) BuildPendingHeaders(wo *types.WorkObject, ord
log.Global.Debug("Entropy: ", common.BigBitsToBits(entropy))
nodeSet, exists := hc.Get(entropy)
if !exists {
log.Global.WithFields(log.Fields{"entropy": common.BigBitsToBits(entropy), "order": order, "number": wo.NumberArray(), "hash": wo.Hash()}).Debug("NodeSet not found for entropy")
log.Global.WithFields(log.Fields{"entropy": common.BigBitsToBits(entropy), "order": order, "number": wo.NumberArray(), "hash": wo.Hash()}).Trace("NodeSet not found for entropy")
}

if nodeSet.Extendable(wo, order) {
Expand All @@ -997,7 +997,7 @@ func (hc *HierarchicalCoordinator) BuildPendingHeaders(wo *types.WorkObject, ord
}
hc.Add(newSetEntropy, newNodeSet, newPendingHeaders)
} else {
log.Global.WithFields(log.Fields{"entropy": common.BigBitsToBits(entropy), "order": order, "number": wo.NumberArray(), "hash": wo.Hash()}).Debug("NodeSet not found for entropy")
log.Global.WithFields(log.Fields{"entropy": common.BigBitsToBits(entropy), "order": order, "number": wo.NumberArray(), "hash": wo.Hash()}).Trace("NodeSet not found for entropy")
}
misses++
if misses > threshold {
Expand Down
83 changes: 82 additions & 1 deletion core/rawdb/accessors_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import (

"github.com/dominant-strategies/go-quai/common"
"github.com/dominant-strategies/go-quai/core/types"
"github.com/dominant-strategies/go-quai/crypto/multiset"
"github.com/dominant-strategies/go-quai/ethdb"
"github.com/dominant-strategies/go-quai/log"
"github.com/dominant-strategies/go-quai/crypto/multiset"
"github.com/dominant-strategies/go-quai/params"
"google.golang.org/protobuf/proto"
)
Expand Down Expand Up @@ -1710,3 +1710,84 @@ func DeleteUtxoToBlockHeight(db ethdb.KeyValueWriter, txHash common.Hash, index
db.Logger().WithField("err", err).Fatal("Failed to delete utxo to block height")
}
}

func ReadCoinbaseLockup(db ethdb.KeyValueReader, batch ethdb.Batch, ownerContract common.Address, beneficiaryMiner common.Address, lockupByte byte, epoch uint32) (*big.Int, uint32, uint16, common.Address) {
deleted, data := batch.GetPending(CoinbaseLockupKey(ownerContract, beneficiaryMiner, lockupByte, epoch))
if deleted {
return new(big.Int), 0, 0, common.Zero
} else if data != nil {
amount := new(big.Int).SetBytes(data[:32])
blockHeight := binary.BigEndian.Uint32(data[32:36])
elements := binary.BigEndian.Uint16(data[36:38])
var delegate common.Address
if len(data) == 58 {
delegate = common.BytesToAddress(data[38:], db.Location())
} else {
delegate = common.Zero
}
return amount, blockHeight, elements, delegate
}
// If the data is not in the batch, try to look up the data in leveldb
data, _ = db.Get(CoinbaseLockupKey(ownerContract, beneficiaryMiner, lockupByte, epoch))
if len(data) == 0 {
return new(big.Int), 0, 0, common.Zero
}
amount := new(big.Int).SetBytes(data[:32])
blockHeight := binary.BigEndian.Uint32(data[32:36])
elements := binary.BigEndian.Uint16(data[36:38])
var delegate common.Address
if len(data) == 58 {
delegate = common.BytesToAddress(data[38:], db.Location())
} else {
delegate = common.Zero
}
return amount, blockHeight, elements, delegate
}

func WriteCoinbaseLockup(db ethdb.KeyValueWriter, ownerContract common.Address, beneficiaryMiner common.Address, lockupByte byte, epoch uint32, amount *big.Int, blockHeight uint32, elements uint16, delegate common.Address) ([]byte, error) {
data := make([]byte, 38)
amountBytes := amount.Bytes()
if len(amountBytes) > 32 {
return nil, fmt.Errorf("amount is too large")
}
// Right-align amountBytes in data[:32]
copy(data[32-len(amountBytes):32], amountBytes)
binary.BigEndian.PutUint32(data[32:36], blockHeight)
binary.BigEndian.PutUint16(data[36:38], elements)
if !delegate.Equal(common.Zero) {
data = append(data, delegate.Bytes()...)
}
key := CoinbaseLockupKey(ownerContract, beneficiaryMiner, lockupByte, epoch)
if err := db.Put(key, data); err != nil {
db.Logger().WithField("err", err).Fatal("Failed to store coinbase lockup")
}
return key, nil
}

func WriteCoinbaseLockupToMap(coinbaseMap map[[47]byte][]byte, key [47]byte, amount *big.Int, blockHeight uint32, elements uint16, delegate common.Address) error {
data := make([]byte, 38)
amountBytes := amount.Bytes()
if len(amountBytes) > 32 {
return fmt.Errorf("amount is too large")
}
// Right-align amountBytes in data[:32]
copy(data[32-len(amountBytes):32], amountBytes)
binary.BigEndian.PutUint32(data[32:36], blockHeight)
binary.BigEndian.PutUint16(data[36:38], elements)
if !delegate.Equal(common.Zero) {
data = append(data, delegate.Bytes()...)
}
coinbaseMap[[47]byte(key)] = data
return nil
}

func DeleteCoinbaseLockup(db ethdb.KeyValueWriter, ownerContract common.Address, beneficiaryMiner common.Address, lockupByte byte, epoch uint32) [47]byte {
key := CoinbaseLockupKey(ownerContract, beneficiaryMiner, lockupByte, epoch)
if err := db.Delete(key); err != nil {
db.Logger().WithField("err", err).Fatal("Failed to delete coinbase lockup")
}
if len(key) != 47 {
db.Logger().Fatal("CoinbaseLockupKey is not 47 bytes")
}
return [47]byte(key)
}
12 changes: 12 additions & 0 deletions core/rawdb/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ var (

// Chain index prefixes (use `i` + single byte to avoid mixing data types).
BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
CoinbaseLockupPrefix = []byte("cl") // coinbaseLockupPrefix + ownerContract + beneficiaryMiner + lockupByte + epoch -> lockup
)

const (
Expand Down Expand Up @@ -387,3 +388,14 @@ func utxoToBlockHeightKey(txHash common.Hash, index uint16) []byte {
txHash[common.HashLength-2] = indexBytes[1]
return append(utxoToBlockHeightPrefix, txHash[:]...)
}

func CoinbaseLockupKey(ownerContract common.Address, beneficiaryMiner common.Address, lockupByte byte, epoch uint32) []byte {
epochBytes := make([]byte, 4)
binary.BigEndian.PutUint32(epochBytes, epoch)
ownerBytes := ownerContract.Bytes()
beneficiaryBytes := beneficiaryMiner.Bytes()
combined := append(ownerBytes, beneficiaryBytes...)
combined = append(combined, lockupByte)
combined = append(combined, epochBytes...)
return append(CoinbaseLockupPrefix, combined...)
}
6 changes: 6 additions & 0 deletions core/rawdb/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ func (b *tableBatch) Replay(w ethdb.KeyValueWriter) error {
return b.batch.Replay(&tableReplayer{w: w, prefix: b.prefix})
}

func (b *tableBatch) SetPending(pending bool) {}

func (b *tableBatch) GetPending(key []byte) (bool, []byte) {
return false, nil
}

// tableIterator is a wrapper around a database iterator that prefixes each key access
// with a pre-configured string.
type tableIterator struct {
Expand Down
38 changes: 34 additions & 4 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/dominant-strategies/go-quai/core/state/snapshot"
"github.com/dominant-strategies/go-quai/core/types"
"github.com/dominant-strategies/go-quai/crypto"
"github.com/dominant-strategies/go-quai/ethdb"
"github.com/dominant-strategies/go-quai/log"
"github.com/dominant-strategies/go-quai/metrics_config"
"github.com/dominant-strategies/go-quai/rlp"
Expand All @@ -45,10 +46,10 @@ type revision struct {

var (
// emptyRoot is the known root hash of an empty trie.
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
newestEtxKey = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff") // max hash
oldestEtxKey = common.HexToHash("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe") // max hash - 1

emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
newestEtxKey = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff") // max hash
oldestEtxKey = common.HexToHash("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe") // max hash - 1
currentEpochKey = common.HexToHash("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffd") // max hash - 2
)

type proofList [][]byte
Expand Down Expand Up @@ -349,6 +350,7 @@ func (s *StateDB) TxIndex() int {
}

func (s *StateDB) GetCode(addr common.InternalAddress) []byte {
return []byte{}
stateObject := s.getStateObject(addr)
if stateObject != nil {
return stateObject.Code(s.db)
Expand Down Expand Up @@ -422,6 +424,10 @@ func (s *StateDB) ETXDatabase() Database {
return s.etxDb
}

func (s *StateDB) UnderlyingDatabase() ethdb.KeyValueReader {
return s.db.TrieDB().DiskDB()
}

// StorageTrie returns the storage trie of an account.
// The return value is a copy and is nil for non-existent accounts.
func (s *StateDB) StorageTrie(addr common.InternalAddress) Trie {
Expand Down Expand Up @@ -702,6 +708,30 @@ func (s *StateDB) CommitEtxs() (common.Hash, error) {
return root, err
}

func (s *StateDB) GetLatestEpoch() (uint32, error) {
epochBytes, err := s.etxTrie.TryGet(currentEpochKey[:])
if err != nil {
return 0, err
}
if epochBytes == nil || len(epochBytes) == 0 {
return 0, nil
}
if len(epochBytes) != 4 {
return 0, fmt.Errorf("invalid epoch length: %d", len(epochBytes))
}
epoch := binary.BigEndian.Uint32(epochBytes)
return epoch, nil
}

func (s *StateDB) SetLatestEpoch(epoch uint32) error {
epochBytes := make([]byte, 4)
binary.BigEndian.PutUint32(epochBytes, epoch)
if err := s.etxTrie.TryUpdate(currentEpochKey[:], epochBytes); err != nil {
return err
}
return nil
}

// getDeletedStateObject is similar to getStateObject, but instead of returning
// nil for a deleted state object, it returns the actual object with the deleted
// flag set. This is needed by the state journal to revert to the correct s-
Expand Down
Loading

0 comments on commit fe3e8ac

Please sign in to comment.