Skip to content

Commit

Permalink
Added reorg logic and quai_getLockupsForContractAndMiner API
Browse files Browse the repository at this point in the history
  • Loading branch information
jdowning100 committed Dec 17, 2024
1 parent fe3e8ac commit c1dfad0
Show file tree
Hide file tree
Showing 10 changed files with 501 additions and 149 deletions.
40 changes: 40 additions & 0 deletions core/headerchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,26 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.WorkObject) error {
}
hc.headerDb.Delete(key)
}
createdCoinbaseKeys, err := rawdb.ReadCreatedCoinbaseLockupKeys(hc.headerDb, prevHeader.Hash())
if err != nil {
return err
}
for _, key := range createdCoinbaseKeys {
if len(key) != rawdb.CoinbaseLockupKeyLength {
return fmt.Errorf("invalid created coinbase key length: %d", len(key))
}
hc.headerDb.Delete(key)
}
deletedCoinbases, err := rawdb.ReadDeletedCoinbaseLockups(hc.headerDb, prevHeader.Hash())
if err != nil {
return err
}
for key, coinbase := range deletedCoinbases {
if len(key) != rawdb.CoinbaseLockupKeyLength {
return fmt.Errorf("invalid deleted coinbase key length: %d", len(key))
}
hc.headerDb.Put(key[:], coinbase)
}
}
prevHeader = hc.GetHeaderByHash(prevHeader.ParentHash(hc.NodeCtx()))
if prevHeader == nil {
Expand Down Expand Up @@ -592,6 +612,26 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.WorkObject) error {
for _, key := range utxoKeys {
hc.headerDb.Delete(key)
}
createdCoinbaseKeys, err := rawdb.ReadCreatedCoinbaseLockupKeys(hc.headerDb, prevHeader.Hash())
if err != nil {
return err
}
for _, key := range createdCoinbaseKeys {
if len(key) != rawdb.CoinbaseLockupKeyLength {
return fmt.Errorf("invalid created coinbase key length: %d", len(key))
}
hc.headerDb.Delete(key)
}
deletedCoinbases, err := rawdb.ReadDeletedCoinbaseLockups(hc.headerDb, prevHeader.Hash())
if err != nil {
return err
}
for key, coinbase := range deletedCoinbases {
if len(key) != rawdb.CoinbaseLockupKeyLength {
return fmt.Errorf("invalid deleted coinbase key length: %d", len(key))
}
hc.headerDb.Put(key[:], coinbase)
}
}
}
for k := len(prevHashStack) - 1; k >= 0; k-- {
Expand Down
71 changes: 71 additions & 0 deletions core/rawdb/accessors_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -1567,6 +1567,77 @@ func DeleteCreatedUTXOKeys(db ethdb.KeyValueWriter, blockHash common.Hash) {
}
}

func WriteCreatedCoinbaseLockupKeys(db ethdb.KeyValueWriter, blockHash common.Hash, keys [][]byte) error {
protoKeys := &types.ProtoKeys{Keys: make([][]byte, 0, len(keys))}
protoKeys.Keys = append(protoKeys.Keys, keys...)

data, err := proto.Marshal(protoKeys)
if err != nil {
db.Logger().WithField("err", err).Fatal("Failed to rlp encode utxo")
}
return db.Put(createdCoinbaseLockupsKey(blockHash), data)
}

func ReadCreatedCoinbaseLockupKeys(db ethdb.Reader, blockHash common.Hash) ([][]byte, error) {
// Try to look up the data in leveldb.
data, _ := db.Get(createdCoinbaseLockupsKey(blockHash))
if len(data) == 0 {
return nil, nil
}
protoKeys := new(types.ProtoKeys)
if err := proto.Unmarshal(data, protoKeys); err != nil {
return nil, err
}
return protoKeys.Keys, nil
}

func DeleteCreatedCoinbaseLockupKeys(db ethdb.KeyValueWriter, blockHash common.Hash) {
if err := db.Delete(createdCoinbaseLockupsKey(blockHash)); err != nil {
db.Logger().WithField("err", err).Fatal("Failed to delete created coinbase lockup keys")
}
}

func WriteDeletedCoinbaseLockups(db ethdb.KeyValueWriter, blockHash common.Hash, deletedLockups map[[47]byte][]byte) error {
protoKeysAndValues := &types.ProtoKeysAndValues{KeysAndValues: make([]*types.ProtoKeyValue, 0, len(deletedLockups))}
for key, value := range deletedLockups {
protoKeysAndValues.KeysAndValues = append(protoKeysAndValues.KeysAndValues, &types.ProtoKeyValue{
Key: key[:],
Value: value,
})
}
data, err := proto.Marshal(protoKeysAndValues)
if err != nil {
db.Logger().WithField("err", err).Fatal("Failed to rlp encode utxo")
}
return db.Put(deletedCoinbaseLockupsKey(blockHash), data)
}

