diff --git a/changelog.txt b/changelog.txt index 319f42b5e8..55a5303e29 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,16 @@ +Karlsend v1.2.0 - 2024-03-07 +============================ + +* Fixed karlsend and karlsenminer Go traces +* Fixed all stability and integration tests +* Fixed crash in karlsenminer +* Fixed race in gRPC client send/recv/close handler +* Switched cointype in bip39 derivation to 121337 +* Added support for derivation path from Kaspa (wallet v1) and Karlsen (wallet v2) +* Added code of conduct +* Changed karlsenwallet listen port from 8082 to 9182 +* Updated Go modules and dependencies + Karlsend v1.1.0 - 2023-12-25 ============================ diff --git a/cmd/karlsenwallet/config.go b/cmd/karlsenwallet/config.go index 711b77d4e3..ed5d76cde3 100644 --- a/cmd/karlsenwallet/config.go +++ b/cmd/karlsenwallet/config.go @@ -42,6 +42,7 @@ type createConfig struct { NumPublicKeys uint32 `long:"num-public-keys" short:"n" description:"Total number of keys" default:"1"` ECDSA bool `long:"ecdsa" description:"Create an ECDSA wallet"` Import bool `long:"import" short:"i" description:"Import private keys (as opposed to generating them)"` + Legacy bool `long:"legacy" short:"l" description:"Use legacy wallet and derivation path"` config.NetworkFlags } diff --git a/cmd/karlsenwallet/create.go b/cmd/karlsenwallet/create.go index a4031d44ff..ceffad11e4 100644 --- a/cmd/karlsenwallet/create.go +++ b/cmd/karlsenwallet/create.go @@ -17,11 +17,23 @@ func create(conf *createConfig) error { var encryptedMnemonics []*keys.EncryptedMnemonic var signerExtendedPublicKeys []string var err error + var version uint32 isMultisig := conf.NumPublicKeys > 1 + + if !conf.Legacy { + + // Version 2 uses new derivation path. + version = keys.LastVersion + } else { + + // Version 1 uses old derivation path. + version = 1 + } + if !conf.Import { - encryptedMnemonics, signerExtendedPublicKeys, err = keys.CreateMnemonics(conf.NetParams(), conf.NumPrivateKeys, conf.Password, isMultisig) + encryptedMnemonics, signerExtendedPublicKeys, err = keys.CreateMnemonics(conf.NetParams(), conf.NumPrivateKeys, conf.Password, isMultisig, version) } else { - encryptedMnemonics, signerExtendedPublicKeys, err = keys.ImportMnemonics(conf.NetParams(), conf.NumPrivateKeys, conf.Password, isMultisig) + encryptedMnemonics, signerExtendedPublicKeys, err = keys.ImportMnemonics(conf.NetParams(), conf.NumPrivateKeys, conf.Password, isMultisig, version) } if err != nil { return err @@ -65,7 +77,7 @@ func create(conf *createConfig) error { } file := keys.File{ - Version: keys.LastVersion, + Version: version, EncryptedMnemonics: encryptedMnemonics, ExtendedPublicKeys: extendedPublicKeys, MinimumSignatures: conf.MinimumSignatures, diff --git a/cmd/karlsenwallet/daemon/server/server.go b/cmd/karlsenwallet/daemon/server/server.go index 654c78b26b..b7b4126b1f 100644 --- a/cmd/karlsenwallet/daemon/server/server.go +++ b/cmd/karlsenwallet/daemon/server/server.go @@ -82,6 +82,18 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri return err } + // Show wallet version + log.Infof("Wallet version %d found", keysFile.Version) + + // Show warning if old wallet version is used. + if keysFile.Version == 1 { + log.Infof("---") + log.Infof("For future compatibility it is...") + log.Infof("highly recommended to create a...") + log.Infof("new one and transfer KLS to it.") + log.Infof("---") + } + serverInstance := &server{ rpcClient: rpcClient, params: params, diff --git a/cmd/karlsenwallet/daemon/server/sign.go b/cmd/karlsenwallet/daemon/server/sign.go index 254e2710c1..e9b8cf3dfa 100644 --- a/cmd/karlsenwallet/daemon/server/sign.go +++ b/cmd/karlsenwallet/daemon/server/sign.go @@ -26,7 +26,7 @@ func (s *server) signTransactions(unsignedTransactions [][]byte, password string } signedTransactions := make([][]byte, len(unsignedTransactions)) for i, unsignedTransaction := range unsignedTransactions { - signedTransaction, err := libkarlsenwallet.Sign(s.params, mnemonics, unsignedTransaction, s.keysFile.ECDSA) + signedTransaction, err := libkarlsenwallet.Sign(s.params, mnemonics, unsignedTransaction, s.keysFile.ECDSA, s.keysFile.Version) if err != nil { return nil, err } diff --git a/cmd/karlsenwallet/daemon/server/split_transaction_test.go b/cmd/karlsenwallet/daemon/server/split_transaction_test.go index 8958285ab5..fda06e4173 100644 --- a/cmd/karlsenwallet/daemon/server/split_transaction_test.go +++ b/cmd/karlsenwallet/daemon/server/split_transaction_test.go @@ -21,53 +21,55 @@ import ( ) func TestEstimateMassAfterSignatures(t *testing.T) { - testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - unsignedTransactionBytes, mnemonics, params, teardown := testEstimateMassIncreaseForSignaturesSetUp(t, consensusConfig) - defer teardown(false) - - serverInstance := &server{ - params: params, - keysFile: &keys.File{MinimumSignatures: 2}, - shutdown: make(chan struct{}), - addressSet: make(walletAddressSet), - txMassCalculator: txmass.NewCalculator(params.MassPerTxByte, params.MassPerScriptPubKeyByte, params.MassPerSigOp), - } - - unsignedTransaction, err := serialization.DeserializePartiallySignedTransaction(unsignedTransactionBytes) - if err != nil { - t.Fatalf("Error deserializing unsignedTransaction: %s", err) - } - - estimatedMassAfterSignatures, err := serverInstance.estimateMassAfterSignatures(unsignedTransaction) - if err != nil { - t.Fatalf("Error from estimateMassAfterSignatures: %s", err) - } - - signedTxStep1Bytes, err := libkarlsenwallet.Sign(params, mnemonics[:1], unsignedTransactionBytes, false) - if err != nil { - t.Fatalf("Sign: %+v", err) - } - - signedTxStep2Bytes, err := libkarlsenwallet.Sign(params, mnemonics[1:2], signedTxStep1Bytes, false) - if err != nil { - t.Fatalf("Sign: %+v", err) - } - - extractedSignedTx, err := libkarlsenwallet.ExtractTransaction(signedTxStep2Bytes, false) - if err != nil { - t.Fatalf("ExtractTransaction: %+v", err) - } - - actualMassAfterSignatures := serverInstance.txMassCalculator.CalculateTransactionMass(extractedSignedTx) - - if estimatedMassAfterSignatures != actualMassAfterSignatures { - t.Errorf("Estimated mass after signatures: %d but actually got %d", - estimatedMassAfterSignatures, actualMassAfterSignatures) - } + testutils.ForAllPaths(t, func(t *testing.T, version uint32) { + testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { + unsignedTransactionBytes, mnemonics, params, teardown := testEstimateMassIncreaseForSignaturesSetUp(t, consensusConfig, version) + defer teardown(false) + + serverInstance := &server{ + params: params, + keysFile: &keys.File{MinimumSignatures: 2}, + shutdown: make(chan struct{}), + addressSet: make(walletAddressSet), + txMassCalculator: txmass.NewCalculator(params.MassPerTxByte, params.MassPerScriptPubKeyByte, params.MassPerSigOp), + } + + unsignedTransaction, err := serialization.DeserializePartiallySignedTransaction(unsignedTransactionBytes) + if err != nil { + t.Fatalf("Error deserializing unsignedTransaction: %s", err) + } + + estimatedMassAfterSignatures, err := serverInstance.estimateMassAfterSignatures(unsignedTransaction) + if err != nil { + t.Fatalf("Error from estimateMassAfterSignatures: %s", err) + } + + signedTxStep1Bytes, err := libkarlsenwallet.Sign(params, mnemonics[:1], unsignedTransactionBytes, false, version) + if err != nil { + t.Fatalf("Sign: %+v", err) + } + + signedTxStep2Bytes, err := libkarlsenwallet.Sign(params, mnemonics[1:2], signedTxStep1Bytes, false, version) + if err != nil { + t.Fatalf("Sign: %+v", err) + } + + extractedSignedTx, err := libkarlsenwallet.ExtractTransaction(signedTxStep2Bytes, false) + if err != nil { + t.Fatalf("ExtractTransaction: %+v", err) + } + + actualMassAfterSignatures := serverInstance.txMassCalculator.CalculateTransactionMass(extractedSignedTx) + + if estimatedMassAfterSignatures != actualMassAfterSignatures { + t.Errorf("Estimated mass after signatures: %d but actually got %d", + estimatedMassAfterSignatures, actualMassAfterSignatures) + } + }) }) } -func testEstimateMassIncreaseForSignaturesSetUp(t *testing.T, consensusConfig *consensus.Config) ( +func testEstimateMassIncreaseForSignaturesSetUp(t *testing.T, consensusConfig *consensus.Config, version uint32) ( []byte, []string, *dagconfig.Params, func(keepDataDir bool)) { consensusConfig.BlockCoinbaseMaturity = 0 @@ -88,7 +90,7 @@ func testEstimateMassIncreaseForSignaturesSetUp(t *testing.T, consensusConfig *c t.Fatalf("CreateMnemonic: %+v", err) } - publicKeys[i], err = libkarlsenwallet.MasterPublicKeyFromMnemonic(&consensusConfig.Params, mnemonics[i], true) + publicKeys[i], err = libkarlsenwallet.MasterPublicKeyFromMnemonic(&consensusConfig.Params, mnemonics[i], true, version) if err != nil { t.Fatalf("MasterPublicKeyFromMnemonic: %+v", err) } diff --git a/cmd/karlsenwallet/dump_unencrypted_data.go b/cmd/karlsenwallet/dump_unencrypted_data.go index 0477a954cc..f9719eaaf5 100644 --- a/cmd/karlsenwallet/dump_unencrypted_data.go +++ b/cmd/karlsenwallet/dump_unencrypted_data.go @@ -36,7 +36,7 @@ func dumpUnencryptedData(conf *dumpUnencryptedDataConfig) error { mnemonicPublicKeys := make(map[string]struct{}) for i, mnemonic := range mnemonics { fmt.Printf("Mnemonic #%d:\n%s\n\n", i+1, mnemonic) - publicKey, err := libkarlsenwallet.MasterPublicKeyFromMnemonic(conf.NetParams(), mnemonic, len(keysFile.ExtendedPublicKeys) > 1) + publicKey, err := libkarlsenwallet.MasterPublicKeyFromMnemonic(conf.NetParams(), mnemonic, len(keysFile.ExtendedPublicKeys) > 1, keysFile.Version) if err != nil { return err } diff --git a/cmd/karlsenwallet/keys/create.go b/cmd/karlsenwallet/keys/create.go index eb083c841f..1d18d04233 100644 --- a/cmd/karlsenwallet/keys/create.go +++ b/cmd/karlsenwallet/keys/create.go @@ -15,7 +15,7 @@ import ( ) // CreateMnemonics generates `numKeys` number of mnemonics. -func CreateMnemonics(params *dagconfig.Params, numKeys uint32, cmdLinePassword string, isMultisig bool) (encryptedPrivateKeys []*EncryptedMnemonic, extendedPublicKeys []string, err error) { +func CreateMnemonics(params *dagconfig.Params, numKeys uint32, cmdLinePassword string, isMultisig bool, version uint32) (encryptedPrivateKeys []*EncryptedMnemonic, extendedPublicKeys []string, err error) { mnemonics := make([]string, numKeys) for i := uint32(0); i < numKeys; i++ { var err error @@ -25,11 +25,11 @@ func CreateMnemonics(params *dagconfig.Params, numKeys uint32, cmdLinePassword s } } - return encryptedMnemonicExtendedPublicKeyPairs(params, mnemonics, cmdLinePassword, isMultisig) + return encryptedMnemonicExtendedPublicKeyPairs(params, mnemonics, cmdLinePassword, isMultisig, version) } // ImportMnemonics imports a `numKeys` of mnemonics. -func ImportMnemonics(params *dagconfig.Params, numKeys uint32, cmdLinePassword string, isMultisig bool) (encryptedPrivateKeys []*EncryptedMnemonic, extendedPublicKeys []string, err error) { +func ImportMnemonics(params *dagconfig.Params, numKeys uint32, cmdLinePassword string, isMultisig bool, version uint32) (encryptedPrivateKeys []*EncryptedMnemonic, extendedPublicKeys []string, err error) { mnemonics := make([]string, numKeys) for i := uint32(0); i < numKeys; i++ { fmt.Printf("Enter mnemonic #%d here:\n", i+1) @@ -45,10 +45,10 @@ func ImportMnemonics(params *dagconfig.Params, numKeys uint32, cmdLinePassword s mnemonics[i] = string(mnemonic) } - return encryptedMnemonicExtendedPublicKeyPairs(params, mnemonics, cmdLinePassword, isMultisig) + return encryptedMnemonicExtendedPublicKeyPairs(params, mnemonics, cmdLinePassword, isMultisig, version) } -func encryptedMnemonicExtendedPublicKeyPairs(params *dagconfig.Params, mnemonics []string, cmdLinePassword string, isMultisig bool) ( +func encryptedMnemonicExtendedPublicKeyPairs(params *dagconfig.Params, mnemonics []string, cmdLinePassword string, isMultisig bool, version uint32) ( encryptedPrivateKeys []*EncryptedMnemonic, extendedPublicKeys []string, err error) { password := []byte(cmdLinePassword) if len(password) == 0 { @@ -65,7 +65,7 @@ func encryptedMnemonicExtendedPublicKeyPairs(params *dagconfig.Params, mnemonics extendedPublicKeys = make([]string, 0, len(mnemonics)) for _, mnemonic := range mnemonics { - extendedPublicKey, err := libkarlsenwallet.MasterPublicKeyFromMnemonic(params, mnemonic, isMultisig) + extendedPublicKey, err := libkarlsenwallet.MasterPublicKeyFromMnemonic(params, mnemonic, isMultisig, version) if err != nil { return nil, nil, err } diff --git a/cmd/karlsenwallet/keys/keys.go b/cmd/karlsenwallet/keys/keys.go index 73e7ef2214..8ab0015d8a 100644 --- a/cmd/karlsenwallet/keys/keys.go +++ b/cmd/karlsenwallet/keys/keys.go @@ -27,7 +27,7 @@ var ( ) // LastVersion is the most up to date file format version -const LastVersion = 1 +const LastVersion = 2 func defaultKeysFile(netParams *dagconfig.Params) string { return filepath.Join(defaultAppDir, netParams.Name, "keys.json") @@ -95,7 +95,7 @@ func (d *File) toJSON() *keysFileJSON { // NewFileFromMnemonic generates a new File from the given mnemonic string func NewFileFromMnemonic(params *dagconfig.Params, mnemonic string, password string) (*File, error) { encryptedMnemonics, extendedPublicKeys, err := - encryptedMnemonicExtendedPublicKeyPairs(params, []string{mnemonic}, password, false) + encryptedMnemonicExtendedPublicKeyPairs(params, []string{mnemonic}, password, false, LastVersion) if err != nil { return nil, err } diff --git a/cmd/karlsenwallet/libkarlsenwallet/bip39.go b/cmd/karlsenwallet/libkarlsenwallet/bip39.go index 031f8c0932..5e32a5b9fb 100644 --- a/cmd/karlsenwallet/libkarlsenwallet/bip39.go +++ b/cmd/karlsenwallet/libkarlsenwallet/bip39.go @@ -22,22 +22,30 @@ const ( // Note: this is not entirely compatible to BIP 45 since // BIP 45 doesn't have a coin type in its derivation path. MultiSigPurpose = 45 - // TODO: Register the coin type in https://github.com/satoshilabs/slips/blob/master/slip-0044.md + // Registered in https://github.com/satoshilabs/slips/blob/master/slip-0044.md CoinType = 121337 + // Wallet version 1 coin type + CoinTypeV1 = 111111 ) -func defaultPath(isMultisig bool) string { +func defaultPath(isMultisig bool, version uint32) string { purpose := SingleSignerPurpose if isMultisig { purpose = MultiSigPurpose } + // Note: this is needed because initial fork was created + // without changing the coin type in derivation path. + if version == 1 { + return fmt.Sprintf("m/%d'/%d'/0'", purpose, CoinTypeV1) + } + return fmt.Sprintf("m/%d'/%d'/0'", purpose, CoinType) } // MasterPublicKeyFromMnemonic returns the master public key with the correct derivation for the given mnemonic. -func MasterPublicKeyFromMnemonic(params *dagconfig.Params, mnemonic string, isMultisig bool) (string, error) { - path := defaultPath(isMultisig) +func MasterPublicKeyFromMnemonic(params *dagconfig.Params, mnemonic string, isMultisig bool, version uint32) (string, error) { + path := defaultPath(isMultisig, version) extendedKey, err := extendedKeyFromMnemonicAndPath(mnemonic, path, params) if err != nil { return "", err diff --git a/cmd/karlsenwallet/libkarlsenwallet/sign.go b/cmd/karlsenwallet/libkarlsenwallet/sign.go index fca6979b0e..2df589d6f9 100644 --- a/cmd/karlsenwallet/libkarlsenwallet/sign.go +++ b/cmd/karlsenwallet/libkarlsenwallet/sign.go @@ -28,14 +28,14 @@ func rawTxInSignature(extendedKey *bip32.ExtendedKey, tx *externalapi.DomainTran } // Sign signs the transaction with the given private keys -func Sign(params *dagconfig.Params, mnemonics []string, serializedPSTx []byte, ecdsa bool) ([]byte, error) { +func Sign(params *dagconfig.Params, mnemonics []string, serializedPSTx []byte, ecdsa bool, version uint32) ([]byte, error) { partiallySignedTransaction, err := serialization.DeserializePartiallySignedTransaction(serializedPSTx) if err != nil { return nil, err } for _, mnemonic := range mnemonics { - err = sign(params, mnemonic, partiallySignedTransaction, ecdsa) + err = sign(params, mnemonic, partiallySignedTransaction, ecdsa, version) if err != nil { return nil, err } @@ -43,7 +43,7 @@ func Sign(params *dagconfig.Params, mnemonics []string, serializedPSTx []byte, e return serialization.SerializePartiallySignedTransaction(partiallySignedTransaction) } -func sign(params *dagconfig.Params, mnemonic string, partiallySignedTransaction *serialization.PartiallySignedTransaction, ecdsa bool) error { +func sign(params *dagconfig.Params, mnemonic string, partiallySignedTransaction *serialization.PartiallySignedTransaction, ecdsa bool, version uint32) error { if isTransactionFullySigned(partiallySignedTransaction) { return nil } @@ -63,7 +63,7 @@ func sign(params *dagconfig.Params, mnemonic string, partiallySignedTransaction signed := false for i, partiallySignedInput := range partiallySignedTransaction.PartiallySignedInputs { isMultisig := len(partiallySignedInput.PubKeySignaturePairs) > 1 - path := defaultPath(isMultisig) + path := defaultPath(isMultisig, version) extendedKey, err := extendedKeyFromMnemonicAndPath(mnemonic, path, params) if err != nil { return err diff --git a/cmd/karlsenwallet/libkarlsenwallet/transaction_test.go b/cmd/karlsenwallet/libkarlsenwallet/transaction_test.go index b820174002..8af1b79d3a 100644 --- a/cmd/karlsenwallet/libkarlsenwallet/transaction_test.go +++ b/cmd/karlsenwallet/libkarlsenwallet/transaction_test.go @@ -28,166 +28,303 @@ func forSchnorrAndECDSA(t *testing.T, testFunc func(t *testing.T, ecdsa bool)) { } func TestMultisig(t *testing.T) { - testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - params := &consensusConfig.Params - forSchnorrAndECDSA(t, func(t *testing.T, ecdsa bool) { - consensusConfig.BlockCoinbaseMaturity = 0 - tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestMultisig") - if err != nil { - t.Fatalf("Error setting up tc: %+v", err) - } - defer teardown(false) - - const numKeys = 3 - mnemonics := make([]string, numKeys) - publicKeys := make([]string, numKeys) - for i := 0; i < numKeys; i++ { - var err error - mnemonics[i], err = libkarlsenwallet.CreateMnemonic() + testutils.ForAllPaths(t, func(t *testing.T, version uint32) { + testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { + params := &consensusConfig.Params + forSchnorrAndECDSA(t, func(t *testing.T, ecdsa bool) { + consensusConfig.BlockCoinbaseMaturity = 0 + tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestMultisig") if err != nil { - t.Fatalf("CreateMnemonic: %+v", err) + t.Fatalf("Error setting up tc: %+v", err) + } + defer teardown(false) + + const numKeys = 3 + mnemonics := make([]string, numKeys) + publicKeys := make([]string, numKeys) + for i := 0; i < numKeys; i++ { + var err error + mnemonics[i], err = libkarlsenwallet.CreateMnemonic() + if err != nil { + t.Fatalf("CreateMnemonic: %+v", err) + } + + publicKeys[i], err = libkarlsenwallet.MasterPublicKeyFromMnemonic(&consensusConfig.Params, mnemonics[i], true, version) + if err != nil { + t.Fatalf("MasterPublicKeyFromMnemonic: %+v", err) + } } - publicKeys[i], err = libkarlsenwallet.MasterPublicKeyFromMnemonic(&consensusConfig.Params, mnemonics[i], true) + const minimumSignatures = 2 + path := "m/1/2/3" + address, err := libkarlsenwallet.Address(params, publicKeys, minimumSignatures, path, ecdsa) if err != nil { - t.Fatalf("MasterPublicKeyFromMnemonic: %+v", err) + t.Fatalf("Address: %+v", err) } - } - const minimumSignatures = 2 - path := "m/1/2/3" - address, err := libkarlsenwallet.Address(params, publicKeys, minimumSignatures, path, ecdsa) - if err != nil { - t.Fatalf("Address: %+v", err) - } - - if _, ok := address.(*util.AddressScriptHash); !ok { - t.Fatalf("The address is of unexpected type") - } + if _, ok := address.(*util.AddressScriptHash); !ok { + t.Fatalf("The address is of unexpected type") + } - scriptPublicKey, err := txscript.PayToAddrScript(address) - if err != nil { - t.Fatalf("PayToAddrScript: %+v", err) - } + scriptPublicKey, err := txscript.PayToAddrScript(address) + if err != nil { + t.Fatalf("PayToAddrScript: %+v", err) + } - coinbaseData := &externalapi.DomainCoinbaseData{ - ScriptPublicKey: scriptPublicKey, - ExtraData: nil, - } + coinbaseData := &externalapi.DomainCoinbaseData{ + ScriptPublicKey: scriptPublicKey, + ExtraData: nil, + } - fundingBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, coinbaseData, nil) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } + fundingBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, coinbaseData, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } - block1Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlockHash}, nil, nil) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } + block1Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlockHash}, nil, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } - block1, _, err := tc.GetBlock(block1Hash) - if err != nil { - t.Fatalf("GetBlock: %+v", err) - } + block1, _, err := tc.GetBlock(block1Hash) + if err != nil { + t.Fatalf("GetBlock: %+v", err) + } - block1Tx := block1.Transactions[0] - block1TxOut := block1Tx.Outputs[0] - selectedUTXOs := []*libkarlsenwallet.UTXO{ - { - Outpoint: &externalapi.DomainOutpoint{ - TransactionID: *consensushashing.TransactionID(block1.Transactions[0]), - Index: 0, + block1Tx := block1.Transactions[0] + block1TxOut := block1Tx.Outputs[0] + selectedUTXOs := []*libkarlsenwallet.UTXO{ + { + Outpoint: &externalapi.DomainOutpoint{ + TransactionID: *consensushashing.TransactionID(block1.Transactions[0]), + Index: 0, + }, + UTXOEntry: utxo.NewUTXOEntry(block1TxOut.Value, block1TxOut.ScriptPublicKey, true, 0), + DerivationPath: path, }, - UTXOEntry: utxo.NewUTXOEntry(block1TxOut.Value, block1TxOut.ScriptPublicKey, true, 0), - DerivationPath: path, - }, - } + } - unsignedTransaction, err := libkarlsenwallet.CreateUnsignedTransaction(publicKeys, minimumSignatures, - []*libkarlsenwallet.Payment{{ - Address: address, - Amount: 10, - }}, selectedUTXOs) - if err != nil { - t.Fatalf("CreateUnsignedTransactions: %+v", err) - } + unsignedTransaction, err := libkarlsenwallet.CreateUnsignedTransaction(publicKeys, minimumSignatures, + []*libkarlsenwallet.Payment{{ + Address: address, + Amount: 10, + }}, selectedUTXOs) + if err != nil { + t.Fatalf("CreateUnsignedTransactions: %+v", err) + } - isFullySigned, err := libkarlsenwallet.IsTransactionFullySigned(unsignedTransaction) - if err != nil { - t.Fatalf("IsTransactionFullySigned: %+v", err) - } + isFullySigned, err := libkarlsenwallet.IsTransactionFullySigned(unsignedTransaction) + if err != nil { + t.Fatalf("IsTransactionFullySigned: %+v", err) + } - if isFullySigned { - t.Fatalf("Transaction is not expected to be signed") - } + if isFullySigned { + t.Fatalf("Transaction is not expected to be signed") + } - _, err = libkarlsenwallet.ExtractTransaction(unsignedTransaction, ecdsa) - if err == nil || !strings.Contains(err.Error(), fmt.Sprintf("missing %d signatures", minimumSignatures)) { - t.Fatal("Unexpectedly succeed to extract a valid transaction out of unsigned transaction") - } + _, err = libkarlsenwallet.ExtractTransaction(unsignedTransaction, ecdsa) + if err == nil || !strings.Contains(err.Error(), fmt.Sprintf("missing %d signatures", minimumSignatures)) { + t.Fatal("Unexpectedly succeed to extract a valid transaction out of unsigned transaction") + } - signedTxStep1, err := libkarlsenwallet.Sign(params, mnemonics[:1], unsignedTransaction, ecdsa) - if err != nil { - t.Fatalf("Sign: %+v", err) - } + signedTxStep1, err := libkarlsenwallet.Sign(params, mnemonics[:1], unsignedTransaction, ecdsa, version) + if err != nil { + t.Fatalf("Sign: %+v", err) + } - isFullySigned, err = libkarlsenwallet.IsTransactionFullySigned(signedTxStep1) - if err != nil { - t.Fatalf("IsTransactionFullySigned: %+v", err) - } + isFullySigned, err = libkarlsenwallet.IsTransactionFullySigned(signedTxStep1) + if err != nil { + t.Fatalf("IsTransactionFullySigned: %+v", err) + } - if isFullySigned { - t.Fatalf("Transaction is not expected to be fully signed") - } + if isFullySigned { + t.Fatalf("Transaction is not expected to be fully signed") + } - signedTxStep2, err := libkarlsenwallet.Sign(params, mnemonics[1:2], signedTxStep1, ecdsa) - if err != nil { - t.Fatalf("Sign: %+v", err) - } + signedTxStep2, err := libkarlsenwallet.Sign(params, mnemonics[1:2], signedTxStep1, ecdsa, version) + if err != nil { + t.Fatalf("Sign: %+v", err) + } - extractedSignedTxStep2, err := libkarlsenwallet.ExtractTransaction(signedTxStep2, ecdsa) - if err != nil { - t.Fatalf("ExtractTransaction: %+v", err) - } + extractedSignedTxStep2, err := libkarlsenwallet.ExtractTransaction(signedTxStep2, ecdsa) + if err != nil { + t.Fatalf("ExtractTransaction: %+v", err) + } - signedTxOneStep, err := libkarlsenwallet.Sign(params, mnemonics[:2], unsignedTransaction, ecdsa) - if err != nil { - t.Fatalf("Sign: %+v", err) - } + signedTxOneStep, err := libkarlsenwallet.Sign(params, mnemonics[:2], unsignedTransaction, ecdsa, version) + if err != nil { + t.Fatalf("Sign: %+v", err) + } - extractedSignedTxOneStep, err := libkarlsenwallet.ExtractTransaction(signedTxOneStep, ecdsa) - if err != nil { - t.Fatalf("ExtractTransaction: %+v", err) - } + extractedSignedTxOneStep, err := libkarlsenwallet.ExtractTransaction(signedTxOneStep, ecdsa) + if err != nil { + t.Fatalf("ExtractTransaction: %+v", err) + } - // We check IDs instead of comparing the actual transactions because the actual transactions have different - // signature scripts due to non deterministic signature scheme. - if !consensushashing.TransactionID(extractedSignedTxStep2).Equal(consensushashing.TransactionID(extractedSignedTxOneStep)) { - t.Fatalf("Expected extractedSignedTxOneStep and extractedSignedTxStep2 IDs to be equal") - } + // We check IDs instead of comparing the actual transactions because the actual transactions have different + // signature scripts due to non deterministic signature scheme. + if !consensushashing.TransactionID(extractedSignedTxStep2).Equal(consensushashing.TransactionID(extractedSignedTxOneStep)) { + t.Fatalf("Expected extractedSignedTxOneStep and extractedSignedTxStep2 IDs to be equal") + } - _, virtualChangeSet, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, []*externalapi.DomainTransaction{extractedSignedTxStep2}) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } + _, virtualChangeSet, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, []*externalapi.DomainTransaction{extractedSignedTxStep2}) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } - addedUTXO := &externalapi.DomainOutpoint{ - TransactionID: *consensushashing.TransactionID(extractedSignedTxStep2), - Index: 0, - } - if !virtualChangeSet.VirtualUTXODiff.ToAdd().Contains(addedUTXO) { - t.Fatalf("Transaction wasn't accepted in the DAG") - } + addedUTXO := &externalapi.DomainOutpoint{ + TransactionID: *consensushashing.TransactionID(extractedSignedTxStep2), + Index: 0, + } + if !virtualChangeSet.VirtualUTXODiff.ToAdd().Contains(addedUTXO) { + t.Fatalf("Transaction wasn't accepted in the DAG") + } + }) }) }) } func TestP2PK(t *testing.T) { - testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - params := &consensusConfig.Params - forSchnorrAndECDSA(t, func(t *testing.T, ecdsa bool) { - consensusConfig.BlockCoinbaseMaturity = 0 - tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestMultisig") + testutils.ForAllPaths(t, func(t *testing.T, version uint32) { + testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { + params := &consensusConfig.Params + forSchnorrAndECDSA(t, func(t *testing.T, ecdsa bool) { + consensusConfig.BlockCoinbaseMaturity = 0 + tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestMultisig") + if err != nil { + t.Fatalf("Error setting up tc: %+v", err) + } + defer teardown(false) + + const numKeys = 1 + mnemonics := make([]string, numKeys) + publicKeys := make([]string, numKeys) + for i := 0; i < numKeys; i++ { + var err error + mnemonics[i], err = libkarlsenwallet.CreateMnemonic() + if err != nil { + t.Fatalf("CreateMnemonic: %+v", err) + } + + publicKeys[i], err = libkarlsenwallet.MasterPublicKeyFromMnemonic(&consensusConfig.Params, mnemonics[i], false, version) + if err != nil { + t.Fatalf("MasterPublicKeyFromMnemonic: %+v", err) + } + } + + const minimumSignatures = 1 + path := "m/1/2/3" + address, err := libkarlsenwallet.Address(params, publicKeys, minimumSignatures, path, ecdsa) + if err != nil { + t.Fatalf("Address: %+v", err) + } + + if ecdsa { + if _, ok := address.(*util.AddressPublicKeyECDSA); !ok { + t.Fatalf("The address is of unexpected type") + } + } else { + if _, ok := address.(*util.AddressPublicKey); !ok { + t.Fatalf("The address is of unexpected type") + } + } + + scriptPublicKey, err := txscript.PayToAddrScript(address) + if err != nil { + t.Fatalf("PayToAddrScript: %+v", err) + } + + coinbaseData := &externalapi.DomainCoinbaseData{ + ScriptPublicKey: scriptPublicKey, + ExtraData: nil, + } + + fundingBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, coinbaseData, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + + block1Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlockHash}, nil, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + + block1, _, err := tc.GetBlock(block1Hash) + if err != nil { + t.Fatalf("GetBlock: %+v", err) + } + + block1Tx := block1.Transactions[0] + block1TxOut := block1Tx.Outputs[0] + selectedUTXOs := []*libkarlsenwallet.UTXO{ + { + Outpoint: &externalapi.DomainOutpoint{ + TransactionID: *consensushashing.TransactionID(block1.Transactions[0]), + Index: 0, + }, + UTXOEntry: utxo.NewUTXOEntry(block1TxOut.Value, block1TxOut.ScriptPublicKey, true, 0), + DerivationPath: path, + }, + } + + unsignedTransaction, err := libkarlsenwallet.CreateUnsignedTransaction(publicKeys, minimumSignatures, + []*libkarlsenwallet.Payment{{ + Address: address, + Amount: 10, + }}, selectedUTXOs) + if err != nil { + t.Fatalf("CreateUnsignedTransactions: %+v", err) + } + + isFullySigned, err := libkarlsenwallet.IsTransactionFullySigned(unsignedTransaction) + if err != nil { + t.Fatalf("IsTransactionFullySigned: %+v", err) + } + + if isFullySigned { + t.Fatalf("Transaction is not expected to be signed") + } + + _, err = libkarlsenwallet.ExtractTransaction(unsignedTransaction, ecdsa) + if err == nil || !strings.Contains(err.Error(), "missing signature") { + t.Fatal("Unexpectedly succeed to extract a valid transaction out of unsigned transaction") + } + + signedTx, err := libkarlsenwallet.Sign(params, mnemonics, unsignedTransaction, ecdsa, version) + if err != nil { + t.Fatalf("Sign: %+v", err) + } + + tx, err := libkarlsenwallet.ExtractTransaction(signedTx, ecdsa) + if err != nil { + t.Fatalf("ExtractTransaction: %+v", err) + } + + _, virtualChangeSet, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, []*externalapi.DomainTransaction{tx}) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + + addedUTXO := &externalapi.DomainOutpoint{ + TransactionID: *consensushashing.TransactionID(tx), + Index: 0, + } + if !virtualChangeSet.VirtualUTXODiff.ToAdd().Contains(addedUTXO) { + t.Fatalf("Transaction wasn't accepted in the DAG") + } + }) + }) + }) +} + +func TestMaxSompi(t *testing.T) { + testutils.ForAllPaths(t, func(t *testing.T, version uint32) { + testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { + params := &consensusConfig.Params + cfg := *consensusConfig + cfg.BlockCoinbaseMaturity = 0 + cfg.PreDeflationaryPhaseBaseSubsidy = 20e6 * constants.SompiPerKarlsen + tc, teardown, err := consensus.NewFactory().NewTestConsensus(&cfg, "TestMaxSompi") if err != nil { t.Fatalf("Error setting up tc: %+v", err) } @@ -203,7 +340,7 @@ func TestP2PK(t *testing.T) { t.Fatalf("CreateMnemonic: %+v", err) } - publicKeys[i], err = libkarlsenwallet.MasterPublicKeyFromMnemonic(&consensusConfig.Params, mnemonics[i], false) + publicKeys[i], err = libkarlsenwallet.MasterPublicKeyFromMnemonic(&cfg.Params, mnemonics[i], false, version) if err != nil { t.Fatalf("MasterPublicKeyFromMnemonic: %+v", err) } @@ -211,21 +348,11 @@ func TestP2PK(t *testing.T) { const minimumSignatures = 1 path := "m/1/2/3" - address, err := libkarlsenwallet.Address(params, publicKeys, minimumSignatures, path, ecdsa) + address, err := libkarlsenwallet.Address(params, publicKeys, minimumSignatures, path, false) if err != nil { t.Fatalf("Address: %+v", err) } - if ecdsa { - if _, ok := address.(*util.AddressPublicKeyECDSA); !ok { - t.Fatalf("The address is of unexpected type") - } - } else { - if _, ok := address.(*util.AddressPublicKey); !ok { - t.Fatalf("The address is of unexpected type") - } - } - scriptPublicKey, err := txscript.PayToAddrScript(address) if err != nil { t.Fatalf("PayToAddrScript: %+v", err) @@ -236,12 +363,42 @@ func TestP2PK(t *testing.T) { ExtraData: nil, } - fundingBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, coinbaseData, nil) + fundingBlock1Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{cfg.GenesisHash}, coinbaseData, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + + fundingBlock2Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlock1Hash}, coinbaseData, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + + fundingBlock3Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlock2Hash}, coinbaseData, nil) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + + fundingBlock4Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlock3Hash}, coinbaseData, nil) if err != nil { t.Fatalf("AddBlock: %+v", err) } - block1Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlockHash}, nil, nil) + fundingBlock2, _, err := tc.GetBlock(fundingBlock2Hash) + if err != nil { + t.Fatalf("GetBlock: %+v", err) + } + + fundingBlock3, _, err := tc.GetBlock(fundingBlock3Hash) + if err != nil { + t.Fatalf("GetBlock: %+v", err) + } + + fundingBlock4, _, err := tc.GetBlock(fundingBlock4Hash) + if err != nil { + t.Fatalf("GetBlock: %+v", err) + } + + block1Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlock4Hash}, nil, nil) if err != nil { t.Fatalf("AddBlock: %+v", err) } @@ -251,269 +408,118 @@ func TestP2PK(t *testing.T) { t.Fatalf("GetBlock: %+v", err) } - block1Tx := block1.Transactions[0] - block1TxOut := block1Tx.Outputs[0] - selectedUTXOs := []*libkarlsenwallet.UTXO{ + txOut1 := fundingBlock2.Transactions[0].Outputs[0] + txOut2 := fundingBlock3.Transactions[0].Outputs[0] + txOut3 := fundingBlock4.Transactions[0].Outputs[0] + txOut4 := block1.Transactions[0].Outputs[0] + selectedUTXOsForTxWithLargeInputAmount := []*libkarlsenwallet.UTXO{ { Outpoint: &externalapi.DomainOutpoint{ - TransactionID: *consensushashing.TransactionID(block1.Transactions[0]), + TransactionID: *consensushashing.TransactionID(fundingBlock2.Transactions[0]), Index: 0, }, - UTXOEntry: utxo.NewUTXOEntry(block1TxOut.Value, block1TxOut.ScriptPublicKey, true, 0), + UTXOEntry: utxo.NewUTXOEntry(txOut1.Value, txOut1.ScriptPublicKey, true, 0), + DerivationPath: path, + }, + { + Outpoint: &externalapi.DomainOutpoint{ + TransactionID: *consensushashing.TransactionID(fundingBlock3.Transactions[0]), + Index: 0, + }, + UTXOEntry: utxo.NewUTXOEntry(txOut2.Value, txOut2.ScriptPublicKey, true, 0), DerivationPath: path, }, } - unsignedTransaction, err := libkarlsenwallet.CreateUnsignedTransaction(publicKeys, minimumSignatures, + unsignedTxWithLargeInputAmount, err := libkarlsenwallet.CreateUnsignedTransaction(publicKeys, minimumSignatures, []*libkarlsenwallet.Payment{{ Address: address, Amount: 10, - }}, selectedUTXOs) + }}, selectedUTXOsForTxWithLargeInputAmount) if err != nil { t.Fatalf("CreateUnsignedTransactions: %+v", err) } - isFullySigned, err := libkarlsenwallet.IsTransactionFullySigned(unsignedTransaction) + signedTxWithLargeInputAmount, err := libkarlsenwallet.Sign(params, mnemonics, unsignedTxWithLargeInputAmount, false, version) if err != nil { - t.Fatalf("IsTransactionFullySigned: %+v", err) + t.Fatalf("Sign: %+v", err) } - if isFullySigned { - t.Fatalf("Transaction is not expected to be signed") + txWithLargeInputAmount, err := libkarlsenwallet.ExtractTransaction(signedTxWithLargeInputAmount, false) + if err != nil { + t.Fatalf("ExtractTransaction: %+v", err) } - _, err = libkarlsenwallet.ExtractTransaction(unsignedTransaction, ecdsa) - if err == nil || !strings.Contains(err.Error(), "missing signature") { - t.Fatal("Unexpectedly succeed to extract a valid transaction out of unsigned transaction") + _, virtualChangeSet, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, []*externalapi.DomainTransaction{txWithLargeInputAmount}) + if err != nil { + t.Fatalf("AddBlock: %+v", err) + } + + addedUTXO1 := &externalapi.DomainOutpoint{ + TransactionID: *consensushashing.TransactionID(txWithLargeInputAmount), + Index: 0, + } + if !virtualChangeSet.VirtualUTXODiff.ToAdd().Contains(addedUTXO1) { + t.Fatalf("Transaction wasn't accepted in the DAG") } - signedTx, err := libkarlsenwallet.Sign(params, mnemonics, unsignedTransaction, ecdsa) + selectedUTXOsForTxWithLargeInputAndOutputAmount := []*libkarlsenwallet.UTXO{ + { + Outpoint: &externalapi.DomainOutpoint{ + TransactionID: *consensushashing.TransactionID(fundingBlock4.Transactions[0]), + Index: 0, + }, + UTXOEntry: utxo.NewUTXOEntry(txOut3.Value, txOut3.ScriptPublicKey, true, 0), + DerivationPath: path, + }, + { + Outpoint: &externalapi.DomainOutpoint{ + TransactionID: *consensushashing.TransactionID(block1.Transactions[0]), + Index: 0, + }, + UTXOEntry: utxo.NewUTXOEntry(txOut4.Value, txOut4.ScriptPublicKey, true, 0), + DerivationPath: path, + }, + } + + unsignedTxWithLargeInputAndOutputAmount, err := libkarlsenwallet.CreateUnsignedTransaction(publicKeys, minimumSignatures, + []*libkarlsenwallet.Payment{{ + Address: address, + Amount: 22e6 * constants.SompiPerKarlsen, + }}, selectedUTXOsForTxWithLargeInputAndOutputAmount) + if err != nil { + t.Fatalf("CreateUnsignedTransactions: %+v", err) + } + + signedTxWithLargeInputAndOutputAmount, err := libkarlsenwallet.Sign(params, mnemonics, unsignedTxWithLargeInputAndOutputAmount, false, version) if err != nil { t.Fatalf("Sign: %+v", err) } - tx, err := libkarlsenwallet.ExtractTransaction(signedTx, ecdsa) + txWithLargeInputAndOutputAmount, err := libkarlsenwallet.ExtractTransaction(signedTxWithLargeInputAndOutputAmount, false) if err != nil { t.Fatalf("ExtractTransaction: %+v", err) } - _, virtualChangeSet, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, []*externalapi.DomainTransaction{tx}) + // We're creating a new longer chain so we can double spend txWithLargeInputAmount + newChainRoot, _, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, nil) if err != nil { t.Fatalf("AddBlock: %+v", err) } - addedUTXO := &externalapi.DomainOutpoint{ - TransactionID: *consensushashing.TransactionID(tx), - Index: 0, - } - if !virtualChangeSet.VirtualUTXODiff.ToAdd().Contains(addedUTXO) { - t.Fatalf("Transaction wasn't accepted in the DAG") + _, virtualChangeSet, err = tc.AddBlock([]*externalapi.DomainHash{newChainRoot}, nil, []*externalapi.DomainTransaction{txWithLargeInputAndOutputAmount}) + if err != nil { + t.Fatalf("AddBlock: %+v", err) } - }) - }) -} -func TestMaxSompi(t *testing.T) { - testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { - params := &consensusConfig.Params - cfg := *consensusConfig - cfg.BlockCoinbaseMaturity = 0 - cfg.PreDeflationaryPhaseBaseSubsidy = 20e6 * constants.SompiPerKarlsen - tc, teardown, err := consensus.NewFactory().NewTestConsensus(&cfg, "TestMaxSompi") - if err != nil { - t.Fatalf("Error setting up tc: %+v", err) - } - defer teardown(false) - - const numKeys = 1 - mnemonics := make([]string, numKeys) - publicKeys := make([]string, numKeys) - for i := 0; i < numKeys; i++ { - var err error - mnemonics[i], err = libkarlsenwallet.CreateMnemonic() - if err != nil { - t.Fatalf("CreateMnemonic: %+v", err) + addedUTXO2 := &externalapi.DomainOutpoint{ + TransactionID: *consensushashing.TransactionID(txWithLargeInputAndOutputAmount), + Index: 0, } - publicKeys[i], err = libkarlsenwallet.MasterPublicKeyFromMnemonic(&cfg.Params, mnemonics[i], false) - if err != nil { - t.Fatalf("MasterPublicKeyFromMnemonic: %+v", err) + if !virtualChangeSet.VirtualUTXODiff.ToAdd().Contains(addedUTXO2) { + t.Fatalf("txWithLargeInputAndOutputAmount weren't accepted in the DAG") } - } - - const minimumSignatures = 1 - path := "m/1/2/3" - address, err := libkarlsenwallet.Address(params, publicKeys, minimumSignatures, path, false) - if err != nil { - t.Fatalf("Address: %+v", err) - } - - scriptPublicKey, err := txscript.PayToAddrScript(address) - if err != nil { - t.Fatalf("PayToAddrScript: %+v", err) - } - - coinbaseData := &externalapi.DomainCoinbaseData{ - ScriptPublicKey: scriptPublicKey, - ExtraData: nil, - } - - fundingBlock1Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{cfg.GenesisHash}, coinbaseData, nil) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } - - fundingBlock2Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlock1Hash}, coinbaseData, nil) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } - - fundingBlock3Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlock2Hash}, coinbaseData, nil) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } - - fundingBlock4Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlock3Hash}, coinbaseData, nil) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } - - fundingBlock2, _, err := tc.GetBlock(fundingBlock2Hash) - if err != nil { - t.Fatalf("GetBlock: %+v", err) - } - - fundingBlock3, _, err := tc.GetBlock(fundingBlock3Hash) - if err != nil { - t.Fatalf("GetBlock: %+v", err) - } - - fundingBlock4, _, err := tc.GetBlock(fundingBlock4Hash) - if err != nil { - t.Fatalf("GetBlock: %+v", err) - } - - block1Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlock4Hash}, nil, nil) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } - - block1, _, err := tc.GetBlock(block1Hash) - if err != nil { - t.Fatalf("GetBlock: %+v", err) - } - - txOut1 := fundingBlock2.Transactions[0].Outputs[0] - txOut2 := fundingBlock3.Transactions[0].Outputs[0] - txOut3 := fundingBlock4.Transactions[0].Outputs[0] - txOut4 := block1.Transactions[0].Outputs[0] - selectedUTXOsForTxWithLargeInputAmount := []*libkarlsenwallet.UTXO{ - { - Outpoint: &externalapi.DomainOutpoint{ - TransactionID: *consensushashing.TransactionID(fundingBlock2.Transactions[0]), - Index: 0, - }, - UTXOEntry: utxo.NewUTXOEntry(txOut1.Value, txOut1.ScriptPublicKey, true, 0), - DerivationPath: path, - }, - { - Outpoint: &externalapi.DomainOutpoint{ - TransactionID: *consensushashing.TransactionID(fundingBlock3.Transactions[0]), - Index: 0, - }, - UTXOEntry: utxo.NewUTXOEntry(txOut2.Value, txOut2.ScriptPublicKey, true, 0), - DerivationPath: path, - }, - } - - unsignedTxWithLargeInputAmount, err := libkarlsenwallet.CreateUnsignedTransaction(publicKeys, minimumSignatures, - []*libkarlsenwallet.Payment{{ - Address: address, - Amount: 10, - }}, selectedUTXOsForTxWithLargeInputAmount) - if err != nil { - t.Fatalf("CreateUnsignedTransactions: %+v", err) - } - - signedTxWithLargeInputAmount, err := libkarlsenwallet.Sign(params, mnemonics, unsignedTxWithLargeInputAmount, false) - if err != nil { - t.Fatalf("Sign: %+v", err) - } - - txWithLargeInputAmount, err := libkarlsenwallet.ExtractTransaction(signedTxWithLargeInputAmount, false) - if err != nil { - t.Fatalf("ExtractTransaction: %+v", err) - } - - _, virtualChangeSet, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, []*externalapi.DomainTransaction{txWithLargeInputAmount}) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } - - addedUTXO1 := &externalapi.DomainOutpoint{ - TransactionID: *consensushashing.TransactionID(txWithLargeInputAmount), - Index: 0, - } - if !virtualChangeSet.VirtualUTXODiff.ToAdd().Contains(addedUTXO1) { - t.Fatalf("Transaction wasn't accepted in the DAG") - } - - selectedUTXOsForTxWithLargeInputAndOutputAmount := []*libkarlsenwallet.UTXO{ - { - Outpoint: &externalapi.DomainOutpoint{ - TransactionID: *consensushashing.TransactionID(fundingBlock4.Transactions[0]), - Index: 0, - }, - UTXOEntry: utxo.NewUTXOEntry(txOut3.Value, txOut3.ScriptPublicKey, true, 0), - DerivationPath: path, - }, - { - Outpoint: &externalapi.DomainOutpoint{ - TransactionID: *consensushashing.TransactionID(block1.Transactions[0]), - Index: 0, - }, - UTXOEntry: utxo.NewUTXOEntry(txOut4.Value, txOut4.ScriptPublicKey, true, 0), - DerivationPath: path, - }, - } - - unsignedTxWithLargeInputAndOutputAmount, err := libkarlsenwallet.CreateUnsignedTransaction(publicKeys, minimumSignatures, - []*libkarlsenwallet.Payment{{ - Address: address, - Amount: 22e6 * constants.SompiPerKarlsen, - }}, selectedUTXOsForTxWithLargeInputAndOutputAmount) - if err != nil { - t.Fatalf("CreateUnsignedTransactions: %+v", err) - } - - signedTxWithLargeInputAndOutputAmount, err := libkarlsenwallet.Sign(params, mnemonics, unsignedTxWithLargeInputAndOutputAmount, false) - if err != nil { - t.Fatalf("Sign: %+v", err) - } - - txWithLargeInputAndOutputAmount, err := libkarlsenwallet.ExtractTransaction(signedTxWithLargeInputAndOutputAmount, false) - if err != nil { - t.Fatalf("ExtractTransaction: %+v", err) - } - - // We're creating a new longer chain so we can double spend txWithLargeInputAmount - newChainRoot, _, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, nil) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } - - _, virtualChangeSet, err = tc.AddBlock([]*externalapi.DomainHash{newChainRoot}, nil, []*externalapi.DomainTransaction{txWithLargeInputAndOutputAmount}) - if err != nil { - t.Fatalf("AddBlock: %+v", err) - } - - addedUTXO2 := &externalapi.DomainOutpoint{ - TransactionID: *consensushashing.TransactionID(txWithLargeInputAndOutputAmount), - Index: 0, - } - - if !virtualChangeSet.VirtualUTXODiff.ToAdd().Contains(addedUTXO2) { - t.Fatalf("txWithLargeInputAndOutputAmount weren't accepted in the DAG") - } + }) }) } diff --git a/cmd/karlsenwallet/send.go b/cmd/karlsenwallet/send.go index eafc0344d5..bf6a606f30 100644 --- a/cmd/karlsenwallet/send.go +++ b/cmd/karlsenwallet/send.go @@ -68,7 +68,7 @@ func send(conf *sendConfig) error { signedTransactions := make([][]byte, len(createUnsignedTransactionsResponse.UnsignedTransactions)) for i, unsignedTransaction := range createUnsignedTransactionsResponse.UnsignedTransactions { - signedTransaction, err := libkarlsenwallet.Sign(conf.NetParams(), mnemonics, unsignedTransaction, keysFile.ECDSA) + signedTransaction, err := libkarlsenwallet.Sign(conf.NetParams(), mnemonics, unsignedTransaction, keysFile.ECDSA, keysFile.Version) if err != nil { return err } diff --git a/cmd/karlsenwallet/sign.go b/cmd/karlsenwallet/sign.go index 3554f89094..15f2d25eed 100644 --- a/cmd/karlsenwallet/sign.go +++ b/cmd/karlsenwallet/sign.go @@ -48,7 +48,7 @@ func sign(conf *signConfig) error { updatedPartiallySignedTransactions := make([][]byte, len(partiallySignedTransactions)) for i, partiallySignedTransaction := range partiallySignedTransactions { updatedPartiallySignedTransactions[i], err = - libkarlsenwallet.Sign(conf.NetParams(), privateKeys, partiallySignedTransaction, keysFile.ECDSA) + libkarlsenwallet.Sign(conf.NetParams(), privateKeys, partiallySignedTransaction, keysFile.ECDSA, keysFile.Version) if err != nil { return err } diff --git a/domain/consensus/utils/testutils/for_all_paths.go b/domain/consensus/utils/testutils/for_all_paths.go new file mode 100644 index 0000000000..ffb9cec267 --- /dev/null +++ b/domain/consensus/utils/testutils/for_all_paths.go @@ -0,0 +1,19 @@ +package testutils + +import ( + "fmt" + "testing" + + "github.com/karlsen-network/karlsend/cmd/karlsenwallet/keys" +) + +// ForAllPaths runs the passed testFunc with all available derivation paths +func ForAllPaths(t *testing.T, testFunc func(*testing.T, uint32)) { + for i := uint32(1); i <= keys.LastVersion; i++ { + version := fmt.Sprintf("v%d", i) + t.Run(version, func(t *testing.T) { + t.Logf("Running test for wallet version %d", i) + testFunc(t, i) + }) + } +} diff --git a/version/version.go b/version/version.go index 01a8c3dbbb..619e8f3f57 100644 --- a/version/version.go +++ b/version/version.go @@ -10,7 +10,7 @@ const validCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs const ( appMajor uint = 1 - appMinor uint = 1 + appMinor uint = 2 appPatch uint = 0 )