Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Locked Quai/Qi coinbase for LMT and Wrapped Qi #2401

Open
wants to merge 6 commits into
base: goldenage
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
7 changes: 7 additions & 0 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,13 @@ func ZeroInternal(nodeLocation Location) InternalAddress {
return InternalAddress{nodeLocation.BytePrefix()}
}

// OneInternal returns an address starting with the byte prefix and ending in 1
func OneInternal(nodeLocation Location) InternalAddress {
one := ZeroInternal(nodeLocation)
one[AddressLength-1] = 1
return one
}

func ZeroAddress(nodeLocation Location) Address {
internal := InternalAddress{nodeLocation.BytePrefix()}
return Address{&internal}
Expand Down
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
154 changes: 153 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 @@ -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 Expand Up @@ -1710,3 +1781,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)
}
18 changes: 9 additions & 9 deletions core/rawdb/accessors_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestHeaderStorage(t *testing.T) {
woBody := types.EmptyWorkObjectBody()
woBody.SetHeader(header)

woHeader := types.NewWorkObjectHeader(header.Hash(), common.Hash{1}, big.NewInt(1), big.NewInt(30000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 1, common.LocationFromAddressBytes([]byte{0x01, 0x01}), common.BytesToAddress([]byte{1}, common.Location{0, 0}))
woHeader := types.NewWorkObjectHeader(header.Hash(), common.Hash{1}, big.NewInt(1), big.NewInt(30000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 1, common.LocationFromAddressBytes([]byte{0x01, 0x01}), common.BytesToAddress([]byte{1}, common.Location{0, 0}), []byte{0, 1, 2, 3})

wo := types.NewWorkObject(woHeader, woBody, nil)

Expand Down Expand Up @@ -190,7 +190,7 @@ func TestPbCacheStorage(t *testing.T) {
woBody := types.EmptyWorkObjectBody()
woBody.SetHeader(types.EmptyHeader())

woHeader := types.NewWorkObjectHeader(types.EmptyRootHash, types.EmptyRootHash, big.NewInt(11), big.NewInt(30000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 1, common.LocationFromAddressBytes([]byte{0x01, 0x01}), common.BytesToAddress([]byte{1}, common.Location{0, 0}))
woHeader := types.NewWorkObjectHeader(types.EmptyRootHash, types.EmptyRootHash, big.NewInt(11), big.NewInt(30000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 1, common.LocationFromAddressBytes([]byte{0x01, 0x01}), common.BytesToAddress([]byte{1}, common.Location{0, 0}), []byte{0, 1, 2, 3})
wo := types.NewWorkObject(woHeader, woBody, nil)
WritePbCacheBody(db, common.Hash{1}, wo)

Expand Down Expand Up @@ -320,7 +320,7 @@ func TestBlockHashesIterator(t *testing.T) {
blockHeader := types.EmptyHeader()
blockBody := types.EmptyWorkObjectBody()
blockBody.SetHeader(blockHeader)
blockWoHeader := types.NewWorkObjectHeader(blockHeader.Hash(), types.EmptyHash, big.NewInt(int64(i+1)), big.NewInt(3000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 2, common.Location{0, byte(j)}, common.BytesToAddress([]byte{1}, common.Location{0, byte(j)}))
blockWoHeader := types.NewWorkObjectHeader(blockHeader.Hash(), types.EmptyHash, big.NewInt(int64(i+1)), big.NewInt(3000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 2, common.Location{0, byte(j)}, common.BytesToAddress([]byte{1}, common.Location{0, byte(j)}), []byte{0, 1, 2, 3})
block := types.NewWorkObject(blockWoHeader, blockBody, nil)
WriteWorkObject(db, block.Hash(), block, types.BlockObject, common.ZONE_CTX)
hashes[i][block.Hash()] = true
Expand All @@ -345,30 +345,30 @@ func TestCommonAncestor(t *testing.T) {
regionHeader.SetNumber(big.NewInt(1), common.REGION_CTX)
regionBody := types.EmptyWorkObjectBody()
regionBody.SetHeader(regionHeader)
regionWoHeader := types.NewWorkObjectHeader(regionHeader.Hash(), types.EmptyRootHash, big.NewInt(1), big.NewInt(3000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 1, common.Location{0}, common.BytesToAddress([]byte{0}, common.Location{0, 0}))
regionWoHeader := types.NewWorkObjectHeader(regionHeader.Hash(), types.EmptyRootHash, big.NewInt(1), big.NewInt(3000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 1, common.Location{0}, common.BytesToAddress([]byte{0}, common.Location{0, 0}), []byte{0, 1, 2, 3})
regionBlock := types.NewWorkObject(regionWoHeader, regionBody, nil)
WriteWorkObject(db, regionBlock.Hash(), regionBlock, types.BlockObject, common.REGION_CTX)

//Write one block on zone 0 context
zone0Header := types.EmptyHeader()
zone0Body := types.EmptyWorkObjectBody()
zone0Body.SetHeader(zone0Header)
zone0WoHeader := types.NewWorkObjectHeader(zone0Header.Hash(), regionBlock.Hash(), big.NewInt(2), big.NewInt(3000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 2, common.Location{0, 0}, common.BytesToAddress([]byte{0}, common.Location{0, 0}))
zone0WoHeader := types.NewWorkObjectHeader(zone0Header.Hash(), regionBlock.Hash(), big.NewInt(2), big.NewInt(3000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 2, common.Location{0, 0}, common.BytesToAddress([]byte{0}, common.Location{0, 0}), []byte{0, 1, 2, 3})
zone0Block := types.NewWorkObject(zone0WoHeader, zone0Body, nil)
WriteWorkObject(db, zone0Block.Hash(), zone0Block, types.BlockObject, common.ZONE_CTX)

//Write two blocks on zone 1 context
zone1Header1 := types.EmptyHeader()
zone1Body1 := types.EmptyWorkObjectBody()
zone1Body1.SetHeader(zone1Header1)
zone1WoHeader1 := types.NewWorkObjectHeader(zone1Header1.Hash(), regionBlock.Hash(), big.NewInt(2), big.NewInt(3000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 2, common.Location{0, 1}, common.BytesToAddress([]byte{0}, common.Location{0, 1}))
zone1WoHeader1 := types.NewWorkObjectHeader(zone1Header1.Hash(), regionBlock.Hash(), big.NewInt(2), big.NewInt(3000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 2, common.Location{0, 1}, common.BytesToAddress([]byte{0}, common.Location{0, 1}), []byte{0, 1, 2, 3})
zone1Block1 := types.NewWorkObject(zone1WoHeader1, zone1Body1, nil)
WriteWorkObject(db, zone1Block1.Hash(), zone1Block1, types.BlockObject, common.ZONE_CTX)

zone1Header2 := types.EmptyHeader()
zone1Body2 := types.EmptyWorkObjectBody()
zone1Body2.SetHeader(zone1Header2)
zone1WoHeader2 := types.NewWorkObjectHeader(zone1Header2.Hash(), zone1Block1.Hash(), big.NewInt(3), big.NewInt(3000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 3, common.Location{0, 1}, common.BytesToAddress([]byte{0}, common.Location{0, 1}))
zone1WoHeader2 := types.NewWorkObjectHeader(zone1Header2.Hash(), zone1Block1.Hash(), big.NewInt(3), big.NewInt(3000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 3, common.Location{0, 1}, common.BytesToAddress([]byte{0}, common.Location{0, 1}), []byte{0, 1, 2, 3})
zone1Block2 := types.NewWorkObject(zone1WoHeader2, zone1Body2, nil)
WriteWorkObject(db, zone1Block2.Hash(), zone1Block2, types.BlockObject, common.ZONE_CTX)

Expand Down Expand Up @@ -588,7 +588,7 @@ func createTestWorkObject() *types.WorkObject {
woBody := types.EmptyWorkObjectBody()
woBody.SetHeader(types.EmptyHeader())

woHeader := types.NewWorkObjectHeader(types.EmptyRootHash, types.EmptyRootHash, big.NewInt(11), big.NewInt(30000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 1, common.LocationFromAddressBytes([]byte{0x01, 0x01}), common.BytesToAddress([]byte{0}, common.Location{0, 0}))
woHeader := types.NewWorkObjectHeader(types.EmptyRootHash, types.EmptyRootHash, big.NewInt(11), big.NewInt(30000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 1, common.LocationFromAddressBytes([]byte{0x01, 0x01}), common.BytesToAddress([]byte{0}, common.Location{0, 0}), []byte{0, 1, 2, 3})
return types.NewWorkObject(woHeader, woBody, nil)
}

Expand Down Expand Up @@ -742,7 +742,7 @@ func createBlockWithTransactions(txs types.Transactions) *types.WorkObject {
woBody := types.EmptyWorkObjectBody()
woBody.SetHeader(types.EmptyHeader())
woBody.SetTransactions(txs)
return types.NewWorkObject(types.NewWorkObjectHeader(types.EmptyRootHash, types.EmptyRootHash, big.NewInt(0), big.NewInt(30000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 1, common.LocationFromAddressBytes([]byte{0x01, 0x01}), common.BytesToAddress([]byte{0}, common.Location{0, 0})), woBody, nil)
return types.NewWorkObject(types.NewWorkObjectHeader(types.EmptyRootHash, types.EmptyRootHash, big.NewInt(0), big.NewInt(30000), big.NewInt(42), types.EmptyRootHash, types.BlockNonce{23}, 1, 1, common.LocationFromAddressBytes([]byte{0x01, 0x01}), common.BytesToAddress([]byte{0}, common.Location{0, 0}), []byte{0, 1, 2, 3}), woBody, nil)
}

func writeBlockForReceipts(db ethdb.Database, hash common.Hash, txs types.Transactions) {
Expand Down
Loading
Loading