Skip to content

Commit

Permalink
Check if Qi address is in-scope before adding to UTXO set
Browse files Browse the repository at this point in the history
  • Loading branch information
jdowning100 committed Apr 12, 2024
1 parent 1ce39f1 commit 498b1af
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 8 deletions.
42 changes: 42 additions & 0 deletions common/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,36 @@ func (a Address) InternalAndQuaiAddress() (InternalAddress, error) {
return *internal, nil
}

func CheckIfBytesAreInternalAndQiAddress(b []byte, nodeLocation Location) error {
if len(b) != AddressLength {
return fmt.Errorf("address %s is not %d bytes long", hexutil.Encode(b), AddressLength)
}
if !IsInChainScope(b, nodeLocation) {
return ErrInvalidScope
}
if AddressBytes(b).IsInQuaiLedgerScope() {
return ErrQuaiAddress
}
return nil
}

func (a Address) InternalAndQiAddress() (InternalAddress, error) {
if a.inner == nil {
return InternalAddress{}, ErrNilInner
}
if a.IsInQuaiLedgerScope() {
return InternalAddress{}, ErrQuaiAddress
}
internal, ok := a.inner.(*InternalAddress)
if !ok {
return InternalAddress{}, ErrInvalidScope
}
if internal == nil {
return InternalAddress{}, ErrNilInner
}
return *internal, nil
}

func (a Address) IsInQiLedgerScope() bool {
// The first bit of the second byte is set if the address is in the Qi ledger
return a.Bytes()[1] > 127
Expand Down Expand Up @@ -358,6 +388,18 @@ func (a AddressBytes) hex() []byte {
return buf[:]
}

func (a AddressBytes) Location() *Location {
// Extract nibbles
lowerNib := a[0] & 0x0F // Lower 4 bits
upperNib := (a[0] & 0xF0) >> 4 // Upper 4 bits, shifted right
return &Location{upperNib, lowerNib}
}

func (a AddressBytes) IsInQuaiLedgerScope() bool {
// The first bit of the second byte is not set if the address is in the Quai ledger
return a[1] <= 127
}

func MakeErrQiAddress(addr string) error {
return fmt.Errorf("address %s is in Qi ledger scope and is not in Quai ledger scope", addr)
}
4 changes: 3 additions & 1 deletion core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,8 @@ func AddGenesisUtxos(state *state.StateDB, nodeLocation common.Location, logger
Denomination: uint8(utxo.Denomination),
}

state.CreateUTXO(hash, uint16(utxo.Index), newUtxo)
if err := state.CreateUTXO(hash, uint16(utxo.Index), newUtxo); err != nil {
panic(fmt.Sprintf("Failed to create genesis UTXO: %v", err))
}
}
}
7 changes: 6 additions & 1 deletion core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,17 +575,22 @@ func (s *StateDB) DeleteUTXO(txHash common.Hash, outputIndex uint16) {
}

