mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-09-13 04:50:11 +00:00

* [NOD-1223] Delete unused files/packages. * [NOD-1223] Move signal and limits to the os package. * [NOD-1223] Put database and dbaccess into the db package. * [NOD-1223] Fold the logs package into the logger package. * [NOD-1223] Rename domainmessage to appmessage. * [NOD-1223] Rename to/from DomainMessage to AppMessage. * [NOD-1223] Move appmessage to the app packge. * [NOD-1223] Move protocol to the app packge. * [NOD-1223] Move the network package to the infrastructure packge. * [NOD-1223] Rename cmd to executables. * [NOD-1223] Fix go.doc in the logger package.
205 lines
6.2 KiB
Go
205 lines
6.2 KiB
Go
package blockdag
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kaspanet/kaspad/util"
|
|
|
|
"github.com/kaspanet/kaspad/app/appmessage"
|
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
|
)
|
|
|
|
// registerSubnetworks scans a list of transactions, singles out
|
|
// subnetwork registry transactions, validates them, and registers a new
|
|
// subnetwork based on it.
|
|
// This function returns an error if one or more transactions are invalid
|
|
func registerSubnetworks(dbContext dbaccess.Context, txs []*util.Tx) error {
|
|
subnetworkRegistryTxs := make([]*appmessage.MsgTx, 0)
|
|
for _, tx := range txs {
|
|
msgTx := tx.MsgTx()
|
|
|
|
if msgTx.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDRegistry) {
|
|
subnetworkRegistryTxs = append(subnetworkRegistryTxs, msgTx)
|
|
}
|
|
|
|
if subnetworkid.Less(subnetworkid.SubnetworkIDRegistry, &msgTx.SubnetworkID) {
|
|
// Transactions are ordered by subnetwork, so we can safely assume
|
|
// that the rest of the transactions will not be subnetwork registry
|
|
// transactions.
|
|
break
|
|
}
|
|
}
|
|
|
|
for _, registryTx := range subnetworkRegistryTxs {
|
|
subnetworkID, err := TxToSubnetworkID(registryTx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
exists, err := dbaccess.HasSubnetwork(dbContext, subnetworkID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exists {
|
|
createdSubnetwork := newSubnetwork(registryTx)
|
|
err := registerSubnetwork(dbContext, subnetworkID, createdSubnetwork)
|
|
if err != nil {
|
|
return errors.Errorf("failed registering subnetwork"+
|
|
"for tx '%s': %s", registryTx.TxHash(), err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// validateSubnetworkRegistryTransaction makes sure that a given subnetwork registry
|
|
// transaction is valid. Such a transaction is valid iff:
|
|
// - Its entire payload is a uint64 (8 bytes)
|
|
func validateSubnetworkRegistryTransaction(tx *appmessage.MsgTx) error {
|
|
if len(tx.Payload) != 8 {
|
|
return ruleError(ErrSubnetworkRegistry, fmt.Sprintf("validation failed: subnetwork registry"+
|
|
"tx '%s' has an invalid payload", tx.TxHash()))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TxToSubnetworkID creates a subnetwork ID from a subnetwork registry transaction
|
|
func TxToSubnetworkID(tx *appmessage.MsgTx) (*subnetworkid.SubnetworkID, error) {
|
|
txHash := tx.TxHash()
|
|
return subnetworkid.New(util.Hash160(txHash[:]))
|
|
}
|
|
|
|
// fetchSubnetwork returns a registered subnetwork.
|
|
func (dag *BlockDAG) fetchSubnetwork(subnetworkID *subnetworkid.SubnetworkID) (*subnetwork, error) {
|
|
serializedSubnetwork, err := dbaccess.FetchSubnetworkData(dag.databaseContext, subnetworkID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
subnet, err := deserializeSubnetwork(serializedSubnetwork)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return subnet, nil
|
|
}
|
|
|
|
// GasLimit returns the gas limit of a registered subnetwork. If the subnetwork does not
|
|
// exist this method returns an error.
|
|
func (dag *BlockDAG) GasLimit(subnetworkID *subnetworkid.SubnetworkID) (uint64, error) {
|
|
sNet, err := dag.fetchSubnetwork(subnetworkID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return sNet.gasLimit, nil
|
|
}
|
|
|
|
func registerSubnetwork(dbContext dbaccess.Context, subnetworkID *subnetworkid.SubnetworkID, network *subnetwork) error {
|
|
serializedSubnetwork, err := serializeSubnetwork(network)
|
|
if err != nil {
|
|
return errors.Errorf("failed to serialize sub-netowrk '%s': %s", subnetworkID, err)
|
|
}
|
|
|
|
return dbaccess.StoreSubnetwork(dbContext, subnetworkID, serializedSubnetwork)
|
|
}
|
|
|
|
type subnetwork struct {
|
|
gasLimit uint64
|
|
}
|
|
|
|
func newSubnetwork(tx *appmessage.MsgTx) *subnetwork {
|
|
return &subnetwork{
|
|
gasLimit: ExtractGasLimit(tx),
|
|
}
|
|
}
|
|
|
|
// ExtractGasLimit extracts the gas limit from the transaction payload
|
|
func ExtractGasLimit(tx *appmessage.MsgTx) uint64 {
|
|
return binary.LittleEndian.Uint64(tx.Payload[:8])
|
|
}
|
|
|
|
// serializeSubnetwork serializes a subnetwork into the following binary format:
|
|
// | gasLimit (8 bytes) |
|
|
func serializeSubnetwork(sNet *subnetwork) ([]byte, error) {
|
|
serializedSNet := bytes.NewBuffer(make([]byte, 0, 8))
|
|
|
|
// Write the gas limit
|
|
err := binary.Write(serializedSNet, byteOrder, sNet.gasLimit)
|
|
if err != nil {
|
|
return nil, errors.Errorf("failed to serialize subnetwork: %s", err)
|
|
}
|
|
|
|
return serializedSNet.Bytes(), nil
|
|
}
|
|
|
|
// deserializeSubnetwork deserializes a byte slice into a subnetwork.
|
|
// See serializeSubnetwork for the binary format.
|
|
func deserializeSubnetwork(serializedSNetBytes []byte) (*subnetwork, error) {
|
|
serializedSNet := bytes.NewBuffer(serializedSNetBytes)
|
|
|
|
// Read the gas limit
|
|
var gasLimit uint64
|
|
err := binary.Read(serializedSNet, byteOrder, &gasLimit)
|
|
if err != nil {
|
|
return nil, errors.Errorf("failed to deserialize subnetwork: %s", err)
|
|
}
|
|
|
|
return &subnetwork{
|
|
gasLimit: gasLimit,
|
|
}, nil
|
|
}
|
|
|
|
func (dag *BlockDAG) validateGasLimit(block *util.Block) error {
|
|
var currentSubnetworkID *subnetworkid.SubnetworkID
|
|
var currentSubnetworkGasLimit uint64
|
|
var currentGasUsage uint64
|
|
var err error
|
|
|
|
// We assume here that transactions are ordered by subnetworkID,
|
|
// since it was already validated in checkTransactionSanity
|
|
for _, tx := range block.Transactions() {
|
|
msgTx := tx.MsgTx()
|
|
|
|
// In native and Built-In subnetworks all txs must have Gas = 0, and that was already validated in checkTransactionSanity
|
|
// Therefore - no need to check them here.
|
|
if msgTx.SubnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) || msgTx.SubnetworkID.IsBuiltIn() {
|
|
continue
|
|
}
|
|
|
|
if !msgTx.SubnetworkID.IsEqual(currentSubnetworkID) {
|
|
currentSubnetworkID = &msgTx.SubnetworkID
|
|
currentGasUsage = 0
|
|
currentSubnetworkGasLimit, err = dag.GasLimit(currentSubnetworkID)
|
|
if err != nil {
|
|
return errors.Errorf("Error getting gas limit for subnetworkID '%s': %s", currentSubnetworkID, err)
|
|
}
|
|
}
|
|
|
|
newGasUsage := currentGasUsage + msgTx.Gas
|
|
if newGasUsage < currentGasUsage { // check for overflow
|
|
str := fmt.Sprintf("Block gas usage in subnetwork with ID %s has overflown", currentSubnetworkID)
|
|
return ruleError(ErrInvalidGas, str)
|
|
}
|
|
if newGasUsage > currentSubnetworkGasLimit {
|
|
str := fmt.Sprintf("Block wastes too much gas in subnetwork with ID %s", currentSubnetworkID)
|
|
return ruleError(ErrInvalidGas, str)
|
|
}
|
|
|
|
currentGasUsage = newGasUsage
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SubnetworkID returns the node's subnetwork ID
|
|
func (dag *BlockDAG) SubnetworkID() *subnetworkid.SubnetworkID {
|
|
return dag.subnetworkID
|
|
}
|