func ReadDeletedCoinbaseLockups(db ethdb.Reader, blockHash common.Hash) (map[[47]byte][]byte, error) {
// Try to look up the data in leveldb.
data, _ := db.Get(deletedCoinbaseLockupsKey(blockHash))
if len(data) == 0 {
return nil, nil
}
protoKeysAndValues := new(types.ProtoKeysAndValues)
if err := proto.Unmarshal(data, protoKeysAndValues); err != nil {
return nil, err
}
deletedLockups := make(map[[47]byte][]byte)
for _, keyValue := range protoKeysAndValues.KeysAndValues {
if len(keyValue.Key) != 47 {
return nil, fmt.Errorf("invalid key length %d", len(keyValue.Key))
}
deletedLockups[[47]byte(keyValue.Key)] = keyValue.Value
}
return deletedLockups, nil
}

func DeleteDeletedCoinbaseLockups(db ethdb.KeyValueWriter, blockHash common.Hash) {
if err := db.Delete(deletedCoinbaseLockupsKey(blockHash)); err != nil {
db.Logger().WithField("err", err).Fatal("Failed to delete deleted coinbase lockups")
}
}

func ReadUTXOSetSize(db ethdb.Reader, blockHash common.Hash) uint64 {
data, _ := db.Get(utxoSetSizeKey(blockHash))
if len(data) == 0 {
Expand Down
50 changes: 48 additions & 2 deletions core/rawdb/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,10 @@ var (
configPrefix = []byte("quai-config-") // config prefix for the db

// 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
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
createdCoinbaseLockupsPrefix = []byte("ccl") // createdCoinbaseLockupsPrefix + hash -> [][]byte
deletedCoinbaseLockupsPrefix = []byte("dcl") // deletedCoinbaseLockupsPrefix + hash -> [][]byte
)

const (
Expand Down Expand Up @@ -389,6 +391,8 @@ func utxoToBlockHeightKey(txHash common.Hash, index uint16) []byte {
return append(utxoToBlockHeightPrefix, txHash[:]...)
}

var CoinbaseLockupKeyLength = len(CoinbaseLockupPrefix) + 2*common.AddressLength + 1 + 4

func CoinbaseLockupKey(ownerContract common.Address, beneficiaryMiner common.Address, lockupByte byte, epoch uint32) []byte {
epochBytes := make([]byte, 4)
binary.BigEndian.PutUint32(epochBytes, epoch)
Expand All @@ -399,3 +403,45 @@ func CoinbaseLockupKey(ownerContract common.Address, beneficiaryMiner common.Add
combined = append(combined, epochBytes...)
return append(CoinbaseLockupPrefix, combined...)
}

func createdCoinbaseLockupsKey(hash common.Hash) []byte {
return append(createdCoinbaseLockupsPrefix, hash.Bytes()...)
}

func deletedCoinbaseLockupsKey(hash common.Hash) []byte {
return append(deletedCoinbaseLockupsPrefix, hash.Bytes()...)
}

func ReverseCoinbaseLockupKey(data []byte, location common.Location) (common.Address, common.Address, byte, uint32, error) {

epochLength := 4 // Length of the epoch in bytes
prefixLength := len(CoinbaseLockupPrefix)

// Ensure the data is long enough to contain all components
if len(data) < prefixLength+2*common.AddressLength+1+epochLength {
return common.Address{}, common.Address{}, 0, 0, fmt.Errorf("key is too short to parse")
}

// Check and remove the prefix
if !bytes.HasPrefix(data, CoinbaseLockupPrefix) {
return common.Address{}, common.Address{}, 0, 0, fmt.Errorf("key does not have the correct prefix")
}
data = data[prefixLength:] // Remove the prefix

// Extract the owner contract address
ownerContract := common.BytesToAddress(data[:common.AddressLength], location)
data = data[common.AddressLength:] // Advance the slice

// Extract the beneficiary miner address
beneficiaryMiner := common.BytesToAddress(data[:common.AddressLength], location)
data = data[common.AddressLength:] // Advance the slice

// Extract the lockup byte
lockupByte := data[0]
data = data[1:] // Advance the slice

// Extract the epoch
epoch := binary.BigEndian.Uint32(data[:epochLength])

return ownerContract, beneficiaryMiner, lockupByte, epoch, nil
}
69 changes: 47 additions & 22 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,14 @@ func NewStateProcessor(config *params.ChainConfig, hc *HeaderChain, engine conse
}

type UtxosCreatedDeleted struct {
UtxosCreatedKeys [][]byte
UtxosCreatedHashes []common.Hash
UtxosDeleted []*types.SpentUtxoEntry
UtxosDeletedHashes []common.Hash
CoinbaseLockupsCreated map[string]common.Hash
CoinbaseLockupsDeleted map[string]common.Hash
RotatedEpochs map[string]struct{}
UtxosCreatedKeys [][]byte
UtxosCreatedHashes []common.Hash
UtxosDeleted []*types.SpentUtxoEntry
UtxosDeletedHashes []common.Hash
CoinbaseLockupsCreatedHashes map[string]common.Hash
CoinbaseLockupsDeletedHashes map[string]common.Hash
CoinbaseLockupsDeleted map[[47]byte][]byte
RotatedEpochs map[string]struct{}
}

// Process processes the state changes according to the Quai rules by running
Expand Down Expand Up @@ -263,8 +264,9 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
return types.Receipts{}, []*types.Transaction{}, []*types.Log{}, nil, 0, 0, 0, nil, nil, err
}
utxosCreatedDeleted := new(UtxosCreatedDeleted) // utxos created and deleted in this block
utxosCreatedDeleted.CoinbaseLockupsCreated = make(map[string]common.Hash)
utxosCreatedDeleted.CoinbaseLockupsDeleted = make(map[string]common.Hash)
utxosCreatedDeleted.CoinbaseLockupsCreatedHashes = make(map[string]common.Hash)
utxosCreatedDeleted.CoinbaseLockupsDeletedHashes = make(map[string]common.Hash)
utxosCreatedDeleted.CoinbaseLockupsDeleted = make(map[[47]byte][]byte)
utxosCreatedDeleted.RotatedEpochs = make(map[string]struct{})
// Apply the previous inbound ETXs to the ETX set state
prevInboundEtxs := rawdb.ReadInboundEtxs(p.hc.bc.db, header.ParentHash(nodeCtx))
Expand Down Expand Up @@ -591,14 +593,14 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
return nil, nil, nil, nil, 0, 0, 0, nil, nil, fmt.Errorf("could not add new lock: %w", err)
}
// Store the new lockup key every time
utxosCreatedDeleted.CoinbaseLockupsCreated[string(newCoinbaseLockupKey)] = *newCoinbaseLockupHash
utxosCreatedDeleted.CoinbaseLockupsCreatedHashes[string(newCoinbaseLockupKey)] = *newCoinbaseLockupHash

if oldCoinbaseLockupHash != nil {
// We deleted (updated) the old lockup, write it to deleted list but only the first time
if _, exists := utxosCreatedDeleted.CoinbaseLockupsDeleted[string(oldCoinbaseLockupKey)]; !exists {
if _, exists := utxosCreatedDeleted.CoinbaseLockupsDeletedHashes[string(oldCoinbaseLockupKey)]; !exists {
if _, exists := utxosCreatedDeleted.RotatedEpochs[string(newCoinbaseLockupKey)]; !exists {
// We only want to add a delete if we have not rotated the epoch (we haven't created a new lock) because otherwise there is nothing to delete
utxosCreatedDeleted.CoinbaseLockupsDeleted[string(oldCoinbaseLockupKey)] = *oldCoinbaseLockupHash
utxosCreatedDeleted.CoinbaseLockupsDeletedHashes[string(oldCoinbaseLockupKey)] = *oldCoinbaseLockupHash
utxosCreatedDeleted.UtxosDeletedHashes = append(utxosCreatedDeleted.UtxosDeletedHashes, *oldCoinbaseLockupHash)
}
}
Expand Down Expand Up @@ -659,14 +661,13 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
return nil, nil, nil, nil, 0, 0, 0, nil, nil, fmt.Errorf("could not add new lock: %w", err)
}
// Store the new lockup key every time
utxosCreatedDeleted.CoinbaseLockupsCreated[string(newCoinbaseLockupKey)] = *newCoinbaseLockupHash
utxosCreatedDeleted.CoinbaseLockupsCreatedHashes[string(newCoinbaseLockupKey)] = *newCoinbaseLockupHash

if oldCoinbaseLockupHash != nil {
// We deleted (updated) the old lockup, write it to deleted list but only the first time
if _, exists := utxosCreatedDeleted.CoinbaseLockupsDeleted[string(oldCoinbaseLockupKey)]; !exists {
if _, exists := utxosCreatedDeleted.RotatedEpochs[string(newCoinbaseLockupKey)]; !exists {
// Don't register deletes for any rotated epochs
utxosCreatedDeleted.CoinbaseLockupsDeleted[string(oldCoinbaseLockupKey)] = *oldCoinbaseLockupHash
if _, exists := utxosCreatedDeleted.CoinbaseLockupsDeletedHashes[string(oldCoinbaseLockupKey)]; !exists {
if _, exists := utxosCreatedDeleted.RotatedEpochs[string(newCoinbaseLockupKey)]; !exists { // Don't register deletes for any rotated epochs
utxosCreatedDeleted.CoinbaseLockupsDeletedHashes[string(oldCoinbaseLockupKey)] = *oldCoinbaseLockupHash
utxosCreatedDeleted.UtxosDeletedHashes = append(utxosCreatedDeleted.UtxosDeletedHashes, *oldCoinbaseLockupHash)
}
}
Expand Down Expand Up @@ -802,10 +803,16 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
totalEtxGas += receipt.GasUsed
timeDelta := time.Since(startTimeEtx)
timeQiToQuai += timeDelta
for _, etx := range receipt.OutboundEtxs {
if receipt.Status == types.ReceiptStatusSuccessful {
if receipt.Status == types.ReceiptStatusSuccessful {
for _, etx := range receipt.OutboundEtxs {
emittedEtxs = append(emittedEtxs, etx)
}
for _, hash := range receipt.CoinbaseLockupDeletedHashes {
utxosCreatedDeleted.UtxosDeletedHashes = append(utxosCreatedDeleted.UtxosDeletedHashes, *hash)
}
for key, lockup := range receipt.CoinbaseLockupsDeleted {
utxosCreatedDeleted.CoinbaseLockupsDeleted[key] = lockup
}
}
receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...)
Expand Down Expand Up @@ -845,10 +852,16 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
minGasPrice = new(big.Int).Set(gasPrice)
}
}
for _, etx := range receipt.OutboundEtxs {
if receipt.Status == types.ReceiptStatusSuccessful {
if receipt.Status == types.ReceiptStatusSuccessful {
for _, etx := range receipt.OutboundEtxs {
emittedEtxs = append(emittedEtxs, etx)
}
for _, hash := range receipt.CoinbaseLockupDeletedHashes {
utxosCreatedDeleted.UtxosDeletedHashes = append(utxosCreatedDeleted.UtxosDeletedHashes, *hash)
}
for key, lockup := range receipt.CoinbaseLockupsDeleted {
utxosCreatedDeleted.CoinbaseLockupsDeleted[key] = lockup
}
}
receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...)
Expand Down Expand Up @@ -979,7 +992,7 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
}
}

for _, hash := range utxosCreatedDeleted.CoinbaseLockupsCreated {
for _, hash := range utxosCreatedDeleted.CoinbaseLockupsCreatedHashes {
// Update the created hash list with the latest new elements (instead of intermediate ones)
utxosCreatedDeleted.UtxosCreatedHashes = append(utxosCreatedDeleted.UtxosCreatedHashes, hash)
}
Expand Down Expand Up @@ -1034,6 +1047,16 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty
if err := rawdb.WriteCreatedUTXOKeys(batch, blockHash, utxosCreatedDeleted.UtxosCreatedKeys); err != nil { // Could do this in Apply instead
return nil, nil, nil, nil, 0, 0, 0, nil, nil, err
}
coinbaseLockupsCreatedKeys := make([][]byte, 0, len(utxosCreatedDeleted.CoinbaseLockupsCreatedHashes))
for key, _ := range utxosCreatedDeleted.CoinbaseLockupsCreatedHashes {
coinbaseLockupsCreatedKeys = append(coinbaseLockupsCreatedKeys, []byte(key))
}
if err := rawdb.WriteCreatedCoinbaseLockupKeys(batch, blockHash, coinbaseLockupsCreatedKeys); err != nil {
return nil, nil, nil, nil, 0, 0, 0, nil, nil, err
}
if err := rawdb.WriteDeletedCoinbaseLockups(batch, blockHash, utxosCreatedDeleted.CoinbaseLockupsDeleted); err != nil {
return nil, nil, nil, nil, 0, 0, 0, nil, nil, err
}
return receipts, emittedEtxs, allLogs, statedb, *usedGas, *usedState, utxoSetSize, multiSet, unlocks, nil
}

Expand Down Expand Up @@ -1230,6 +1253,8 @@ func applyTransaction(msg types.Message, parent *types.WorkObject, config *param
receipt.ContractAddress = *result.ContractAddr
}
receipt.OutboundEtxs = result.Etxs
receipt.CoinbaseLockupDeletedHashes = evm.CoinbaseDeletedHashes
receipt.CoinbaseLockupsDeleted = evm.CoinbasesDeleted
}
receipt.TxHash = tx.Hash()
receipt.GasUsed = result.UsedGas
Expand Down
Loading

0 comments on commit c1dfad0

Please sign in to comment.