// CreateUTXO explicitly creates a UTXO entry.
func (s *StateDB) CreateUTXO(txHash common.Hash, outputIndex uint16, utxo *types.UtxoEntry) {
func (s *StateDB) CreateUTXO(txHash common.Hash, outputIndex uint16, utxo *types.UtxoEntry) error {
if metrics_config.MetricsEnabled() {
defer func(start time.Time) { stateMetrics.WithLabelValues("CreateUTXO").Add(float64(time.Since(start))) }(time.Now())
}
// This check is largely redundant, but it's a good sanity check. Might be removed in the future.
if err := common.CheckIfBytesAreInternalAndQiAddress(utxo.Address, s.nodeLocation); err != nil {
return err
}
data, err := rlp.EncodeToBytes(utxo)
if err != nil {
panic(fmt.Errorf("can't encode UTXO entry at %x: %v", txHash, err))
}
if err := s.utxoTrie.TryUpdate(utxoKey(txHash, outputIndex), data); err != nil {
s.setError(fmt.Errorf("createUTXO (%x) error: %v", txHash, err))
}
return nil
}

func (s *StateDB) CommitUTXOs() (common.Hash, error) {
Expand Down
12 changes: 9 additions & 3 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,9 @@ func (p *StateProcessor) Process(block *types.WorkObject, etxSet *types.EtxSet)
return nil, nil, nil, nil, 0, fmt.Errorf("etx %032x emits UTXO with value %d greater than max denomination", tx.Hash(), tx.Value().Int64())
}
// There are no more checks to be made as the ETX is worked so add it to the set
statedb.CreateUTXO(tx.OriginatingTxHash(), tx.ETXIndex(), types.NewUtxoEntry(types.NewTxOut(uint8(tx.Value().Int64()), tx.To().Bytes())))
if err := statedb.CreateUTXO(tx.OriginatingTxHash(), tx.ETXIndex(), types.NewUtxoEntry(types.NewTxOut(uint8(tx.Value().Int64()), tx.To().Bytes()))); err != nil {
return nil, nil, nil, nil, 0, fmt.Errorf("could not create UTXO for etx %032x: %w", tx.Hash(), err)
}
if err := gp.SubGas(params.CallValueTransferGas); err != nil {
return nil, nil, nil, nil, 0, err
}
Expand Down Expand Up @@ -369,7 +371,9 @@ func (p *StateProcessor) Process(block *types.WorkObject, etxSet *types.EtxSet)
return nil, nil, nil, nil, 0, fmt.Errorf("coinbase tx emits UTXO with To address not equal to block coinbase")
}
utxo := types.NewUtxoEntry(&txOut)
statedb.CreateUTXO(qiTransactions[0].Hash(), uint16(txOutIdx), utxo)
if err := statedb.CreateUTXO(qiTransactions[0].Hash(), uint16(txOutIdx), utxo); err != nil {
return nil, nil, nil, nil, 0, fmt.Errorf("could not create UTXO for coinbase tx %032x: %w", qiTransactions[0].Hash(), err)
}
p.logger.WithFields(log.Fields{
"txHash": qiTransactions[0].Hash().Hex(),
"txOutIdx": txOutIdx,
Expand Down Expand Up @@ -596,7 +600,9 @@ func ProcessQiTx(tx *types.Transaction, chain ChainContext, updateState bool, cu
// This output creates a normal UTXO
utxo := types.NewUtxoEntry(&txOut)
if updateState {
statedb.CreateUTXO(tx.Hash(), uint16(txOutIdx), utxo)
if err := statedb.CreateUTXO(tx.Hash(), uint16(txOutIdx), utxo); err != nil {
return nil, nil, err
}
}
}
}
Expand Down
14 changes: 11 additions & 3 deletions core/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,9 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t
if tx.Value().Int64() > types.MaxDenomination {
return nil, fmt.Errorf("tx %032x emits UTXO with value greater than max denomination", tx.Hash())
}
env.state.CreateUTXO(tx.OriginatingTxHash(), tx.ETXIndex(), types.NewUtxoEntry(types.NewTxOut(uint8(tx.Value().Int64()), tx.To().Bytes())))
if err := env.state.CreateUTXO(tx.OriginatingTxHash(), tx.ETXIndex(), types.NewUtxoEntry(types.NewTxOut(uint8(tx.Value().Int64()), tx.To().Bytes()))); err != nil {
return nil, err
}
gasUsed := env.wo.Header().GasUsed()
gasUsed += params.CallValueTransferGas
env.wo.Header().SetGasUsed(gasUsed)
Expand Down Expand Up @@ -1468,7 +1470,11 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment) error {
env.state.DeleteUTXO(utxo.TxHash, utxo.Index)
}
for outPoint, utxo := range utxosCreate {
env.state.CreateUTXO(outPoint.TxHash, outPoint.Index, utxo)
if err := env.state.CreateUTXO(outPoint.TxHash, outPoint.Index, utxo); err != nil {
// This should never happen and will invalidate the block
log.Global.Errorf("Failed to create UTXO %032x:%d: %v", outPoint.TxHash, outPoint.Index, err)
return err
}
}
gasUsed = env.wo.Header().GasUsed()
env.wo.Header().SetGasUsed(gasUsed + txGas)
Expand Down Expand Up @@ -1518,7 +1524,9 @@ func createCoinbaseTxWithFees(header *types.WorkObject, fees *big.Int, state *st
tx := types.NewTx(qiTx)
for i, out := range qiTx.TxOut {
// this may be unnecessary
state.CreateUTXO(tx.Hash(), uint16(i), types.NewUtxoEntry(&out))
if err := state.CreateUTXO(tx.Hash(), uint16(i), types.NewUtxoEntry(&out)); err != nil {
return nil, err
}
}
return tx, nil
}

0 comments on commit 498b1af

Please sign in to comment.