diff --git a/app/appmessage/p2p_msgtx_test.go b/app/appmessage/p2p_msgtx_test.go index a75d6228a..52bf5618d 100644 --- a/app/appmessage/p2p_msgtx_test.go +++ b/app/appmessage/p2p_msgtx_test.go @@ -133,8 +133,8 @@ func TestTx(t *testing.T) { // TestTxHash tests the ability to generate the hash of a transaction accurately. func TestTxHashAndID(t *testing.T) { - txHash1Str := "93663e597f6c968d32d229002f76408edf30d6a0151ff679fc729812d8cb2acc" - txID1Str := "24079c6d2bdf602fc389cc307349054937744a9c8dc0f07c023e6af0e949a4e7" + txHash1Str := "b06f8b650115b5cf4d59499e10764a9312742930cb43c9b4ff6495d76f332ed7" + txID1Str := "e20225c3d065ee41743607ee627db44d01ef396dc9779b05b2caf55bac50e12d" wantTxID1, err := transactionid.FromString(txID1Str) if err != nil { t.Fatalf("NewTxIDFromStr: %v", err) @@ -185,7 +185,7 @@ func TestTxHashAndID(t *testing.T) { spew.Sprint(tx1ID), spew.Sprint(wantTxID1)) } - hash2Str := "8dafd1bec24527d8e3b443ceb0a3b92fffc0d60026317f890b2faf5e9afc177a" + hash2Str := "fa16a8ce88d52ca1ff45187bbba0d33044e9f5fe309e8d0b22d4812dcf1782b7" wantHash2, err := externalapi.NewDomainHashFromString(hash2Str) if err != nil { t.Errorf("NewTxIDFromStr: %v", err) diff --git a/app/protocol/flows/v5/blockrelay/ibd_with_headers_proof.go b/app/protocol/flows/v5/blockrelay/ibd_with_headers_proof.go index b53589116..2be9bb341 100644 --- a/app/protocol/flows/v5/blockrelay/ibd_with_headers_proof.go +++ b/app/protocol/flows/v5/blockrelay/ibd_with_headers_proof.go @@ -25,7 +25,7 @@ func (flow *handleIBDFlow) ibdWithHeadersProof( return err } - log.Infof("IBD with pruning proof from %s was unsuccessful. Deleting the staging consensus.", flow.peer) + log.Infof("IBD with pruning proof from %s was unsuccessful. Deleting the staging consensus. (%s)", flow.peer, err) deleteStagingConsensusErr := flow.Domain().DeleteStagingConsensus() if deleteStagingConsensusErr != nil { return deleteStagingConsensusErr diff --git a/app/rpc/rpccontext/verbosedata.go b/app/rpc/rpccontext/verbosedata.go index 9bfa5c81e..e42d3ed8d 100644 --- a/app/rpc/rpccontext/verbosedata.go +++ b/app/rpc/rpccontext/verbosedata.go @@ -122,6 +122,7 @@ func (ctx *Context) PopulateTransactionWithVerboseData( } ctx.Domain.Consensus().PopulateMass(domainTransaction) + transaction.VerboseData = &appmessage.RPCTransactionVerboseData{ TransactionID: consensushashing.TransactionID(domainTransaction).String(), Hash: consensushashing.TransactionHash(domainTransaction).String(), diff --git a/cmd/kaspawallet/create.go b/cmd/kaspawallet/create.go index 2773fc9f1..69268a6ed 100644 --- a/cmd/kaspawallet/create.go +++ b/cmd/kaspawallet/create.go @@ -78,6 +78,11 @@ func create(conf *createConfig) error { return err } + err = file.TryLock() + if err != nil { + return err + } + err = file.Save() if err != nil { return err diff --git a/cmd/kaspawallet/daemon/server/server.go b/cmd/kaspawallet/daemon/server/server.go index 0640cff4e..55d9ce74a 100644 --- a/cmd/kaspawallet/daemon/server/server.go +++ b/cmd/kaspawallet/daemon/server/server.go @@ -77,6 +77,11 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri return (errors.Wrapf(err, "Error reading keys file %s", keysFilePath)) } + err = keysFile.TryLock() + if err != nil { + return err + } + serverInstance := &server{ rpcClient: rpcClient, params: params, diff --git a/cmd/kaspawallet/keys/keys.go b/cmd/kaspawallet/keys/keys.go index dceb9f7be..78676416d 100644 --- a/cmd/kaspawallet/keys/keys.go +++ b/cmd/kaspawallet/keys/keys.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/gofrs/flock" "os" "path/filepath" "runtime" @@ -400,3 +401,28 @@ func decryptMnemonic(numThreads uint8, encryptedPrivateKey *EncryptedMnemonic, p return string(decrypted), nil } + +// flockMap is a map that holds all lock file handlers. This map guarantees that +// the associated locked file handler will never get cleaned by the GC, because +// once they are cleaned the associated file will be unlocked. +var flockMap = make(map[string]*flock.Flock) + +// TryLock tries to acquire an exclusive lock for the file. +func (d *File) TryLock() error { + if _, ok := flockMap[d.path]; ok { + return errors.Errorf("file %s is already locked", d.path) + } + + lockFile := flock.New(d.path + ".lock") + flockMap[d.path] = lockFile + + success, err := lockFile.TryLock() + if err != nil { + return err + } + + if !success { + return errors.Errorf("%s is locked and cannot be used. Make sure that no other active wallet command is using it.", d.path) + } + return nil +} diff --git a/cmd/kaspawallet/send.go b/cmd/kaspawallet/send.go index c251f984f..2385d7397 100644 --- a/cmd/kaspawallet/send.go +++ b/cmd/kaspawallet/send.go @@ -3,6 +3,8 @@ package main import ( "context" "fmt" + "os" + "strings" "github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/client" "github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb" @@ -49,6 +51,10 @@ func send(conf *sendConfig) error { } mnemonics, err := keysFile.DecryptMnemonics(conf.Password) if err != nil { + if strings.Contains(err.Error(), "message authentication failed") { + fmt.Fprintf(os.Stderr, "Password decryption failed. Sometimes this is a result of not "+ + "specifying the same keys file used by the wallet daemon process.\n") + } return err } diff --git a/domain/consensus/factory.go b/domain/consensus/factory.go index 712d43cf8..5d75ee08f 100644 --- a/domain/consensus/factory.go +++ b/domain/consensus/factory.go @@ -216,6 +216,8 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas transactionValidator := transactionvalidator.New(config.BlockCoinbaseMaturity, config.EnableNonNativeSubnetworks, config.MaxCoinbasePayloadLength, + config.K, + config.CoinbasePayloadScriptPublicKeyMaxLength, dbManager, pastMedianTimeManager, ghostdagDataStore, @@ -237,12 +239,14 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas config.GenesisBlock.Header.Bits()) coinbaseManager := coinbasemanager.New( dbManager, + config.SubsidyGenesisReward, config.PreDeflationaryPhaseBaseSubsidy, config.CoinbasePayloadScriptPublicKeyMaxLength, config.GenesisHash, config.DeflationaryPhaseDaaScore, config.DeflationaryPhaseBaseSubsidy, + dagTraversalManager, ghostdagDataStore, acceptanceDataStore, diff --git a/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go b/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go index 56394a0b0..15d60206d 100644 --- a/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go +++ b/domain/consensus/processes/blockvalidator/block_body_in_isolation_test.go @@ -169,10 +169,10 @@ var unOrderedParentsBlock = externalapi.DomainBlock{ }), }}, externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{ - 0x31, 0x33, 0x37, 0x72, 0x5c, 0xde, 0x1c, 0xdf, - 0xf5, 0x9f, 0xde, 0x16, 0x74, 0xbf, 0x0c, 0x64, - 0x37, 0x40, 0x49, 0xdf, 0x02, 0x05, 0xca, 0x6d, - 0x52, 0x23, 0x6f, 0xc2, 0x2b, 0xec, 0xad, 0x42, + 0x7e, 0xe2, 0x10, 0x4e, 0x21, 0x2f, 0x2a, 0xb1, + 0x7d, 0x22, 0xf5, 0xe8, 0xa0, 0x98, 0xef, 0x53, + 0x83, 0xae, 0x59, 0x1f, 0x83, 0xf3, 0x78, 0x5d, + 0x30, 0xae, 0x3e, 0xb3, 0x06, 0x08, 0x6f, 0x79, }), externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{ 0x80, 0xf7, 0x00, 0xe3, 0x16, 0x3d, 0x04, 0x95, @@ -202,20 +202,7 @@ var unOrderedParentsBlock = externalapi.DomainBlock{ Transactions: []*externalapi.DomainTransaction{ { Version: 0, - Inputs: []*externalapi.DomainTransactionInput{ - { - PreviousOutpoint: externalapi.DomainOutpoint{ - TransactionID: *externalapi.NewDomainTransactionIDFromByteArray(&[externalapi.DomainHashSize]byte{}), - Index: 0xffffffff, - }, - SignatureScript: []byte{ - 0x02, 0x10, 0x27, 0x08, 0xac, 0x29, 0x2f, 0x2f, - 0xcf, 0x70, 0xb0, 0x7e, 0x0b, 0x2f, 0x50, 0x32, - 0x53, 0x48, 0x2f, 0x62, 0x74, 0x63, 0x64, 0x2f, - }, - Sequence: math.MaxUint64, - }, - }, + Inputs: nil, Outputs: []*externalapi.DomainTransactionOutput{ { Value: 0x12a05f200, // 5000000000 @@ -446,10 +433,10 @@ var exampleValidBlock = externalapi.DomainBlock{ }), }}, externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{ - 0x86, 0x8b, 0x73, 0xcd, 0x20, 0x51, 0x23, 0x60, - 0xea, 0x62, 0x99, 0x9b, 0x87, 0xf6, 0xdd, 0x8d, - 0xa4, 0x0b, 0xd7, 0xcf, 0xc6, 0x32, 0x38, 0xee, - 0xd9, 0x68, 0x72, 0x1f, 0xa2, 0x51, 0xe4, 0x28, + 0x46, 0xec, 0xf4, 0x5b, 0xe3, 0xba, 0xca, 0x34, + 0x9d, 0xfe, 0x8a, 0x78, 0xde, 0xaf, 0x05, 0x3b, + 0x0a, 0xa6, 0xd5, 0x38, 0x97, 0x4d, 0xa5, 0x0f, + 0xd6, 0xef, 0xb4, 0xd2, 0x66, 0xbc, 0x8d, 0x21, }), externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{ 0x8a, 0xb7, 0xd6, 0x73, 0x1b, 0xe6, 0xc5, 0xd3, @@ -474,21 +461,7 @@ var exampleValidBlock = externalapi.DomainBlock{ Transactions: []*externalapi.DomainTransaction{ { Version: 0, - Inputs: []*externalapi.DomainTransactionInput{ - { - PreviousOutpoint: externalapi.DomainOutpoint{ - TransactionID: *externalapi.NewDomainTransactionIDFromByteArray(&[externalapi.DomainHashSize]byte{ - 0x9b, 0x22, 0x59, 0x44, 0x66, 0xf0, 0xbe, 0x50, - 0x7c, 0x1c, 0x8a, 0xf6, 0x06, 0x27, 0xe6, 0x33, - 0x38, 0x7e, 0xd1, 0xd5, 0x8c, 0x42, 0x59, 0x1a, - 0x31, 0xac, 0x9a, 0xa6, 0x2e, 0xd5, 0x2b, 0x0f, - }), - Index: 0xffffffff, - }, - SignatureScript: nil, - Sequence: math.MaxUint64, - }, - }, + Inputs: nil, Outputs: []*externalapi.DomainTransactionOutput{ { Value: 0x12a05f200, // 5000000000 @@ -751,10 +724,10 @@ var blockWithWrongTxOrder = externalapi.DomainBlock{ }), }}, externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{ - 0x7b, 0x25, 0x8b, 0xfa, 0xfb, 0x49, 0xe4, 0x94, - 0x48, 0x2c, 0xf9, 0x74, 0xdd, 0xad, 0x9d, 0x6f, - 0x98, 0x8f, 0xfb, 0x01, 0x9d, 0x49, 0x29, 0xbe, - 0x3c, 0xec, 0x90, 0xfe, 0xa5, 0x0c, 0xaf, 0x6b, + 0xd5, 0xd2, 0x32, 0xe4, 0xbe, 0x9c, 0x33, 0xbd, + 0xf1, 0x0a, 0xd2, 0x9d, 0x0c, 0xbd, 0xe5, 0xae, + 0xcb, 0x1a, 0xf9, 0x5a, 0x3e, 0xfb, 0xf3, 0xc7, + 0x2b, 0x4d, 0x10, 0xa6, 0xbd, 0x5f, 0x07, 0xe7, }), externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{ 0xa0, 0x69, 0x2d, 0x16, 0xb5, 0xd7, 0xe4, 0xf3, diff --git a/domain/consensus/processes/coinbasemanager/coinbasemanager_external_test.go b/domain/consensus/processes/coinbasemanager/coinbasemanager_external_test.go new file mode 100644 index 000000000..a446ba5d6 --- /dev/null +++ b/domain/consensus/processes/coinbasemanager/coinbasemanager_external_test.go @@ -0,0 +1,57 @@ +package coinbasemanager_test + +import ( + "github.com/kaspanet/kaspad/domain/consensus" + "github.com/kaspanet/kaspad/domain/consensus/model" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/kaspanet/kaspad/domain/consensus/utils/testutils" + "testing" +) + +func TestExtractCoinbaseDataBlueScoreAndSubsidy(t *testing.T) { + testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { + factory := consensus.NewFactory() + tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestBlockStatus") + if err != nil { + t.Fatalf("Error setting up consensus: %+v", err) + } + defer teardown(false) + + tests := []struct { + name string + scriptPublicKeyVersion uint16 + }{ + { + name: "below 255", + scriptPublicKeyVersion: 100, + }, + { + name: "above 255", + scriptPublicKeyVersion: 300, + }, + } + + for _, test := range tests { + coinbaseTx, _, err := tc.CoinbaseManager().ExpectedCoinbaseTransaction(model.NewStagingArea(), model.VirtualBlockHash, &externalapi.DomainCoinbaseData{ + ScriptPublicKey: &externalapi.ScriptPublicKey{ + Script: nil, + Version: test.scriptPublicKeyVersion, + }, + ExtraData: nil, + }) + if err != nil { + t.Fatal(err) + } + + _, cbData, _, err := tc.CoinbaseManager().ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx) + if err != nil { + t.Fatal(err) + } + + if cbData.ScriptPublicKey.Version != test.scriptPublicKeyVersion { + t.Fatalf("test %s post HF expected %d but got %d", test.name, test.scriptPublicKeyVersion, cbData.ScriptPublicKey.Version) + } + } + + }) +} diff --git a/domain/consensus/processes/coinbasemanager/payload.go b/domain/consensus/processes/coinbasemanager/payload.go index 120a55147..ff82e5c69 100644 --- a/domain/consensus/processes/coinbasemanager/payload.go +++ b/domain/consensus/processes/coinbasemanager/payload.go @@ -28,7 +28,7 @@ func (c *coinbaseManager) serializeCoinbasePayload(blueScore uint64, binary.LittleEndian.PutUint64(payload[:uint64Len], blueScore) binary.LittleEndian.PutUint64(payload[uint64Len:], subsidy) - payload[uint64Len+lengthOfSubsidy] = uint8(coinbaseData.ScriptPublicKey.Version) + binary.LittleEndian.PutUint16(payload[uint64Len+lengthOfSubsidy:], coinbaseData.ScriptPublicKey.Version) payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey] = uint8(len(coinbaseData.ScriptPublicKey.Script)) copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength:], coinbaseData.ScriptPublicKey.Script) copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength+scriptLengthOfScriptPubKey:], coinbaseData.ExtraData) @@ -52,7 +52,7 @@ func ModifyCoinbasePayload(payload []byte, coinbaseData *externalapi.DomainCoinb payload = newPayload } - payload[uint64Len+lengthOfSubsidy] = uint8(coinbaseData.ScriptPublicKey.Version) + binary.LittleEndian.PutUint16(payload[uint64Len+lengthOfSubsidy:uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey], coinbaseData.ScriptPublicKey.Version) payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey] = uint8(len(coinbaseData.ScriptPublicKey.Script)) copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength:], coinbaseData.ScriptPublicKey.Script) copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength+scriptLengthOfScriptPubKey:], coinbaseData.ExtraData) @@ -73,7 +73,8 @@ func (c *coinbaseManager) ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx *ext blueScore = binary.LittleEndian.Uint64(coinbaseTx.Payload[:uint64Len]) subsidy = binary.LittleEndian.Uint64(coinbaseTx.Payload[uint64Len:]) - scriptPubKeyVersion := uint16(coinbaseTx.Payload[uint64Len+lengthOfSubsidy]) + scriptPubKeyVersion := binary.LittleEndian.Uint16(coinbaseTx.Payload[uint64Len+lengthOfSubsidy : uint64Len+lengthOfSubsidy+uint16Len]) + scriptPubKeyScriptLength := coinbaseTx.Payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey] if scriptPubKeyScriptLength > c.coinbasePayloadScriptPublicKeyMaxLength { diff --git a/domain/consensus/processes/consensusstatemanager/consensus_state_manager.go b/domain/consensus/processes/consensusstatemanager/consensus_state_manager.go index 8f4514392..08fbb37bb 100644 --- a/domain/consensus/processes/consensusstatemanager/consensus_state_manager.go +++ b/domain/consensus/processes/consensusstatemanager/consensus_state_manager.go @@ -72,7 +72,8 @@ func New( maxBlockParents: maxBlockParents, mergeSetSizeLimit: mergeSetSizeLimit, genesisHash: genesisHash, - databaseContext: databaseContext, + + databaseContext: databaseContext, ghostdagManager: ghostdagManager, dagTopologyManager: dagTopologyManager, diff --git a/domain/consensus/processes/transactionvalidator/transaction_in_context.go b/domain/consensus/processes/transactionvalidator/transaction_in_context.go index 159c2e74c..88f6cf076 100644 --- a/domain/consensus/processes/transactionvalidator/transaction_in_context.go +++ b/domain/consensus/processes/transactionvalidator/transaction_in_context.go @@ -350,6 +350,7 @@ func (v *transactionValidator) validateTransactionSigOpCounts(tx *externalapi.Do sigScript := input.SignatureScript isP2SH := txscript.IsPayToScriptHash(utxoEntry.ScriptPublicKey()) sigOpCount := txscript.GetPreciseSigOpCount(sigScript, utxoEntry.ScriptPublicKey(), isP2SH) + if sigOpCount != int(input.SigOpCount) { return errors.Wrapf(ruleerrors.ErrWrongSigOpCount, "input %d specifies SigOpCount %d while actual SigOpCount is %d", diff --git a/domain/consensus/processes/transactionvalidator/transaction_in_isolation.go b/domain/consensus/processes/transactionvalidator/transaction_in_isolation.go index ef04f9b0b..699374aab 100644 --- a/domain/consensus/processes/transactionvalidator/transaction_in_isolation.go +++ b/domain/consensus/processes/transactionvalidator/transaction_in_isolation.go @@ -23,7 +23,7 @@ func (v *transactionValidator) ValidateTransactionInIsolation(tx *externalapi.Do if err != nil { return err } - err = v.checkCoinbaseLength(tx) + err = v.checkCoinbaseInIsolation(tx) if err != nil { return err } @@ -114,7 +114,7 @@ func (v *transactionValidator) checkDuplicateTransactionInputs(tx *externalapi.D return nil } -func (v *transactionValidator) checkCoinbaseLength(tx *externalapi.DomainTransaction) error { +func (v *transactionValidator) checkCoinbaseInIsolation(tx *externalapi.DomainTransaction) error { if !transactionhelper.IsCoinBase(tx) { return nil } @@ -127,6 +127,22 @@ func (v *transactionValidator) checkCoinbaseLength(tx *externalapi.DomainTransac payloadLen, v.maxCoinbasePayloadLength) } + if len(tx.Inputs) != 0 { + return errors.Wrap(ruleerrors.ErrCoinbaseWithInputs, "coinbase has inputs") + } + + outputsLimit := uint64(v.ghostdagK) + 2 + if uint64(len(tx.Outputs)) > outputsLimit { + return errors.Wrapf(ruleerrors.ErrCoinbaseTooManyOutputs, "coinbase has too many outputs: got %d where the limit is %d", len(tx.Outputs), outputsLimit) + } + + for i, output := range tx.Outputs { + if len(output.ScriptPublicKey.Script) > int(v.coinbasePayloadScriptPublicKeyMaxLength) { + return errors.Wrapf(ruleerrors.ErrCoinbaseTooLongScriptPublicKey, "coinbase output %d has a too long script public key", i) + + } + } + return nil } diff --git a/domain/consensus/processes/transactionvalidator/transaction_in_isolation_test.go b/domain/consensus/processes/transactionvalidator/transaction_in_isolation_test.go index e95f714c2..b9acce545 100644 --- a/domain/consensus/processes/transactionvalidator/transaction_in_isolation_test.go +++ b/domain/consensus/processes/transactionvalidator/transaction_in_isolation_test.go @@ -76,7 +76,7 @@ func TestValidateTransactionInIsolationAndPopulateMass(t *testing.T) { subnetworks.SubnetworkIDNative, &txSubnetworkData{subnetworks.SubnetworkIDCoinbase, 0, nil}, nil, - nil, 0}, + ruleerrors.ErrCoinbaseWithInputs, 0}, {"no inputs coinbase", 0, 1, diff --git a/domain/consensus/processes/transactionvalidator/transactionvalidator.go b/domain/consensus/processes/transactionvalidator/transactionvalidator.go index 73621e756..818e88e9b 100644 --- a/domain/consensus/processes/transactionvalidator/transactionvalidator.go +++ b/domain/consensus/processes/transactionvalidator/transactionvalidator.go @@ -2,6 +2,7 @@ package transactionvalidator import ( "github.com/kaspanet/kaspad/domain/consensus/model" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/txscript" "github.com/kaspanet/kaspad/util/txmass" ) @@ -11,22 +12,26 @@ const sigCacheSize = 10_000 // transactionValidator exposes a set of validation classes, after which // it's possible to determine whether either a transaction is valid type transactionValidator struct { - blockCoinbaseMaturity uint64 - databaseContext model.DBReader - pastMedianTimeManager model.PastMedianTimeManager - ghostdagDataStore model.GHOSTDAGDataStore - daaBlocksStore model.DAABlocksStore - enableNonNativeSubnetworks bool - maxCoinbasePayloadLength uint64 - sigCache *txscript.SigCache - sigCacheECDSA *txscript.SigCacheECDSA - txMassCalculator *txmass.Calculator + blockCoinbaseMaturity uint64 + databaseContext model.DBReader + pastMedianTimeManager model.PastMedianTimeManager + ghostdagDataStore model.GHOSTDAGDataStore + daaBlocksStore model.DAABlocksStore + enableNonNativeSubnetworks bool + maxCoinbasePayloadLength uint64 + ghostdagK externalapi.KType + coinbasePayloadScriptPublicKeyMaxLength uint8 + sigCache *txscript.SigCache + sigCacheECDSA *txscript.SigCacheECDSA + txMassCalculator *txmass.Calculator } // New instantiates a new TransactionValidator func New(blockCoinbaseMaturity uint64, enableNonNativeSubnetworks bool, maxCoinbasePayloadLength uint64, + ghostdagK externalapi.KType, + coinbasePayloadScriptPublicKeyMaxLength uint8, databaseContext model.DBReader, pastMedianTimeManager model.PastMedianTimeManager, ghostdagDataStore model.GHOSTDAGDataStore, @@ -34,15 +39,17 @@ func New(blockCoinbaseMaturity uint64, txMassCalculator *txmass.Calculator) model.TransactionValidator { return &transactionValidator{ - blockCoinbaseMaturity: blockCoinbaseMaturity, - enableNonNativeSubnetworks: enableNonNativeSubnetworks, - maxCoinbasePayloadLength: maxCoinbasePayloadLength, - databaseContext: databaseContext, - pastMedianTimeManager: pastMedianTimeManager, - ghostdagDataStore: ghostdagDataStore, - daaBlocksStore: daaBlocksStore, - sigCache: txscript.NewSigCache(sigCacheSize), - sigCacheECDSA: txscript.NewSigCacheECDSA(sigCacheSize), - txMassCalculator: txMassCalculator, + blockCoinbaseMaturity: blockCoinbaseMaturity, + enableNonNativeSubnetworks: enableNonNativeSubnetworks, + maxCoinbasePayloadLength: maxCoinbasePayloadLength, + ghostdagK: ghostdagK, + coinbasePayloadScriptPublicKeyMaxLength: coinbasePayloadScriptPublicKeyMaxLength, + databaseContext: databaseContext, + pastMedianTimeManager: pastMedianTimeManager, + ghostdagDataStore: ghostdagDataStore, + daaBlocksStore: daaBlocksStore, + sigCache: txscript.NewSigCache(sigCacheSize), + sigCacheECDSA: txscript.NewSigCacheECDSA(sigCacheSize), + txMassCalculator: txMassCalculator, } } diff --git a/domain/consensus/processes/transactionvalidator/transactionvalidator_test.go b/domain/consensus/processes/transactionvalidator/transactionvalidator_test.go index f5db9a468..f9cdbdbcc 100644 --- a/domain/consensus/processes/transactionvalidator/transactionvalidator_test.go +++ b/domain/consensus/processes/transactionvalidator/transactionvalidator_test.go @@ -109,18 +109,6 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) { 0), } - txInputWithBadSigOpCount := externalapi.DomainTransactionInput{ - PreviousOutpoint: prevOutPoint, - SignatureScript: []byte{}, - Sequence: constants.MaxTxInSequenceNum, - SigOpCount: 2, - UTXOEntry: utxo.NewUTXOEntry( - 100_000_000, // 1 KAS - scriptPublicKey, - true, - uint64(5)), - } - txOutput := externalapi.DomainTransactionOutput{ Value: 100000000, // 1 KAS ScriptPublicKey: scriptPublicKey, @@ -193,13 +181,6 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) { SubnetworkID: subnetworks.SubnetworkIDRegistry, Gas: 0, LockTime: 0} - txWithBadSigOpCount := externalapi.DomainTransaction{ - Version: constants.MaxTransactionVersion, - Inputs: []*externalapi.DomainTransactionInput{&txInputWithBadSigOpCount}, - Outputs: []*externalapi.DomainTransactionOutput{&txOutput}, - SubnetworkID: subnetworks.SubnetworkIDRegistry, - Gas: 0, - LockTime: 0} stagingArea := model.NewStagingArea() @@ -266,13 +247,6 @@ func TestValidateTransactionInContextAndPopulateFee(t *testing.T) { isValid: false, expectedError: ruleerrors.ErrScriptValidation, }, - { // the SigOpCount in the input is wrong, and hence invalid - name: "checkTransactionSigOpCounts", - tx: &txWithBadSigOpCount, - povBlockHash: povBlockHash, - isValid: false, - expectedError: ruleerrors.ErrWrongSigOpCount, - }, } for _, test := range tests { diff --git a/domain/consensus/ruleerrors/rule_error.go b/domain/consensus/ruleerrors/rule_error.go index eb0515997..b3321e9ca 100644 --- a/domain/consensus/ruleerrors/rule_error.go +++ b/domain/consensus/ruleerrors/rule_error.go @@ -241,6 +241,9 @@ var ( ErrPruningProofEmpty = newRuleError("ErrPruningProofEmpty") ErrWrongCoinbaseSubsidy = newRuleError("ErrWrongCoinbaseSubsidy") ErrWrongBlockVersion = newRuleError("ErrWrongBlockVersion") + ErrCoinbaseWithInputs = newRuleError("ErrCoinbaseWithInputs") + ErrCoinbaseTooManyOutputs = newRuleError("ErrCoinbaseTooManyOutputs") + ErrCoinbaseTooLongScriptPublicKey = newRuleError("ErrCoinbaseTooLongScriptPublicKey") ) // RuleError identifies a rule violation. It is used to indicate that diff --git a/domain/consensus/utils/consensushashing/transaction.go b/domain/consensus/utils/consensushashing/transaction.go index a364a7ae6..aadbaafd4 100644 --- a/domain/consensus/utils/consensushashing/transaction.go +++ b/domain/consensus/utils/consensushashing/transaction.go @@ -139,11 +139,19 @@ func writeTransactionInput(w io.Writer, ti *externalapi.DomainTransactionInput, if encodingFlags&txEncodingExcludeSignatureScript != txEncodingExcludeSignatureScript { err = writeVarBytes(w, ti.SignatureScript) + if err != nil { + return err + } + + _, err = w.Write([]byte{ti.SigOpCount}) + if err != nil { + return err + } } else { err = writeVarBytes(w, []byte{}) - } - if err != nil { - return err + if err != nil { + return err + } } return binaryserializer.PutUint64(w, ti.Sequence) diff --git a/domain/dagconfig/params.go b/domain/dagconfig/params.go index 67e20c16f..0340763ef 100644 --- a/domain/dagconfig/params.go +++ b/domain/dagconfig/params.go @@ -297,7 +297,11 @@ var TestnetParams = Params{ Net: appmessage.Testnet, RPCPort: "16210", DefaultPort: "16211", - DNSSeeds: []string{"testnet-10-dnsseed.kas.pa"}, + DNSSeeds: []string{ + "testnet-10-dnsseed.kas.pa", + // This DNS seeder is run by Tiram + "seeder1-testnet.kaspad.net", + }, // DAG parameters GenesisBlock: &testnetGenesisBlock, diff --git a/go.mod b/go.mod index 1f2bda3ca..5cc7e7a6d 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd github.com/btcsuite/winsvc v1.0.0 github.com/davecgh/go-spew v1.1.1 + github.com/gofrs/flock v0.8.1 github.com/golang/protobuf v1.5.2 github.com/jessevdk/go-flags v1.4.0 github.com/jrick/logrotate v1.0.0 @@ -30,6 +31,5 @@ require ( golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect golang.org/x/text v0.3.5 // indirect google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08 // indirect - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect ) \ No newline at end of file diff --git a/go.sum b/go.sum index ed42be015..f391787b8 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -118,8 +120,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea h1:+WiDlPBBaO+h9vPNZi8uJ3k4BkKQB7Iow3aqwHVA5hI= -golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -155,8 +155,6 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -167,12 +165,10 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= diff --git a/infrastructure/network/netadapter/server/grpcserver/connection_loops.go b/infrastructure/network/netadapter/server/grpcserver/connection_loops.go index 63df69ffc..0f5c6359e 100644 --- a/infrastructure/network/netadapter/server/grpcserver/connection_loops.go +++ b/infrastructure/network/netadapter/server/grpcserver/connection_loops.go @@ -2,8 +2,12 @@ package grpcserver import ( "github.com/davecgh/go-spew/spew" + "github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/infrastructure/logger" "io" + "os" + "strconv" + "sync" "time" routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" @@ -25,6 +29,9 @@ func (c *gRPCConnection) connectionLoops() error { return err } +var blockDelayOnce sync.Once +var blockDelay = 0 + func (c *gRPCConnection) sendLoop() error { outgoingRoute := c.router.OutgoingRoute() for c.IsConnected() { @@ -36,6 +43,20 @@ func (c *gRPCConnection) sendLoop() error { return err } + blockDelayOnce.Do(func() { + experimentalDelayEnv := os.Getenv("KASPA_EXPERIMENTAL_DELAY") + if experimentalDelayEnv != "" { + blockDelay, err = strconv.Atoi(experimentalDelayEnv) + if err != nil { + panic(err) + } + } + }) + + if blockDelay != 0 && message.Command() == appmessage.CmdBlock { + time.Sleep(time.Duration(blockDelay) * time.Second) + } + log.Debugf("outgoing '%s' message to %s", message.Command(), c) log.Tracef("outgoing '%s' message to %s: %s", message.Command(), c, logger.NewLogClosure(func() string { return spew.Sdump(message) diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_connected_peer_info.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_connected_peer_info.go index 0ae4abeae..89841678b 100644 --- a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_connected_peer_info.go +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_connected_peer_info.go @@ -35,7 +35,7 @@ func (x *KaspadMessage_GetConnectedPeerInfoResponse) fromAppMessage(message *app TimeOffset: info.TimeOffset, UserAgent: info.UserAgent, AdvertisedProtocolVersion: info.AdvertisedProtocolVersion, - TimeConnected: info.TimeOffset, + TimeConnected: info.TimeConnected, IsIbdPeer: info.IsIBDPeer, } } diff --git a/infrastructure/network/rpcclient/rpcclient.go b/infrastructure/network/rpcclient/rpcclient.go index e4671c028..d6ea54c78 100644 --- a/infrastructure/network/rpcclient/rpcclient.go +++ b/infrastructure/network/rpcclient/rpcclient.go @@ -72,7 +72,7 @@ func (c *RPCClient) connect() error { remoteVersion := getInfoResponse.ServerVersion if localVersion != remoteVersion { - return errors.Errorf("Server version mismatch, expect: %s, got: %s", localVersion, remoteVersion) + log.Warnf("version mismatch, client: %s, server: %s - expected responses and requests may deviate", localVersion, remoteVersion) } return nil diff --git a/version/version.go b/version/version.go index cd27ef67d..8548c4c01 100644 --- a/version/version.go +++ b/version/version.go @@ -11,7 +11,7 @@ const validCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs const ( appMajor uint = 0 appMinor uint = 12 - appPatch uint = 6 + appPatch uint = 8 ) // appBuild is defined as a variable so it can be overridden during the build