mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-07-09 14:22:33 +00:00
[DEV-307] Create the sub-network registry (#147)
* [DEV-307] Implemented adding pending registry transactions. * [DEV-307] Implemented a skeleton for the sub-network registry algorithm. * [DEV-307] Implemented the serialization/deserialization logic for pending sub-network transactions. * [DEV-307] Implemented marking sub-network registry transactions as successfully registered. * [DEV-307] Implemented clearing pending sub-network entries and writing registered sub-networks. * [DEV-307] Added comments for all the dagio functions. * [DEV-307] Fixed a couple of bugs and added a test for checking serialization/deserialization of pending sub-network transactions. * [DEV-307] Implemented getting a registered sub-network's gas limit. * [DEV-307] Updated sub-network announcement transaction validation rules. * [DEV-307] Specified what is validated. * [DEV-307] Added initialization for lastSubNetworkID. * [DEV-307] Renamed extractValidSubNetworkRegistryTxs to validateAndExtractSubNetworkRegistryTxs to better reflect what it does. * Dev 303 implement block finality (#139) * [DEV-303] Implement block finality * [DEV-303] Add finality tests * [DEV-303] Make finality tests use maybeAcceptBlock * [DEV-303] Only check finality rules if we are fastAdd mode * [DEV-303] replace hasBiggerFinalityScoreThan checks with .finalityScore() * [DEV-307] Forgot to actually create the sub-network buckets. * [DEV-307] Wrote a full-flow test for sub-network registry and fixed a couple of bugs that it had uncovered. * [DEV-307] Took firstSubNetworkID out of dagconfig.Params. It's unnecessary there. * [DEV-307] Added persistance for dag.lastSubNetworkID. * [DEV-307] Moved sub-network stuff to their own files. Got rid of firstSubNetworkID. Replaced SubNetworkReservedLast with SubNetworkUnreservedFirst. Added ErrSubNetworkRegistry. * [DEV-307] Added a newline at the end of dag_test.go. * [DEV-307] Renamed previousFinalityPoint to initialFinalityPoint. * [DEV-307] Moved sub-network IO stuff to subnetworks.go and subnetworks_test.go. * [DEV-307] Merged dbPutRegisteredSubNetworkTx and dbRegisterSubNetwork. Fixed a too-big allocation and a block double-processing bug. * [DEV-307] Simplified the serialized representation of a slice of transactions. * [DEV-307] Fixed comments.
This commit is contained in:
parent
ade33a94f3
commit
d33d633e77
@ -96,6 +96,10 @@ type BlockDAG struct {
|
|||||||
// virtual tracks the current tips.
|
// virtual tracks the current tips.
|
||||||
virtual *virtualBlock
|
virtual *virtualBlock
|
||||||
|
|
||||||
|
// lastSubNetworkID holds the last registered sub-network ID in the DAG.
|
||||||
|
// Note that it is NOT the total amount of registered (or active) sub-networks.
|
||||||
|
lastSubNetworkID uint64
|
||||||
|
|
||||||
// These fields are related to handling of orphan blocks. They are
|
// These fields are related to handling of orphan blocks. They are
|
||||||
// protected by a combination of the chain lock and the orphan lock.
|
// protected by a combination of the chain lock and the orphan lock.
|
||||||
orphanLock sync.RWMutex
|
orphanLock sync.RWMutex
|
||||||
@ -512,8 +516,9 @@ func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block, fastAdd bo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var finalityPointCandidate *blockNode
|
initialFinalityPoint := dag.lastFinalityPoint
|
||||||
|
|
||||||
|
var finalityPointCandidate *blockNode
|
||||||
if !fastAdd {
|
if !fastAdd {
|
||||||
var err error
|
var err error
|
||||||
finalityPointCandidate, err = dag.checkFinalityRulesAndGetFinalityPointCandidate(node)
|
finalityPointCandidate, err = dag.checkFinalityRulesAndGetFinalityPointCandidate(node)
|
||||||
@ -532,6 +537,14 @@ func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block, fastAdd bo
|
|||||||
dag.lastFinalityPoint = finalityPointCandidate
|
dag.lastFinalityPoint = finalityPointCandidate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scan all accepted transactions and collect any sub-network registry
|
||||||
|
// transactions into subNetworkRegistryTxs. If any sub-network registry
|
||||||
|
// transaction is not well-formed, fail the entire block.
|
||||||
|
subNetworkRegistryTxs, err := validateAndExtractSubNetworkRegistryTxs(acceptedTxsData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Write any block status changes to DB before updating the DAG state.
|
// Write any block status changes to DB before updating the DAG state.
|
||||||
err = dag.index.flushToDB()
|
err = dag.index.flushToDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -544,6 +557,7 @@ func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block, fastAdd bo
|
|||||||
state := &dagState{
|
state := &dagState{
|
||||||
TipHashes: dag.TipHashes(),
|
TipHashes: dag.TipHashes(),
|
||||||
LastFinalityPoint: dag.lastFinalityPoint.hash,
|
LastFinalityPoint: dag.lastFinalityPoint.hash,
|
||||||
|
LastSubNetworkID: dag.lastSubNetworkID,
|
||||||
}
|
}
|
||||||
err := dbPutDAGState(dbTx, state)
|
err := dbPutDAGState(dbTx, state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -564,6 +578,22 @@ func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block, fastAdd bo
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the pending sub-network in this block to the pending sub-networks
|
||||||
|
// collection.
|
||||||
|
err = dbPutPendingSubNetworkTxs(dbTx, block.Hash(), subNetworkRegistryTxs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register all pending sub-networks between the initial finality point and
|
||||||
|
// the new one.
|
||||||
|
if initialFinalityPoint != dag.lastFinalityPoint {
|
||||||
|
err = dag.registerPendingSubNetworks(dbTx, initialFinalityPoint, dag.lastFinalityPoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Allow the index manager to call each of the currently active
|
// Allow the index manager to call each of the currently active
|
||||||
// optional indexes with the block being connected so they can
|
// optional indexes with the block being connected so they can
|
||||||
// update themselves accordingly.
|
// update themselves accordingly.
|
||||||
@ -1573,6 +1603,7 @@ func New(config *Config) (*BlockDAG, error) {
|
|||||||
warningCaches: newThresholdCaches(vbNumBits),
|
warningCaches: newThresholdCaches(vbNumBits),
|
||||||
deploymentCaches: newThresholdCaches(dagconfig.DefinedDeployments),
|
deploymentCaches: newThresholdCaches(dagconfig.DefinedDeployments),
|
||||||
blockCount: 1,
|
blockCount: 1,
|
||||||
|
lastSubNetworkID: wire.SubNetworkUnreservedFirst,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the chain state from the passed database. When the db
|
// Initialize the chain state from the passed database. When the db
|
||||||
|
@ -53,6 +53,18 @@ var (
|
|||||||
// unspent transaction output set.
|
// unspent transaction output set.
|
||||||
utxoSetBucketName = []byte("utxoset")
|
utxoSetBucketName = []byte("utxoset")
|
||||||
|
|
||||||
|
// pendingSubNetworksBucketName is the name of the db bucket used to store the
|
||||||
|
// pending sub-networks.
|
||||||
|
pendingSubNetworksBucketName = []byte("pendingsubnetworks")
|
||||||
|
|
||||||
|
// registeredSubNetworkTxsBucketName is the name of the db bucket used to house
|
||||||
|
// the transactions that have been used to register sub-networks.
|
||||||
|
registeredSubNetworkTxsBucketName = []byte("registeredsubnetworktxs")
|
||||||
|
|
||||||
|
// subNetworksBucketName is the name of the db bucket used to store the
|
||||||
|
// sub-network registry.
|
||||||
|
subNetworksBucketName = []byte("subnetworks")
|
||||||
|
|
||||||
// byteOrder is the preferred byte order used for serializing numeric
|
// byteOrder is the preferred byte order used for serializing numeric
|
||||||
// fields for storage in the database.
|
// fields for storage in the database.
|
||||||
byteOrder = binary.LittleEndian
|
byteOrder = binary.LittleEndian
|
||||||
@ -567,48 +579,6 @@ func deserializeUTXOEntry(serialized []byte) (*UTXOEntry, error) {
|
|||||||
return entry, nil
|
return entry, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// dbFetchUTXOEntry uses an existing database transaction to fetch the specified
|
|
||||||
// transaction output from the UTXO set.
|
|
||||||
//
|
|
||||||
// When there is no entry for the provided output, nil will be returned for both
|
|
||||||
// the entry and the error.
|
|
||||||
func dbFetchUTXOEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UTXOEntry, error) {
|
|
||||||
// Fetch the unspent transaction output information for the passed
|
|
||||||
// transaction output. Return now when there is no entry.
|
|
||||||
key := outpointKey(outpoint)
|
|
||||||
utxoBucket := dbTx.Metadata().Bucket(utxoSetBucketName)
|
|
||||||
serializedUTXO := utxoBucket.Get(*key)
|
|
||||||
recycleOutpointKey(key)
|
|
||||||
if serializedUTXO == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A non-nil zero-length entry means there is an entry in the database
|
|
||||||
// for a spent transaction output which should never be the case.
|
|
||||||
if len(serializedUTXO) == 0 {
|
|
||||||
return nil, AssertError(fmt.Sprintf("database contains entry "+
|
|
||||||
"for spent tx output %v", outpoint))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserialize the utxo entry and return it.
|
|
||||||
entry, err := deserializeUTXOEntry(serializedUTXO)
|
|
||||||
if err != nil {
|
|
||||||
// Ensure any deserialization errors are returned as database
|
|
||||||
// corruption errors.
|
|
||||||
if isDeserializeErr(err) {
|
|
||||||
return nil, database.Error{
|
|
||||||
ErrorCode: database.ErrCorruption,
|
|
||||||
Description: fmt.Sprintf("corrupt UTXO entry "+
|
|
||||||
"for %v: %v", outpoint, err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return entry, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// dbPutUTXODiff uses an existing database transaction to update the UTXO set
|
// dbPutUTXODiff uses an existing database transaction to update the UTXO set
|
||||||
// in the database based on the provided UTXO view contents and state. In
|
// in the database based on the provided UTXO view contents and state. In
|
||||||
// particular, only the entries that have been marked as modified are written
|
// particular, only the entries that have been marked as modified are written
|
||||||
@ -700,6 +670,7 @@ func dbFetchHeightByHash(dbTx database.Tx, hash *daghash.Hash) (int32, error) {
|
|||||||
type dagState struct {
|
type dagState struct {
|
||||||
TipHashes []daghash.Hash
|
TipHashes []daghash.Hash
|
||||||
LastFinalityPoint daghash.Hash
|
LastFinalityPoint daghash.Hash
|
||||||
|
LastSubNetworkID uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// serializeDAGState returns the serialization of the DAG state.
|
// serializeDAGState returns the serialization of the DAG state.
|
||||||
@ -805,6 +776,25 @@ func (dag *BlockDAG) createDAGState() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the bucket that houses the pending sub-networks.
|
||||||
|
_, err = meta.CreateBucket(pendingSubNetworksBucketName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the bucket that houses the registered sub-networks to
|
||||||
|
// their registry transactions index.
|
||||||
|
_, err = meta.CreateBucket(registeredSubNetworkTxsBucketName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the bucket that houses the registered sub-networks.
|
||||||
|
_, err = meta.CreateBucket(subNetworksBucketName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Save the genesis block to the block index database.
|
// Save the genesis block to the block index database.
|
||||||
err = dbStoreBlockNode(dbTx, node)
|
err = dbStoreBlockNode(dbTx, node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1002,6 +992,9 @@ func (dag *BlockDAG) initDAGState() error {
|
|||||||
// Set the last finality point
|
// Set the last finality point
|
||||||
dag.lastFinalityPoint = dag.index.LookupNode(&state.LastFinalityPoint)
|
dag.lastFinalityPoint = dag.index.LookupNode(&state.LastFinalityPoint)
|
||||||
|
|
||||||
|
// Set the last sub-network ID
|
||||||
|
dag.lastSubNetworkID = state.LastSubNetworkID
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -545,18 +545,20 @@ func TestDAGStateSerialization(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "genesis",
|
name: "genesis",
|
||||||
state: &dagState{
|
state: &dagState{
|
||||||
LastFinalityPoint: *newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
|
|
||||||
TipHashes: []daghash.Hash{*newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")},
|
TipHashes: []daghash.Hash{*newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")},
|
||||||
|
LastFinalityPoint: *newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
|
||||||
|
LastSubNetworkID: 123456,
|
||||||
},
|
},
|
||||||
serialized: []byte("{\"TipHashes\":[[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0]],\"LastFinalityPoint\":[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0]}"),
|
serialized: []byte("{\"TipHashes\":[[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0]],\"LastFinalityPoint\":[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0],\"LastSubNetworkID\":123456}"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "block 1",
|
name: "block 1",
|
||||||
state: &dagState{
|
state: &dagState{
|
||||||
LastFinalityPoint: *newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
|
|
||||||
TipHashes: []daghash.Hash{*newHashFromStr("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048")},
|
TipHashes: []daghash.Hash{*newHashFromStr("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048")},
|
||||||
|
LastFinalityPoint: *newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
|
||||||
|
LastSubNetworkID: 684321,
|
||||||
},
|
},
|
||||||
serialized: []byte("{\"TipHashes\":[[72,96,235,24,191,27,22,32,227,126,148,144,252,138,66,117,20,65,111,215,81,89,171,134,104,142,154,131,0,0,0,0]],\"LastFinalityPoint\":[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0]}"),
|
serialized: []byte("{\"TipHashes\":[[72,96,235,24,191,27,22,32,227,126,148,144,252,138,66,117,20,65,111,215,81,89,171,134,104,142,154,131,0,0,0,0]],\"LastFinalityPoint\":[111,226,140,10,182,241,179,114,193,166,162,70,174,99,247,79,147,30,131,101,225,90,8,156,104,214,25,0,0,0,0,0],\"LastSubNetworkID\":684321}"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,9 +211,14 @@ const (
|
|||||||
|
|
||||||
// ErrFinality indicates that a block doesn't adhere to the finality rules
|
// ErrFinality indicates that a block doesn't adhere to the finality rules
|
||||||
ErrFinality
|
ErrFinality
|
||||||
|
|
||||||
// ErrTransactionsNotSorted indicates that transactions in block are not
|
// ErrTransactionsNotSorted indicates that transactions in block are not
|
||||||
// sorted by subnetwork
|
// sorted by sub-network
|
||||||
ErrTransactionsNotSorted
|
ErrTransactionsNotSorted
|
||||||
|
|
||||||
|
// ErrSubNetwork indicates that a block doesn't adhere to the sub-network
|
||||||
|
// registry rules
|
||||||
|
ErrSubNetworkRegistry
|
||||||
)
|
)
|
||||||
|
|
||||||
// Map of ErrorCode values back to their constant names for pretty printing.
|
// Map of ErrorCode values back to their constant names for pretty printing.
|
||||||
|
357
blockdag/subnetworks.go
Normal file
357
blockdag/subnetworks.go
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
package blockdag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"github.com/daglabs/btcd/dagconfig/daghash"
|
||||||
|
"github.com/daglabs/btcd/database"
|
||||||
|
"github.com/daglabs/btcd/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// validateAndExtractSubNetworkRegistryTxs filters the given input and extracts a list
|
||||||
|
// of valid sub-network registry transactions.
|
||||||
|
func validateAndExtractSubNetworkRegistryTxs(txs []*TxWithBlockHash) ([]*wire.MsgTx, error) {
|
||||||
|
validSubNetworkRegistryTxs := make([]*wire.MsgTx, 0)
|
||||||
|
for _, txData := range txs {
|
||||||
|
tx := txData.Tx.MsgTx()
|
||||||
|
if tx.SubNetworkID == wire.SubNetworkRegistry {
|
||||||
|
err := validateSubNetworkRegistryTransaction(tx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
validSubNetworkRegistryTxs = append(validSubNetworkRegistryTxs, tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return validSubNetworkRegistryTxs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateSubNetworkRegistryTransaction makes sure that a given sub-network registry
|
||||||
|
// transaction is valid. Such a transaction is valid iff:
|
||||||
|
// - Its entire payload is a uint64 (8 bytes)
|
||||||
|
func validateSubNetworkRegistryTransaction(tx *wire.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
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerPendingSubNetworks attempts to register all the pending sub-networks that
|
||||||
|
// had previously been defined between the initial finality point and the new one.
|
||||||
|
// Note: transactions within newFinalityPoint itself are not registered. Instead, they will
|
||||||
|
// be registered when the next finality point is chosen; then it will be the
|
||||||
|
// initialFinalityPoint.
|
||||||
|
func (dag *BlockDAG) registerPendingSubNetworks(dbTx database.Tx, initialFinalityPoint *blockNode, newFinalityPoint *blockNode) error {
|
||||||
|
var stack []*blockNode
|
||||||
|
for currentNode := newFinalityPoint; currentNode != initialFinalityPoint; currentNode = currentNode.selectedParent {
|
||||||
|
stack = append(stack, currentNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register a pending sub-network for all blues. The block itself is not explicitly
|
||||||
|
// registered since it will be one of the blues of the next block.
|
||||||
|
// Note that this means that the very last block in the selected parent chain is not
|
||||||
|
// registered. This is intentional.
|
||||||
|
for i := len(stack) - 1; i >= 0; i-- {
|
||||||
|
currentNode := stack[i]
|
||||||
|
for _, blue := range currentNode.blues {
|
||||||
|
err := dag.registerPendingSubNetworksInBlock(dbTx, blue.hash)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to register pending sub-networks: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerPendingSubNetworksInBlock attempts to register all the sub-networks
|
||||||
|
// that had been defined in a given block.
|
||||||
|
func (dag *BlockDAG) registerPendingSubNetworksInBlock(dbTx database.Tx, blockHash daghash.Hash) error {
|
||||||
|
pendingSubNetworkTxs, err := dbGetPendingSubNetworkTxs(dbTx, blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to retrieve pending sub-network txs in block '%s': %s", blockHash, err)
|
||||||
|
}
|
||||||
|
for _, tx := range pendingSubNetworkTxs {
|
||||||
|
if !dbIsRegisteredSubNetworkTx(dbTx, tx.TxHash()) {
|
||||||
|
createdSubNetwork := newSubNetwork(tx)
|
||||||
|
err := dbRegisterSubNetwork(dbTx, dag.lastSubNetworkID, tx.TxHash(), createdSubNetwork)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed registering sub-network"+
|
||||||
|
"for tx '%s' in block '%s': %s", tx.TxHash(), blockHash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dag.lastSubNetworkID++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbRemovePendingSubNetworkTxs(dbTx, blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to remove block '%s'"+
|
||||||
|
"from pending sub-networks: %s", blockHash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// subNetwork returns a registered-and-finalized sub-network. If the sub-network
|
||||||
|
// does not exist this method returns an error.
|
||||||
|
func (dag *BlockDAG) subNetwork(subNetworkID uint64) (*subNetwork, error) {
|
||||||
|
var sNet *subNetwork
|
||||||
|
var err error
|
||||||
|
dbErr := dag.db.View(func(dbTx database.Tx) error {
|
||||||
|
sNet, err = dbGetSubNetwork(dbTx, subNetworkID)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if dbErr != nil {
|
||||||
|
return nil, fmt.Errorf("could not retrieve sub-network '%d': %s", subNetworkID, dbErr)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not retrieve sub-network '%d': %s", subNetworkID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sNet, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GasLimit returns the gas limit of a registered-and-finalized sub-network. If
|
||||||
|
// the sub-network does not exist this method returns an error.
|
||||||
|
func (dag *BlockDAG) GasLimit(subNetworkID uint64) (uint64, error) {
|
||||||
|
sNet, err := dag.subNetwork(subNetworkID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sNet.gasLimit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// The sub-network registry consists of three buckets:
|
||||||
|
// a. The pending sub-network bucket
|
||||||
|
// b. The registered transaction bucket
|
||||||
|
// c. The sub-network bucket
|
||||||
|
//
|
||||||
|
// All newly-discovered sub-network registry transactions are stored in the
|
||||||
|
// pending sub-network bucket. These transactions are withheld until the
|
||||||
|
// blocks that contain them become final.
|
||||||
|
//
|
||||||
|
// Once the block of a sub-network registry transaction becomes final, all the
|
||||||
|
// transactions within that block are retrieved and checked for validity.
|
||||||
|
// Valid transactions are then:
|
||||||
|
// 1. Assigned a sub-network ID
|
||||||
|
// 2. Added to the registered transaction bucket
|
||||||
|
// 3. Added to the sub-network bucket
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// dbPutPendingSubNetworkTxs stores mappings from a block (via its hash) to an
|
||||||
|
// array of sub-network transactions.
|
||||||
|
func dbPutPendingSubNetworkTxs(dbTx database.Tx, blockHash *daghash.Hash, subNetworkRegistryTxs []*wire.MsgTx) error {
|
||||||
|
// Empty blocks are not written
|
||||||
|
if len(subNetworkRegistryTxs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedTxs, err := serializeSubNetworkRegistryTxs(subNetworkRegistryTxs)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to serialize pending sub-network txs in block '%s': %s", blockHash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the serialized transactions
|
||||||
|
bucket := dbTx.Metadata().Bucket(pendingSubNetworksBucketName)
|
||||||
|
err = bucket.Put(blockHash[:], serializedTxs)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to write pending sub-network txs in block '%s': %s", blockHash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbGetPendingSubNetworkTxs retrieves an array of sub-network transactions,
|
||||||
|
// associated with a block's hash, that was previously stored with
|
||||||
|
// dbPutPendingSubNetworkTxs.
|
||||||
|
// Returns an empty slice if the hash was not previously stored.
|
||||||
|
func dbGetPendingSubNetworkTxs(dbTx database.Tx, blockHash daghash.Hash) ([]*wire.MsgTx, error) {
|
||||||
|
bucket := dbTx.Metadata().Bucket(pendingSubNetworksBucketName)
|
||||||
|
serializedTxsBytes := bucket.Get(blockHash[:])
|
||||||
|
if serializedTxsBytes == nil {
|
||||||
|
return []*wire.MsgTx{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
txs, err := deserializeSubNetworkRegistryTxs(serializedTxsBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize pending sub-network txs for block '%s': %s", blockHash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return txs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// serializeSubNetworkRegistryTxs serializes a slice of MsgTxs by serializing each transaction
|
||||||
|
// individually and appending it to one long byte slice.
|
||||||
|
func serializeSubNetworkRegistryTxs(subNetworkRegistryTxs []*wire.MsgTx) ([]byte, error) {
|
||||||
|
// Calculate the length in bytes of the serialized transactions
|
||||||
|
serializedTxsLength := uint64(0)
|
||||||
|
for _, tx := range subNetworkRegistryTxs {
|
||||||
|
serializedTxsLength += uint64(tx.SerializeSize())
|
||||||
|
}
|
||||||
|
serializedTxs := bytes.NewBuffer(make([]byte, 0, serializedTxsLength))
|
||||||
|
|
||||||
|
// Write each transaction in the order it appears in
|
||||||
|
for _, tx := range subNetworkRegistryTxs {
|
||||||
|
err := tx.Serialize(serializedTxs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to serialize tx '%s': %s", tx.TxHash(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return serializedTxs.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// deserializeSubNetworkRegistryTxs deserializes a byte slice into a slice of MsgTxs.
|
||||||
|
func deserializeSubNetworkRegistryTxs(serializedTxsBytes []byte) ([]*wire.MsgTx, error) {
|
||||||
|
serializedTxs := bytes.NewBuffer(serializedTxsBytes)
|
||||||
|
|
||||||
|
// Read each transaction and store it in txs until the end of the buffer
|
||||||
|
txs := make([]*wire.MsgTx, 0)
|
||||||
|
for serializedTxs.Len() > 0 {
|
||||||
|
var tx wire.MsgTx
|
||||||
|
err := tx.Deserialize(serializedTxs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize pending sub-network txs: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
txs = append(txs, &tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return txs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbRemovePendingSubNetworkTxs deletes an array of sub-network transactions,
|
||||||
|
// associated with a block's hash, that was previously stored with
|
||||||
|
// dbPutPendingSubNetworkTxs.
|
||||||
|
// This function does not return an error if the hash was not previously stored.
|
||||||
|
func dbRemovePendingSubNetworkTxs(dbTx database.Tx, blockHash daghash.Hash) error {
|
||||||
|
bucket := dbTx.Metadata().Bucket(pendingSubNetworksBucketName)
|
||||||
|
|
||||||
|
err := bucket.Delete(blockHash[:])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to remove pending sub-network txs in block '%s': %s", blockHash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbIsRegisteredSubNetworkTx checks whether a sub-network registry transaction
|
||||||
|
// had successfully registered a sub-network.
|
||||||
|
func dbIsRegisteredSubNetworkTx(dbTx database.Tx, txHash daghash.Hash) bool {
|
||||||
|
bucket := dbTx.Metadata().Bucket(registeredSubNetworkTxsBucketName)
|
||||||
|
subNetworkIDBytes := bucket.Get(txHash[:])
|
||||||
|
|
||||||
|
return subNetworkIDBytes != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbRegisterSubNetwork stores mappings:
|
||||||
|
// a. from the ID of the sub-network to the sub-network data.
|
||||||
|
// b. from the hash of a sub-network registry transaction to the sub-network ID.
|
||||||
|
func dbRegisterSubNetwork(dbTx database.Tx, subNetworkID uint64, txHash daghash.Hash, network *subNetwork) error {
|
||||||
|
// Serialize the sub-network ID
|
||||||
|
subNetworkIDBytes := make([]byte, 8)
|
||||||
|
byteOrder.PutUint64(subNetworkIDBytes, subNetworkID)
|
||||||
|
|
||||||
|
// Serialize the sub-network
|
||||||
|
serializedSubNetwork, err := serializeSubNetwork(network)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to serialize sub-netowrk of tx '%s': %s", network.txHash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the sub-network
|
||||||
|
subNetworksBucket := dbTx.Metadata().Bucket(subNetworksBucketName)
|
||||||
|
err = subNetworksBucket.Put(subNetworkIDBytes, serializedSubNetwork)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to write sub-netowrk of tx '%s': %s", network.txHash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the mapping between txHash and subNetworkID
|
||||||
|
registeredSubNetworkTxs := dbTx.Metadata().Bucket(registeredSubNetworkTxsBucketName)
|
||||||
|
err = registeredSubNetworkTxs.Put(txHash[:], subNetworkIDBytes)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to put registered sub-networkTx '%s': %s", txHash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbGetSubNetwork(dbTx database.Tx, subNetworkID uint64) (*subNetwork, error) {
|
||||||
|
// Serialize the sub-network ID
|
||||||
|
subNetworkIDBytes := make([]byte, 8)
|
||||||
|
byteOrder.PutUint64(subNetworkIDBytes, subNetworkID)
|
||||||
|
|
||||||
|
// Get the sub-network
|
||||||
|
bucket := dbTx.Metadata().Bucket(subNetworksBucketName)
|
||||||
|
serializedSubNetwork := bucket.Get(subNetworkIDBytes)
|
||||||
|
if serializedSubNetwork == nil {
|
||||||
|
return nil, fmt.Errorf("sub-network '%d' not found", subNetworkID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return deserializeSubNetwork(serializedSubNetwork)
|
||||||
|
}
|
||||||
|
|
||||||
|
type subNetwork struct {
|
||||||
|
txHash daghash.Hash
|
||||||
|
gasLimit uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSubNetwork(tx *wire.MsgTx) *subNetwork {
|
||||||
|
txHash := tx.TxHash()
|
||||||
|
gasLimit := binary.LittleEndian.Uint64(tx.Payload[:8])
|
||||||
|
|
||||||
|
return &subNetwork{
|
||||||
|
txHash: txHash,
|
||||||
|
gasLimit: gasLimit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// serializeSubNetwork serializes a subNetwork into the following binary format:
|
||||||
|
// | txHash (32 bytes) | gasLimit (8 bytes) |
|
||||||
|
func serializeSubNetwork(sNet *subNetwork) ([]byte, error) {
|
||||||
|
serializedSNet := bytes.NewBuffer(make([]byte, 0, 40))
|
||||||
|
|
||||||
|
// Write the tx hash
|
||||||
|
err := binary.Write(serializedSNet, byteOrder, sNet.txHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to serialize sub-network for tx '%s': %s", sNet.txHash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the gas limit
|
||||||
|
err = binary.Write(serializedSNet, byteOrder, sNet.gasLimit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to serialize sub-network for tx '%s': %s", sNet.txHash, 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 tx hash
|
||||||
|
var txHash daghash.Hash
|
||||||
|
err := binary.Read(serializedSNet, byteOrder, &txHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize sub-network: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the gas limit
|
||||||
|
var gasLimit uint64
|
||||||
|
err = binary.Read(serializedSNet, byteOrder, &gasLimit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize sub-network: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &subNetwork{
|
||||||
|
txHash: txHash,
|
||||||
|
gasLimit: gasLimit,
|
||||||
|
}, nil
|
||||||
|
}
|
220
blockdag/subnetworks_test.go
Normal file
220
blockdag/subnetworks_test.go
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
package blockdag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"github.com/daglabs/btcd/dagconfig"
|
||||||
|
"github.com/daglabs/btcd/util"
|
||||||
|
"github.com/daglabs/btcd/wire"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestSubNetworkRegistry tests the full sub-network registry flow. In this test:
|
||||||
|
// 1. We create a sub-network registry transaction and add its block to the DAG
|
||||||
|
// 2. Add 2*finalityInterval so that the sub-network registry transaction becomes final
|
||||||
|
// 3. Attempt to retrieve the gas limit of the now-registered sub-network
|
||||||
|
func TestSubNetworkRegistry(t *testing.T) {
|
||||||
|
params := dagconfig.SimNetParams
|
||||||
|
params.K = 1
|
||||||
|
dag, teardownFunc, err := DAGSetup("TestFinality", Config{
|
||||||
|
DAGParams: ¶ms,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to setup DAG instance: %v", err)
|
||||||
|
}
|
||||||
|
defer teardownFunc()
|
||||||
|
blockTime := time.Unix(dag.genesis.timestamp, 0)
|
||||||
|
extraNonce := int64(0)
|
||||||
|
|
||||||
|
buildNextBlock := func(parents blockSet, txs []*wire.MsgTx) (*util.Block, error) {
|
||||||
|
// We need to change the blockTime to keep all block hashes unique
|
||||||
|
blockTime = blockTime.Add(time.Second)
|
||||||
|
|
||||||
|
// We need to change the extraNonce to keep coinbase hashes unique
|
||||||
|
extraNonce++
|
||||||
|
|
||||||
|
bh := &wire.BlockHeader{
|
||||||
|
Version: 1,
|
||||||
|
Bits: dag.genesis.bits,
|
||||||
|
ParentHashes: parents.hashes(),
|
||||||
|
Timestamp: blockTime,
|
||||||
|
}
|
||||||
|
msgBlock := wire.NewMsgBlock(bh)
|
||||||
|
blockHeight := parents.maxHeight() + 1
|
||||||
|
coinbaseTx, err := createCoinbaseTx(blockHeight, 1, extraNonce, dag.dagParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_ = msgBlock.AddTransaction(coinbaseTx)
|
||||||
|
|
||||||
|
for _, tx := range txs {
|
||||||
|
_ = msgBlock.AddTransaction(tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.NewBlock(msgBlock), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
addBlockToDAG := func(block *util.Block) (*blockNode, error) {
|
||||||
|
dag.dagLock.Lock()
|
||||||
|
defer dag.dagLock.Unlock()
|
||||||
|
|
||||||
|
err = dag.maybeAcceptBlock(block, BFNone)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dag.index.LookupNode(block.Hash()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode := dag.genesis
|
||||||
|
|
||||||
|
// Create a block with a valid sub-network registry transaction
|
||||||
|
gasLimit := uint64(12345)
|
||||||
|
registryTx := wire.NewMsgTx(wire.TxVersion)
|
||||||
|
registryTx.SubNetworkID = wire.SubNetworkRegistry
|
||||||
|
registryTx.Payload = make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(registryTx.Payload, gasLimit)
|
||||||
|
|
||||||
|
// Add it to the DAG
|
||||||
|
registryBlock, err := buildNextBlock(setFromSlice(currentNode), []*wire.MsgTx{registryTx})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not build registry block: %s", err)
|
||||||
|
}
|
||||||
|
currentNode, err = addBlockToDAG(registryBlock)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not add registry block to DAG: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add 2*finalityInterval to ensure the registry transaction is finalized
|
||||||
|
for currentNode.blueScore < 2*finalityInterval {
|
||||||
|
nextBlock, err := buildNextBlock(setFromSlice(currentNode), []*wire.MsgTx{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not create block: %s", err)
|
||||||
|
}
|
||||||
|
currentNode, err = addBlockToDAG(nextBlock)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not add block to DAG: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that the sub-network had been successfully registered by trying
|
||||||
|
// to retrieve its gas limit.
|
||||||
|
mostRecentlyRegisteredSubNetworkID := dag.lastSubNetworkID - 1
|
||||||
|
limit, err := dag.GasLimit(mostRecentlyRegisteredSubNetworkID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not retrieve gas limit: %s", err)
|
||||||
|
}
|
||||||
|
if limit != gasLimit {
|
||||||
|
t.Fatalf("unexpected gas limit. want: %d, got: %d", gasLimit, limit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeSubNetworkRegistryTxs(t *testing.T) {
|
||||||
|
payload1 := make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(payload1, uint64(100))
|
||||||
|
tx1 := wire.MsgTx{
|
||||||
|
Version: 1,
|
||||||
|
SubNetworkID: wire.SubNetworkRegistry,
|
||||||
|
Payload: payload1,
|
||||||
|
TxIn: []*wire.TxIn{
|
||||||
|
{
|
||||||
|
PreviousOutPoint: wire.OutPoint{
|
||||||
|
Hash: *newHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"),
|
||||||
|
Index: 0,
|
||||||
|
},
|
||||||
|
SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"),
|
||||||
|
Sequence: math.MaxUint64,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TxOut: []*wire.TxOut{{
|
||||||
|
Value: 1000000000,
|
||||||
|
PkScript: hexToBytes("4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac"),
|
||||||
|
}, {
|
||||||
|
Value: 4000000000,
|
||||||
|
PkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
payload2 := make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(payload2, uint64(200))
|
||||||
|
tx2 := wire.MsgTx{
|
||||||
|
Version: 1,
|
||||||
|
SubNetworkID: wire.SubNetworkRegistry,
|
||||||
|
Payload: payload2,
|
||||||
|
TxIn: []*wire.TxIn{{
|
||||||
|
PreviousOutPoint: wire.OutPoint{
|
||||||
|
Hash: *newHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"),
|
||||||
|
Index: 0,
|
||||||
|
},
|
||||||
|
SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"),
|
||||||
|
Sequence: math.MaxUint64,
|
||||||
|
}},
|
||||||
|
TxOut: []*wire.TxOut{{
|
||||||
|
Value: 5000000,
|
||||||
|
PkScript: hexToBytes("76a914f419b8db4ba65f3b6fcc233acb762ca6f51c23d488ac"),
|
||||||
|
}, {
|
||||||
|
Value: 34400000000,
|
||||||
|
PkScript: hexToBytes("76a914cadf4fc336ab3c6a4610b75f31ba0676b7f663d288ac"),
|
||||||
|
}},
|
||||||
|
LockTime: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
txs []*wire.MsgTx
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty slice",
|
||||||
|
txs: []*wire.MsgTx{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one transaction",
|
||||||
|
txs: []*wire.MsgTx{&tx1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "two transactions",
|
||||||
|
txs: []*wire.MsgTx{&tx2, &tx1},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
serializedTxs, err := serializeSubNetworkRegistryTxs(test.txs)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("serialization in test '%s' unexpectedly failed: %s", test.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializedTxs, err := deserializeSubNetworkRegistryTxs(serializedTxs)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("deserialization in test '%s' unexpectedly failed: %s", test.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(test.txs, deserializedTxs) {
|
||||||
|
t.Errorf("original txs and deserialized txs are not equal in test '%s'", test.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeSubNetwork(t *testing.T) {
|
||||||
|
sNet := &subNetwork{
|
||||||
|
txHash: *newHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"),
|
||||||
|
gasLimit: 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedSNet, err := serializeSubNetwork(sNet)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("sub-network serialization unexpectedly failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializedSNet, err := deserializeSubNetwork(serializedSNet)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("sub-network deserialization unexpectedly failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(sNet, deserializedSNet) {
|
||||||
|
t.Errorf("original sub-network and deserialized sub-network are not equal")
|
||||||
|
}
|
||||||
|
}
|
@ -751,7 +751,7 @@ func loadConfig() (*Config, []string, error) {
|
|||||||
cfg.MiningAddrs = append(cfg.MiningAddrs, addr)
|
cfg.MiningAddrs = append(cfg.MiningAddrs, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.SubNetwork >= wire.SubNetworkReservedFirst && cfg.SubNetwork <= wire.SubNetworkReservedLast {
|
if cfg.SubNetwork >= wire.SubNetworkReservedFirst && cfg.SubNetwork < wire.SubNetworkUnreservedFirst {
|
||||||
str := "%s: subnetworkID %d is reserved for future use, and nodes can not run in it "
|
str := "%s: subnetworkID %d is reserved for future use, and nodes can not run in it "
|
||||||
err := fmt.Errorf(str, funcName, cfg.SubNetwork)
|
err := fmt.Errorf(str, funcName, cfg.SubNetwork)
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
@ -83,7 +83,7 @@ func TestConstants(t *testing.T) {
|
|||||||
t.Errorf("wire.SubNetworkReservedFirst value was changed from 0, therefore you probably need to update the help text for SubNetwork")
|
t.Errorf("wire.SubNetworkReservedFirst value was changed from 0, therefore you probably need to update the help text for SubNetwork")
|
||||||
}
|
}
|
||||||
|
|
||||||
if wire.SubNetworkReservedLast != 255 {
|
if wire.SubNetworkUnreservedFirst != 256 {
|
||||||
t.Errorf("wire.SubNetworkReservedLast value was changed from 0, therefore you probably need to update the help text for SubNetwork")
|
t.Errorf("wire.SubNetworkUnreservedFirst value was changed from 0, therefore you probably need to update the help text for SubNetwork")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,8 +109,9 @@ const (
|
|||||||
// and are currently un-assigned for anything
|
// and are currently un-assigned for anything
|
||||||
// SubNetworkReservedFirst is the first reserved sub-network
|
// SubNetworkReservedFirst is the first reserved sub-network
|
||||||
SubNetworkReservedFirst = 3
|
SubNetworkReservedFirst = 3
|
||||||
// SubNetworkReservedLast is the last reserved sub-network
|
|
||||||
SubNetworkReservedLast = 255
|
// SubNetworkUnreservedFirst is the first unreserved sub-network
|
||||||
|
SubNetworkUnreservedFirst = 256
|
||||||
)
|
)
|
||||||
|
|
||||||
// scriptFreeList defines a free list of byte slices (up to the maximum number
|
// scriptFreeList defines a free list of byte slices (up to the maximum number
|
||||||
|
Loading…
x
Reference in New Issue
Block a user