// Copyright (c) 2013-2016 The btcsuite developers // Copyright (c) 2015-2019 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package txscript import ( "crypto/rand" "errors" "fmt" mrand "math/rand" "testing" "github.com/decred/dcrd/chaincfg/chainhash" "github.com/decred/dcrd/chaincfg/v2" "github.com/decred/dcrd/chaincfg/v2/chainec" "github.com/decred/dcrd/dcrec" "github.com/decred/dcrd/dcrutil/v2" "github.com/decred/dcrd/wire" ) // testingParams defines the chain params to use throughout these tests so it // can more easily be changed if desired. var testingParams = chaincfg.RegNetParams() const testValueIn = 12345 type addressToKey struct { key *chainec.PrivateKey compressed bool } func mkGetKey(keys map[string]addressToKey) KeyDB { if keys == nil { return KeyClosure(func(addr dcrutil.Address) (chainec.PrivateKey, bool, error) { return nil, false, errors.New("nope 1") }) } return KeyClosure(func(addr dcrutil.Address) (chainec.PrivateKey, bool, error) { a2k, ok := keys[addr.Address()] if !ok { return nil, false, errors.New("nope 2") } return *a2k.key, a2k.compressed, nil }) } func mkGetKeyPub(keys map[string]addressToKey) KeyDB { if keys == nil { return KeyClosure(func(addr dcrutil.Address) (chainec.PrivateKey, bool, error) { return nil, false, errors.New("nope 1") }) } return KeyClosure(func(addr dcrutil.Address) (chainec.PrivateKey, bool, error) { a2k, ok := keys[addr.String()] if !ok { return nil, false, errors.New("nope 2") } return *a2k.key, a2k.compressed, nil }) } func mkGetScript(scripts map[string][]byte) ScriptDB { if scripts == nil { return ScriptClosure(func(addr dcrutil.Address) ( []byte, error) { return nil, errors.New("nope 3") }) } return ScriptClosure(func(addr dcrutil.Address) ([]byte, error) { script, ok := scripts[addr.Address()] if !ok { return nil, errors.New("nope 4") } return script, nil }) } func checkScripts(msg string, tx *wire.MsgTx, idx int, sigScript, pkScript []byte) error { tx.TxIn[idx].SignatureScript = sigScript var scriptFlags ScriptFlags vm, err := NewEngine(pkScript, tx, idx, scriptFlags, 0, nil) if err != nil { return fmt.Errorf("failed to make script engine for %s: %v", msg, err) } err = vm.Execute() if err != nil { return fmt.Errorf("invalid script signature for %s: %v", msg, err) } return nil } func signAndCheck(msg string, tx *wire.MsgTx, idx int, pkScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB, suite dcrec.SignatureType) error { sigScript, err := SignTxOutput(testingParams, tx, idx, pkScript, hashType, kdb, sdb, nil, suite) if err != nil { return fmt.Errorf("failed to sign output %s: %v", msg, err) } return checkScripts(msg, tx, idx, sigScript, pkScript) } func signBadAndCheck(msg string, tx *wire.MsgTx, idx int, pkScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB, suite dcrec.SignatureType) error { // Setup a PRNG. randScriptHash := chainhash.HashB(pkScript) tRand := mrand.New(mrand.NewSource(int64(randScriptHash[0]))) sigScript, err := SignTxOutput(testingParams, tx, idx, pkScript, hashType, kdb, sdb, nil, suite) if err != nil { return fmt.Errorf("failed to sign output %s: %v", msg, err) } // Be sure to reset the value in when we're done creating the // corrupted signature for that flag. tx.TxIn[0].ValueIn = testValueIn // Corrupt a random bit in the signature. pos := tRand.Intn(len(sigScript) - 1) bitPos := tRand.Intn(7) sigScript[pos] ^= 1 << uint8(bitPos) return checkScripts(msg, tx, idx, sigScript, pkScript) } func TestSignTxOutput(t *testing.T) { t.Parallel() // make key // make script based on key. // sign with magic pixie dust. hashTypes := []SigHashType{ SigHashAll, SigHashNone, SigHashSingle, SigHashAll | SigHashAnyOneCanPay, SigHashNone | SigHashAnyOneCanPay, SigHashSingle | SigHashAnyOneCanPay, } signatureSuites := []dcrec.SignatureType{ dcrec.STEcdsaSecp256k1, dcrec.STEd25519, dcrec.STSchnorrSecp256k1, } tx := &wire.MsgTx{ SerType: wire.TxSerializeFull, Version: 1, TxIn: []*wire.TxIn{ { PreviousOutPoint: wire.OutPoint{ Hash: chainhash.Hash{}, Index: 0, Tree: 0, }, Sequence: 4294967295, ValueIn: testValueIn, BlockHeight: 78901, BlockIndex: 23456, }, { PreviousOutPoint: wire.OutPoint{ Hash: chainhash.Hash{}, Index: 1, Tree: 0, }, Sequence: 4294967295, ValueIn: testValueIn, BlockHeight: 78901, BlockIndex: 23456, }, { PreviousOutPoint: wire.OutPoint{ Hash: chainhash.Hash{}, Index: 2, Tree: 0, }, Sequence: 4294967295, ValueIn: testValueIn, BlockHeight: 78901, BlockIndex: 23456, }, }, TxOut: []*wire.TxOut{ { Version: wire.DefaultPkScriptVersion, Value: 1, }, { Version: wire.DefaultPkScriptVersion, Value: 2, }, { Version: wire.DefaultPkScriptVersion, Value: 3, }, }, LockTime: 0, Expiry: 0, } // Pay to Pubkey Hash (uncompressed) secp256k1 := chainec.Secp256k1 for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeUncompressed() case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeUncompressed() case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey( rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.Serialize() } msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) address, err := dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(pkBytes), testingParams, suite) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } if err := signAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(nil), suite); err != nil { t.Error(err) break } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(nil), suite); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } } // Pay to Pubkey Hash (uncompressed) (merging with correct) for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeUncompressed() case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeUncompressed() case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.Serialize() } msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) address, err := dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(pkBytes), testingParams, suite) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } sigScript, err := SignTxOutput( testingParams, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(nil), nil, suite) if err != nil { t.Errorf("failed to sign output %s: %v", msg, err) break } // by the above loop, this should be valid, now sign // again and merge. sigScript, err = SignTxOutput( testingParams, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(nil), sigScript, suite) if err != nil { t.Errorf("failed to sign output %s a "+ "second time: %v", msg, err) break } err = checkScripts(msg, tx, i, sigScript, pkScript) if err != nil { t.Errorf("twice signed script invalid for "+ "%s: %v", msg, err) break } } } } // Pay to Pubkey Hash (compressed) for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() } msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) address, err := dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(pkBytes), testingParams, suite) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } if err := signAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), suite); err != nil { t.Error(err) break } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), suite); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } } // Pay to Pubkey Hash (compressed) with duplicate merge for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() } msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) address, err := dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(pkBytes), testingParams, suite) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } sigScript, err := SignTxOutput(testingParams, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), nil, suite) if err != nil { t.Errorf("failed to sign output %s: %v", msg, err) break } // by the above loop, this should be valid, now sign // again and merge. sigScript, err = SignTxOutput(testingParams, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), sigScript, suite) if err != nil { t.Errorf("failed to sign output %s a "+ "second time: %v", msg, err) break } err = checkScripts(msg, tx, i, sigScript, pkScript) if err != nil { t.Errorf("twice signed script invalid for "+ "%s: %v", msg, err) break } } } } // Pay to Pubkey Hash for a ticket(SStx) (compressed) for _, hashType := range hashTypes { for i := range tx.TxIn { msg := fmt.Sprintf("%d:%d", hashType, i) keyDB, _, _, err := secp256k1.GenerateKey(rand.Reader) if err != nil { t.Errorf("failed to generate key: %v", err) break } key, pk := secp256k1.PrivKeyFromBytes(keyDB) pkBytes := pk.SerializeCompressed() address, err := dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(pkBytes), testingParams, dcrec.STEcdsaSecp256k1) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToSStx(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } if err := signAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), dcrec.STEcdsaSecp256k1); err != nil { t.Error(err) break } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), dcrec.STEcdsaSecp256k1); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } // Pay to Pubkey Hash for a ticket change (SStx change) (compressed) for _, hashType := range hashTypes { for i := range tx.TxIn { msg := fmt.Sprintf("%d:%d", hashType, i) keyDB, _, _, err := secp256k1.GenerateKey(rand.Reader) if err != nil { t.Errorf("failed to generate key: %v", err) break } key, pk := secp256k1.PrivKeyFromBytes(keyDB) pkBytes := pk.SerializeCompressed() address, err := dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(pkBytes), testingParams, dcrec.STEcdsaSecp256k1) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToSStxChange(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } if err := signAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), dcrec.STEcdsaSecp256k1); err != nil { t.Error(err) break } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), dcrec.STEcdsaSecp256k1); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } // Pay to Pubkey Hash for a ticket spending (SSGen) (compressed) for _, hashType := range hashTypes { for i := range tx.TxIn { msg := fmt.Sprintf("%d:%d", hashType, i) keyDB, _, _, err := secp256k1.GenerateKey(rand.Reader) if err != nil { t.Errorf("failed to generate key: %v", err) break } key, pk := secp256k1.PrivKeyFromBytes(keyDB) pkBytes := pk.SerializeCompressed() address, err := dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(pkBytes), testingParams, dcrec.STEcdsaSecp256k1) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToSSGen(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } if err := signAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), dcrec.STEcdsaSecp256k1); err != nil { t.Error(err) break } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), dcrec.STEcdsaSecp256k1); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } // Pay to Pubkey Hash for a ticket revocation (SSRtx) (compressed) for _, hashType := range hashTypes { for i := range tx.TxIn { msg := fmt.Sprintf("%d:%d", hashType, i) keyDB, _, _, err := secp256k1.GenerateKey(rand.Reader) if err != nil { t.Errorf("failed to generate key: %v", err) break } key, pk := secp256k1.PrivKeyFromBytes(keyDB) pkBytes := pk.SerializeCompressed() address, err := dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(pkBytes), testingParams, dcrec.STEcdsaSecp256k1) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToSSRtx(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } if err := signAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), dcrec.STEcdsaSecp256k1); err != nil { t.Error(err) break } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), dcrec.STEcdsaSecp256k1); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } // Pay to PubKey (uncompressed) for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) keyDB, _, _, err := secp256k1.GenerateKey(rand.Reader) if err != nil { t.Errorf("failed to generate key: %v", err) break } key, pk := secp256k1.PrivKeyFromBytes(keyDB) // For address generation, consensus rules require using // a compressed public key. Look up ExtractPkScriptAddrs // for more details address, err := dcrutil.NewAddressSecpPubKeyCompressed(pk, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } if err := signAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(nil), suite); err != nil { t.Error(err) break } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(nil), suite); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } } // Pay to PubKey (uncompressed) for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey var address dcrutil.Address var err error msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) // For address generation, consensus rules require using // a compressed public key. Look up ExtractPkScriptAddrs // for more details address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeUncompressed() address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.Serialize() address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } sigScript, err := SignTxOutput(testingParams, tx, i, pkScript, hashType, mkGetKeyPub(map[string]addressToKey{ address.String(): {&key, false}, }), mkGetScript(nil), nil, suite) if err != nil { t.Errorf("failed to sign output %s: %v", msg, err) } // by the above loop, this should be valid, now sign // again and merge. sigScript, err = SignTxOutput(testingParams, tx, i, pkScript, hashType, mkGetKeyPub(map[string]addressToKey{ address.String(): {&key, false}, }), mkGetScript(nil), sigScript, suite) if err != nil { t.Errorf("failed to sign output %s a "+ "second time: %v", msg, err) } err = checkScripts(msg, tx, i, sigScript, pkScript) if err != nil { t.Errorf("twice signed script invalid for "+ "%s: %v", msg, err) } } } } // Pay to PubKey (compressed) for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey var address dcrutil.Address var err error msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) // For address generation, consensus rules require using // a compressed public key. Look up ExtractPkScriptAddrs // for more details address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.Serialize() address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } if err := signAndCheck(msg, tx, i, pkScript, hashType, mkGetKeyPub(map[string]addressToKey{ address.String(): {&key, true}, }), mkGetScript(nil), suite); err != nil { t.Error(err) break } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKeyPub(map[string]addressToKey{ address.String(): {&key, true}, }), mkGetScript(nil), suite); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } } // Pay to PubKey (compressed) with duplicate merge for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey var address dcrutil.Address var err error msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.Serialize() address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } sigScript, err := SignTxOutput(testingParams, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), nil, suite) if err != nil { t.Errorf("failed to sign output %s: %v", msg, err) break } // by the above loop, this should be valid, now sign // again and merge. sigScript, err = SignTxOutput(testingParams, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), sigScript, suite) if err != nil { t.Errorf("failed to sign output %s a "+ "second time: %v", msg, err) break } err = checkScripts(msg, tx, i, sigScript, pkScript) if err != nil { t.Errorf("twice signed script invalid for "+ "%s: %v", msg, err) break } } } } // As before, but with p2sh now. // Pay to Pubkey Hash (uncompressed) for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeUncompressed() case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeUncompressed() case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.Serialize() } msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) address, err := dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(pkBytes), testingParams, suite) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) break } scriptAddr, err := dcrutil.NewAddressScriptHash( pkScript, testingParams) if err != nil { t.Errorf("failed to make p2sh addr for %s: %v", msg, err) break } scriptPkScript, err := PayToAddrScript( scriptAddr) if err != nil { t.Errorf("failed to make script pkscript for "+ "%s: %v", msg, err) break } if err := signAndCheck(msg, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), suite); err != nil { t.Error(err) break } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(nil), suite); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } } // Pay to Pubkey Hash (uncompressed) with duplicate merge for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeUncompressed() case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeUncompressed() case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.Serialize() } msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) address, err := dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(pkBytes), testingParams, suite) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) break } scriptAddr, err := dcrutil.NewAddressScriptHash( pkScript, testingParams) if err != nil { t.Errorf("failed to make p2sh addr for %s: %v", msg, err) break } scriptPkScript, err := PayToAddrScript( scriptAddr) if err != nil { t.Errorf("failed to make script pkscript for "+ "%s: %v", msg, err) break } _, err = SignTxOutput(testingParams, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), nil, suite) if err != nil { t.Errorf("failed to sign output %s: %v", msg, err) break } // by the above loop, this should be valid, now sign // again and merge. sigScript, err := SignTxOutput(testingParams, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), nil, suite) if err != nil { t.Errorf("failed to sign output %s a "+ "second time: %v", msg, err) break } err = checkScripts(msg, tx, i, sigScript, scriptPkScript) if err != nil { t.Errorf("twice signed script invalid for "+ "%s: %v", msg, err) break } } } } // Pay to Pubkey Hash (compressed) for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() } msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) address, err := dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(pkBytes), testingParams, suite) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } scriptAddr, err := dcrutil.NewAddressScriptHash( pkScript, testingParams) if err != nil { t.Errorf("failed to make p2sh addr for %s: %v", msg, err) break } scriptPkScript, err := PayToAddrScript( scriptAddr) if err != nil { t.Errorf("failed to make script pkscript for "+ "%s: %v", msg, err) break } if err := signAndCheck(msg, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), suite); err != nil { t.Error(err) break } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(nil), suite); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } } // Pay to Pubkey Hash (compressed) with duplicate merge for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() } msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) address, err := dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(pkBytes), testingParams, suite) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } scriptAddr, err := dcrutil.NewAddressScriptHash( pkScript, testingParams) if err != nil { t.Errorf("failed to make p2sh addr for %s: %v", msg, err) break } scriptPkScript, err := PayToAddrScript( scriptAddr) if err != nil { t.Errorf("failed to make script pkscript for "+ "%s: %v", msg, err) break } _, err = SignTxOutput(testingParams, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), nil, suite) if err != nil { t.Errorf("failed to sign output %s: %v", msg, err) break } // by the above loop, this should be valid, now sign // again and merge. sigScript, err := SignTxOutput(testingParams, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), nil, suite) if err != nil { t.Errorf("failed to sign output %s a "+ "second time: %v", msg, err) break } err = checkScripts(msg, tx, i, sigScript, scriptPkScript) if err != nil { t.Errorf("twice signed script invalid for "+ "%s: %v", msg, err) break } } } } // Pay to PubKey (uncompressed) for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey var address dcrutil.Address var err error msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) // For address generation, consensus rules require using // a compressed public key. Look up ExtractPkScriptAddrs // for more details address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeUncompressed() address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.Serialize() address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } scriptAddr, err := dcrutil.NewAddressScriptHash( pkScript, testingParams) if err != nil { t.Errorf("failed to make p2sh addr for %s: %v", msg, err) } scriptPkScript, err := PayToAddrScript( scriptAddr) if err != nil { t.Errorf("failed to make script pkscript for "+ "%s: %v", msg, err) } if err := signAndCheck(msg, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), suite); err != nil { t.Error(err) } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(nil), suite); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } } // Pay to PubKey (uncompressed) with duplicate merge for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey var address dcrutil.Address var err error msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) // For address generation, consensus rules require using // a compressed public key. Look up ExtractPkScriptAddrs // for more details address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeUncompressed() address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.Serialize() address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } scriptAddr, err := dcrutil.NewAddressScriptHash( pkScript, testingParams) if err != nil { t.Errorf("failed to make p2sh addr for %s: %v", msg, err) } scriptPkScript, err := PayToAddrScript( scriptAddr) if err != nil { t.Errorf("failed to make script pkscript for "+ "%s: %v", msg, err) } _, err = SignTxOutput(testingParams, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), nil, suite) if err != nil { t.Errorf("failed to sign output %s: %v", msg, err) break } // by the above loop, this should be valid, now sign // again and merge. sigScript, err := SignTxOutput(testingParams, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), nil, suite) if err != nil { t.Errorf("failed to sign output %s a "+ "second time: %v", msg, err) break } err = checkScripts(msg, tx, i, sigScript, scriptPkScript) if err != nil { t.Errorf("twice signed script invalid for "+ "%s: %v", msg, err) break } } } } // Pay to PubKey (compressed) for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey var address dcrutil.Address var err error msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.Serialize() address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } scriptAddr, err := dcrutil.NewAddressScriptHash( pkScript, testingParams) if err != nil { t.Errorf("failed to make p2sh addr for %s: %v", msg, err) break } scriptPkScript, err := PayToAddrScript( scriptAddr) if err != nil { t.Errorf("failed to make script pkscript for "+ "%s: %v", msg, err) break } if err := signAndCheck(msg, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), suite); err != nil { t.Error(err) break } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, false}, }), mkGetScript(nil), suite); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } } // Pay to PubKey (compressed) for _, hashType := range hashTypes { for _, suite := range signatureSuites { for i := range tx.TxIn { var keyDB, pkBytes []byte var key chainec.PrivateKey var pk chainec.PublicKey var address dcrutil.Address var err error msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite) switch suite { case dcrec.STEcdsaSecp256k1: keyDB, _, _, _ = secp256k1.GenerateKey(rand.Reader) key, pk = secp256k1.PrivKeyFromBytes(keyDB) address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STEd25519: keyDB, _, _, _ = chainec.Edwards.GenerateKey(rand.Reader) key, pk = chainec.Edwards.PrivKeyFromBytes(keyDB) pkBytes = pk.SerializeCompressed() address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } case dcrec.STSchnorrSecp256k1: keyDB, _, _, _ = chainec.SecSchnorr.GenerateKey(rand.Reader) key, pk = chainec.SecSchnorr.PrivKeyFromBytes(keyDB) pkBytes = pk.Serialize() address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) } } pkScript, err := PayToAddrScript(address) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } scriptAddr, err := dcrutil.NewAddressScriptHash( pkScript, testingParams) if err != nil { t.Errorf("failed to make p2sh addr for %s: %v", msg, err) break } scriptPkScript, err := PayToAddrScript( scriptAddr) if err != nil { t.Errorf("failed to make script pkscript for "+ "%s: %v", msg, err) break } _, err = SignTxOutput(testingParams, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), nil, suite) if err != nil { t.Errorf("failed to sign output %s: %v", msg, err) break } // by the above loop, this should be valid, now sign // again and merge. sigScript, err := SignTxOutput(testingParams, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address.Address(): {&key, true}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), nil, suite) if err != nil { t.Errorf("failed to sign output %s a "+ "second time: %v", msg, err) break } err = checkScripts(msg, tx, i, sigScript, scriptPkScript) if err != nil { t.Errorf("twice signed script invalid for "+ "%s: %v", msg, err) break } } } } // Basic Multisig for _, hashType := range hashTypes { for i := range tx.TxIn { msg := fmt.Sprintf("%d:%d", hashType, i) keyDB1, _, _, err := secp256k1.GenerateKey(rand.Reader) if err != nil { t.Errorf("failed to generate key: %v", err) break } key1, pk1 := secp256k1.PrivKeyFromBytes(keyDB1) address1, err := dcrutil.NewAddressSecpPubKeyCompressed(pk1, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } keyDB2, _, _, err := secp256k1.GenerateKey(rand.Reader) if err != nil { t.Errorf("failed to generate key: %v", err) break } key2, pk2 := secp256k1.PrivKeyFromBytes(keyDB2) address2, err := dcrutil.NewAddressSecpPubKeyCompressed(pk2, testingParams) if err != nil { t.Errorf("failed to make address 2 for %s: %v", msg, err) break } pkScript, err := MultiSigScript( []*dcrutil.AddressSecpPubKey{address1, address2}, 2) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } scriptAddr, err := dcrutil.NewAddressScriptHash( pkScript, testingParams) if err != nil { t.Errorf("failed to make p2sh addr for %s: %v", msg, err) break } scriptPkScript, err := PayToAddrScript(scriptAddr) if err != nil { t.Errorf("failed to make script pkscript for "+ "%s: %v", msg, err) break } if err := signAndCheck(msg, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address1.Address(): {&key1, true}, address2.Address(): {&key2, true}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), dcrec.STEcdsaSecp256k1); err != nil { t.Error(err) break } if err := signBadAndCheck(msg, tx, i, pkScript, hashType, mkGetKey(map[string]addressToKey{ address1.Address(): {&key1, true}, address2.Address(): {&key2, true}, }), mkGetScript(nil), dcrec.STEcdsaSecp256k1); err == nil { t.Errorf("corrupted signature validated %s: %v", msg, err) break } } } // Two part multisig, sign with one key then the other. for _, hashType := range hashTypes { for i := range tx.TxIn { msg := fmt.Sprintf("%d:%d", hashType, i) keyDB1, _, _, err := secp256k1.GenerateKey(rand.Reader) if err != nil { t.Errorf("failed to generate key: %v", err) break } key1, pk1 := secp256k1.PrivKeyFromBytes(keyDB1) address1, err := dcrutil.NewAddressSecpPubKeyCompressed(pk1, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } keyDB2, _, _, err := secp256k1.GenerateKey(rand.Reader) if err != nil { t.Errorf("failed to generate key: %v", err) break } key2, pk2 := secp256k1.PrivKeyFromBytes(keyDB2) address2, err := dcrutil.NewAddressSecpPubKeyCompressed(pk2, testingParams) if err != nil { t.Errorf("failed to make address 2 for %s: %v", msg, err) break } pkScript, err := MultiSigScript( []*dcrutil.AddressSecpPubKey{address1, address2}, 2) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } scriptAddr, err := dcrutil.NewAddressScriptHash( pkScript, testingParams) if err != nil { t.Errorf("failed to make p2sh addr for %s: %v", msg, err) break } scriptPkScript, err := PayToAddrScript(scriptAddr) if err != nil { t.Errorf("failed to make script pkscript for "+ "%s: %v", msg, err) break } sigScript, err := SignTxOutput(testingParams, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address1.Address(): {&key1, true}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), nil, dcrec.STEcdsaSecp256k1) if err != nil { t.Errorf("failed to sign output %s: %v", msg, err) break } // Only 1 out of 2 signed, this *should* fail. if checkScripts(msg, tx, i, sigScript, scriptPkScript) == nil { t.Errorf("part signed script valid for %s", msg) break } // Sign with the other key and merge sigScript, err = SignTxOutput(testingParams, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address2.Address(): {&key2, true}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), sigScript, dcrec.STEcdsaSecp256k1) if err != nil { t.Errorf("failed to sign output %s: %v", msg, err) break } err = checkScripts(msg, tx, i, sigScript, scriptPkScript) if err != nil { t.Errorf("fully signed script invalid for "+ "%s: %v", msg, err) break } } } // Two part multisig, sign with one key then both, check key dedup // correctly. for _, hashType := range hashTypes { for i := range tx.TxIn { msg := fmt.Sprintf("%d:%d", hashType, i) keyDB1, _, _, err := secp256k1.GenerateKey(rand.Reader) if err != nil { t.Errorf("failed to generate key: %v", err) break } key1, pk1 := secp256k1.PrivKeyFromBytes(keyDB1) address1, err := dcrutil.NewAddressSecpPubKeyCompressed(pk1, testingParams) if err != nil { t.Errorf("failed to make address for %s: %v", msg, err) break } keyDB2, _, _, err := secp256k1.GenerateKey(rand.Reader) if err != nil { t.Errorf("failed to generate key: %v", err) break } key2, pk2 := secp256k1.PrivKeyFromBytes(keyDB2) address2, err := dcrutil.NewAddressSecpPubKeyCompressed(pk2, testingParams) if err != nil { t.Errorf("failed to make address 2 for %s: %v", msg, err) break } pkScript, err := MultiSigScript( []*dcrutil.AddressSecpPubKey{address1, address2}, 2) if err != nil { t.Errorf("failed to make pkscript "+ "for %s: %v", msg, err) } scriptAddr, err := dcrutil.NewAddressScriptHash( pkScript, testingParams) if err != nil { t.Errorf("failed to make p2sh addr for %s: %v", msg, err) break } scriptPkScript, err := PayToAddrScript(scriptAddr) if err != nil { t.Errorf("failed to make script pkscript for "+ "%s: %v", msg, err) break } sigScript, err := SignTxOutput(testingParams, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address1.Address(): {&key1, true}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), nil, dcrec.STEcdsaSecp256k1) if err != nil { t.Errorf("failed to sign output %s: %v", msg, err) break } // Only 1 out of 2 signed, this *should* fail. if checkScripts(msg, tx, i, sigScript, scriptPkScript) == nil { t.Errorf("part signed script valid for %s", msg) break } // Sign with the other key and merge sigScript, err = SignTxOutput(testingParams, tx, i, scriptPkScript, hashType, mkGetKey(map[string]addressToKey{ address1.Address(): {&key1, true}, address2.Address(): {&key2, true}, }), mkGetScript(map[string][]byte{ scriptAddr.Address(): pkScript, }), sigScript, dcrec.STEcdsaSecp256k1) if err != nil { t.Errorf("failed to sign output %s: %v", msg, err) break } // Now we should pass. err = checkScripts(msg, tx, i, sigScript, scriptPkScript) if err != nil { t.Errorf("fully signed script invalid for "+ "%s: %v", msg, err) break } } } } type tstInput struct { txout *wire.TxOut sigscriptGenerates bool inputValidates bool indexOutOfRange bool } type tstSigScript struct { name string inputs []tstInput hashType SigHashType compress bool scriptAtWrongIndex bool } var coinbaseOutPoint = &wire.OutPoint{ Index: (1 << 32) - 1, } // Pregenerated private key, with associated public key and pkScripts // for the uncompressed and compressed hash160. var ( privKeyD = []byte{0x6b, 0x0f, 0xd8, 0xda, 0x54, 0x22, 0xd0, 0xb7, 0xb4, 0xfc, 0x4e, 0x55, 0xd4, 0x88, 0x42, 0xb3, 0xa1, 0x65, 0xac, 0x70, 0x7f, 0x3d, 0xa4, 0x39, 0x5e, 0xcb, 0x3b, 0xb0, 0xd6, 0x0e, 0x06, 0x92} _, thisPubKey = chainec.Secp256k1.PrivKeyFromBytes(privKeyD) thisAddressUnc, _ = dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(thisPubKey.SerializeUncompressed()), testingParams, dcrec.STEcdsaSecp256k1) uncompressedPkScript, _ = PayToAddrScript(thisAddressUnc) thisAddressCom, _ = dcrutil.NewAddressPubKeyHash( dcrutil.Hash160(thisPubKey.SerializeCompressed()), testingParams, dcrec.STEcdsaSecp256k1) compressedPkScript, _ = PayToAddrScript(thisAddressCom) shortPkScript = []byte{0x76, 0xa9, 0x14, 0xd1, 0x7c, 0xb5, 0xeb, 0xa4, 0x02, 0xcb, 0x68, 0xe0, 0x69, 0x56, 0xbf, 0x32, 0x53, 0x90, 0x0e, 0x0a, 0x88, 0xac} ) // Pretend output amounts. const coinbaseVal = 2500000000 const fee = 5000000 var sigScriptTests = []tstSigScript{ { name: "one input uncompressed", inputs: []tstInput{ { txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, }, hashType: SigHashAll, compress: false, scriptAtWrongIndex: false, }, { name: "two inputs uncompressed", inputs: []tstInput{ { txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, { txout: wire.NewTxOut(coinbaseVal+fee, uncompressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, }, hashType: SigHashAll, compress: false, scriptAtWrongIndex: false, }, { name: "one input compressed", inputs: []tstInput{ { txout: wire.NewTxOut(coinbaseVal, compressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, }, hashType: SigHashAll, compress: true, scriptAtWrongIndex: false, }, { name: "two inputs compressed", inputs: []tstInput{ { txout: wire.NewTxOut(coinbaseVal, compressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, { txout: wire.NewTxOut(coinbaseVal+fee, compressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, }, hashType: SigHashAll, compress: true, scriptAtWrongIndex: false, }, { name: "hashType SigHashNone", inputs: []tstInput{ { txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, }, hashType: SigHashNone, compress: false, scriptAtWrongIndex: false, }, { name: "hashType SigHashSingle", inputs: []tstInput{ { txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, }, hashType: SigHashSingle, compress: false, scriptAtWrongIndex: false, }, { name: "hashType SigHashAnyoneCanPay", inputs: []tstInput{ { txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, }, hashType: SigHashAnyOneCanPay | SigHashAll, compress: false, scriptAtWrongIndex: false, }, { name: "hashType non-standard", inputs: []tstInput{ { txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), sigscriptGenerates: true, inputValidates: false, indexOutOfRange: false, }, }, hashType: 0x04, compress: false, scriptAtWrongIndex: false, }, { name: "invalid compression", inputs: []tstInput{ { txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), sigscriptGenerates: true, inputValidates: false, indexOutOfRange: false, }, }, hashType: SigHashAll, compress: true, scriptAtWrongIndex: false, }, { name: "short PkScript", inputs: []tstInput{ { txout: wire.NewTxOut(coinbaseVal, shortPkScript), sigscriptGenerates: false, indexOutOfRange: false, }, }, hashType: SigHashAll, compress: false, scriptAtWrongIndex: false, }, { name: "valid script at wrong index", inputs: []tstInput{ { txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, { txout: wire.NewTxOut(coinbaseVal+fee, uncompressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, }, hashType: SigHashAll, compress: false, scriptAtWrongIndex: true, }, { name: "index out of range", inputs: []tstInput{ { txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, { txout: wire.NewTxOut(coinbaseVal+fee, uncompressedPkScript), sigscriptGenerates: true, inputValidates: true, indexOutOfRange: false, }, }, hashType: SigHashAll, compress: false, scriptAtWrongIndex: true, }, } // Test the sigscript generation for valid and invalid inputs, all // hashTypes, and with and without compression. This test creates // sigscripts to spend fake coinbase inputs, as sigscripts cannot be // created for the MsgTxs in txTests, since they come from the blockchain // and we don't have the private keys. func TestSignatureScript(t *testing.T) { t.Parallel() privKey, _ := chainec.Secp256k1.PrivKeyFromBytes(privKeyD) nexttest: for i := range sigScriptTests { tx := wire.NewMsgTx() output := wire.NewTxOut(500, []byte{OP_RETURN}) tx.AddTxOut(output) for range sigScriptTests[i].inputs { txin := wire.NewTxIn(coinbaseOutPoint, 500, nil) tx.AddTxIn(txin) } var script []byte var err error for j := range tx.TxIn { var idx int if sigScriptTests[i].inputs[j].indexOutOfRange { t.Errorf("at test %v", sigScriptTests[i].name) idx = len(sigScriptTests[i].inputs) } else { idx = j } script, err = SignatureScript(tx, idx, sigScriptTests[i].inputs[j].txout.PkScript, sigScriptTests[i].hashType, privKey, sigScriptTests[i].compress) if (err == nil) != sigScriptTests[i].inputs[j].sigscriptGenerates { if err == nil { t.Errorf("passed test '%v' incorrectly", sigScriptTests[i].name) } else { t.Errorf("failed test '%v': %v", sigScriptTests[i].name, err) } continue nexttest } if !sigScriptTests[i].inputs[j].sigscriptGenerates { // done with this test continue nexttest } tx.TxIn[j].SignatureScript = script } // If testing using a correct sigscript but for an incorrect // index, use last input script for first input. Requires > 0 // inputs for test. if sigScriptTests[i].scriptAtWrongIndex { tx.TxIn[0].SignatureScript = script sigScriptTests[i].inputs[0].inputValidates = false } // Validate tx input scripts var scriptFlags ScriptFlags for j := range tx.TxIn { vm, err := NewEngine(sigScriptTests[i].inputs[j].txout. PkScript, tx, j, scriptFlags, 0, nil) if err != nil { t.Errorf("cannot create script vm for test %v: %v", sigScriptTests[i].name, err) continue nexttest } err = vm.Execute() if (err == nil) != sigScriptTests[i].inputs[j].inputValidates { if err == nil { t.Errorf("passed test '%v' validation incorrectly: %v", sigScriptTests[i].name, err) } else { t.Errorf("failed test '%v' validation: %v", sigScriptTests[i].name, err) } continue nexttest } } } }