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

Added check for preventing smaller denominations creating larger ones #1786

Merged
merged 2 commits into from
May 24, 2024
Merged
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
11 changes: 11 additions & 0 deletions common/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,3 +407,14 @@ func MakeErrQiAddress(addr string) error {
func (a Address) MixedcaseAddress() MixedcaseAddress {
return NewMixedcaseAddress(a)
}

func IsConversionOutput(a []byte, nodeLocation Location) bool {
if len(a) != AddressLength {
return false
}
// Extract nibbles
lowerNib := a[0] & 0x0F // Lower 4 bits
upperNib := (a[0] & 0xF0) >> 4 // Upper 4 bits, shifted right

return Location{upperNib, lowerNib}.Equal(nodeLocation) && a[1] <= 127
}
47 changes: 45 additions & 2 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,7 @@ func ValidateQiTxInputs(tx *types.Transaction, chain ChainContext, statedb *stat
}
totalQitIn := big.NewInt(0)
addresses := make(map[common.AddressBytes]struct{})
inputs := make(map[uint]uint64)
for _, txIn := range tx.TxIn() {
utxo := statedb.GetUTXO(txIn.PreviousOutPoint.TxHash, txIn.PreviousOutPoint.Index)
if utxo == nil {
Expand Down Expand Up @@ -668,6 +669,24 @@ func ValidateQiTxInputs(tx *types.Transaction, chain ChainContext, statedb *stat
return nil, errors.New(str)
}
totalQitIn.Add(totalQitIn, types.Denominations[denomination])
inputs[uint(denomination)]++
}
outputs := make(map[uint]uint64)
for _, txOut := range tx.TxOut() {
if txOut.Denomination > types.MaxDenomination {
str := fmt.Sprintf("transaction output value of %v is "+
"higher than max allowed value of %v",
txOut.Denomination,
types.MaxDenomination)
return nil, errors.New(str)
}
outputs[uint(txOut.Denomination)]++
if common.IsConversionOutput(txOut.Address, location) { // Qi->Quai conversion
outputs[uint(txOut.Denomination)] -= 1 // This output no longer exists because it has been aggregated
}
}
if err := CheckDenominations(inputs, outputs); err != nil {
return nil, err
}
return totalQitIn, nil

Expand Down Expand Up @@ -725,6 +744,8 @@ func ValidateQiTxOutputsAndSignature(tx *types.Transaction, chain ChainContext,
totalConvertQitOut.Add(totalConvertQitOut, types.Denominations[txOut.Denomination]) // Add to total conversion output for aggregation
delete(addresses, toAddr.Bytes20())
continue
} else if toAddr.IsInQuaiLedgerScope() {
return nil, fmt.Errorf("tx [%v] emits UTXO with To address not in the Qi ledger scope", tx.Hash().Hex())
}

if !toAddr.Location().Equal(location) { // This output creates an ETX
Expand Down Expand Up @@ -841,7 +862,7 @@ func ProcessQiTx(tx *types.Transaction, chain ChainContext, updateState bool, ch
}

addresses := make(map[common.AddressBytes]struct{})

inputs := make(map[uint]uint64)
totalQitIn := big.NewInt(0)
pubKeys := make([]*btcec.PublicKey, 0)
for _, txIn := range tx.TxIn() {
Expand Down Expand Up @@ -881,13 +902,15 @@ func ProcessQiTx(tx *types.Transaction, chain ChainContext, updateState bool, ch
return nil, nil, errors.New(str)
}
totalQitIn.Add(totalQitIn, types.Denominations[denomination])
inputs[uint(denomination)]++
if updateState { // only update the state if requested (txpool check does not need to update the state)
statedb.DeleteUTXO(txIn.PreviousOutPoint.TxHash, txIn.PreviousOutPoint.Index)
}
}
var ETXRCount int
var ETXPCount int
etxs := make([]*types.ExternalTx, 0)
outputs := make(map[uint]uint64)
totalQitOut := big.NewInt(0)
totalConvertQitOut := big.NewInt(0)
conversion := false
Expand All @@ -914,6 +937,7 @@ func ProcessQiTx(tx *types.Transaction, chain ChainContext, updateState bool, ch
return nil, nil, errors.New("Duplicate address in QiTx outputs: " + toAddr.String())
}
addresses[toAddr.Bytes20()] = struct{}{}
outputs[uint(txOut.Denomination)]++

if toAddr.Location().Equal(location) && toAddr.IsInQuaiLedgerScope() { // Qi->Quai conversion
conversion = true
Expand All @@ -922,8 +946,11 @@ func ProcessQiTx(tx *types.Transaction, chain ChainContext, updateState bool, ch
return nil, nil, fmt.Errorf("tx %v emits UTXO with value %d less than minimum denomination %d", tx.Hash().Hex(), txOut.Denomination, params.MinQiConversionDenomination)
}
totalConvertQitOut.Add(totalConvertQitOut, types.Denominations[txOut.Denomination]) // Add to total conversion output for aggregation
outputs[uint(txOut.Denomination)] -= 1 // This output no longer exists because it has been aggregated
delete(addresses, toAddr.Bytes20())
continue
} else if toAddr.IsInQuaiLedgerScope() {
return nil, nil, fmt.Errorf("tx %v emits UTXO with To address not in the Qi ledger scope", tx.Hash().Hex())
}

if !toAddr.Location().Equal(location) { // This output creates an ETX
Expand Down Expand Up @@ -1014,7 +1041,9 @@ func ProcessQiTx(tx *types.Transaction, chain ChainContext, updateState bool, ch
etxs = append(etxs, &etxInner)
txFeeInQit.Sub(txFeeInQit, txFeeInQit) // Fee goes entirely to gas to pay for conversion
}

if err := CheckDenominations(inputs, outputs); err != nil {
return nil, nil, err
}
// Ensure the transaction signature is valid
if checkSig {
var finalKey *btcec.PublicKey
Expand All @@ -1041,6 +1070,20 @@ func ProcessQiTx(tx *types.Transaction, chain ChainContext, updateState bool, ch
return txFeeInQit, etxs, nil
}

// Go through all denominations largest to smallest, check if the input exists as the output, if not, convert it to the respective number of bills for the next smallest denomination, then repeat the check. Subtract the 'carry' when the outputs match the carry for that denomination.
func CheckDenominations(inputs, outputs map[uint]uint64) error {
carries := make(map[uint]uint64)
for i := types.MaxDenomination; i >= 0; i-- {
if outputs[uint(i)] <= inputs[uint(i)]+carries[uint(i)] {
diff := new(big.Int).SetUint64((inputs[uint(i)] + carries[uint(i)]) - (outputs[uint(i)]))
carries[uint(i-1)] += diff.Mul(diff, (new(big.Int).Div(types.Denominations[uint8(i)], types.Denominations[uint8(i-1)]))).Uint64()
} else {
return fmt.Errorf("tx attempts to combine smaller denominations into larger one for denomination %d", i)
}
}
return nil
}

// Apply State
func (p *StateProcessor) Apply(batch ethdb.Batch, block *types.WorkObject) ([]*types.Log, error) {
nodeCtx := p.hc.NodeCtx()
Expand Down
16 changes: 11 additions & 5 deletions core/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -1468,7 +1468,7 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment) error {
addresses := make(map[common.AddressBytes]struct{})
totalQitIn := big.NewInt(0)
utxosDelete := make([]types.OutPoint, 0)
inputDenominations := make(map[uint8]uint64)
inputs := make(map[uint]uint64)
for _, txIn := range tx.TxIn() {
utxo := env.state.GetUTXO(txIn.PreviousOutPoint.TxHash, txIn.PreviousOutPoint.Index)
if utxo == nil {
Expand All @@ -1487,14 +1487,14 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment) error {
types.MaxDenomination)
return errors.New(str)
}
inputDenominations[denomination] += 1
// Check for duplicate addresses. This also checks for duplicate inputs.
if _, exists := addresses[common.AddressBytes(utxo.Address)]; exists {
return errors.New("Duplicate address in QiTx inputs: " + common.AddressBytes(utxo.Address).String())
}
addresses[common.AddressBytes(utxo.Address)] = struct{}{}
totalQitIn.Add(totalQitIn, types.Denominations[denomination])
utxosDelete = append(utxosDelete, txIn.PreviousOutPoint)
inputs[uint(denomination)]++
}
var ETXRCount int
var ETXPCount int
Expand All @@ -1504,7 +1504,7 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment) error {
utxosCreate := make(map[types.OutPoint]*types.UtxoEntry)
conversion := false
var convertAddress common.Address
outputDenominations := make(map[uint8]uint64)
outputs := make(map[uint]uint64)
for txOutIdx, txOut := range tx.TxOut() {
if txOutIdx > types.MaxOutputIndex {
return errors.New("transaction has too many outputs")
Expand All @@ -1516,7 +1516,7 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment) error {
types.MaxDenomination)
return errors.New(str)
}
outputDenominations[txOut.Denomination] += 1
outputs[uint(txOut.Denomination)] += 1
totalQitOut.Add(totalQitOut, types.Denominations[txOut.Denomination])
toAddr := common.BytesToAddress(txOut.Address, location)
if _, exists := addresses[toAddr.Bytes20()]; exists {
Expand All @@ -1532,9 +1532,12 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment) error {
return fmt.Errorf("tx %032x emits convert UTXO with value %d less than minimum conversion denomination", tx.Hash(), txOut.Denomination)
}
totalConvertQitOut.Add(totalConvertQitOut, types.Denominations[txOut.Denomination]) // Add to total conversion output for aggregation
outputDenominations[txOut.Denomination] -= 1 // This output no longer exists because it has been aggregated
outputs[uint(txOut.Denomination)] -= 1 // This output no longer exists because it has been aggregated
delete(addresses, toAddr.Bytes20())
continue
} else if toAddr.IsInQuaiLedgerScope() {
w.logger.Error(fmt.Errorf("tx %032x emits UTXO with To address not in the Qi ledger scope", tx.Hash()))
return fmt.Errorf("tx %032x emits UTXO with To address not in the Qi ledger scope", tx.Hash())
}

if !toAddr.Location().Equal(location) { // This output creates an ETX
Expand Down Expand Up @@ -1639,6 +1642,9 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment) error {
return err
}
}
if err := CheckDenominations(inputs, outputs); err != nil {
return err
}
// We could add signature verification here, but it's already checked in the mempool and the signature can't be changed, so duplication is largely unnecessary
return nil
}
Expand Down
4 changes: 2 additions & 2 deletions internal/quaiapi/quai_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ func (s *PublicBlockChainQuaiAPI) GetCode(ctx context.Context, address common.Mi
if state == nil || err != nil {
return nil, err
}
internal, err := address.Address().InternalAndQuaiAddress()
internal, err := common.HexToAddress(address.Original(), s.b.NodeLocation()).InternalAddress()
if err != nil {
return nil, err
}
Expand All @@ -423,7 +423,7 @@ func (s *PublicBlockChainQuaiAPI) GetStorageAt(ctx context.Context, address comm
if state == nil || err != nil {
return nil, err
}
internal, err := address.Address().InternalAndQuaiAddress()
internal, err := common.HexToAddress(address.Original(), s.b.NodeLocation()).InternalAddress()
if err != nil {
return nil, err
}
Expand Down
Loading