// prepare a signed transaction func make_signed_tx() { // Make an empty transaction tx := new(btc.Tx) tx.Version = 1 tx.Lock_time = 0 // Select as many inputs as we need to pay the full amount (with the fee) var btcsofar uint64 for i := range unspentOuts { if unspentOuts[i].key == nil { continue } uo := getUO(&unspentOuts[i].TxPrevOut) // add the input to our transaction: tin := new(btc.TxIn) tin.Input = unspentOuts[i].TxPrevOut tin.Sequence = uint32(*sequence) tx.TxIn = append(tx.TxIn, tin) btcsofar += uo.Value unspentOuts[i].spent = true if !*useallinputs && (btcsofar >= spendBtc+feeBtc) { break } } if btcsofar < (spendBtc + feeBtc) { fmt.Println("ERROR: You have", btc.UintToBtc(btcsofar), "BTC, but you need", btc.UintToBtc(spendBtc+feeBtc), "BTC for the transaction") cleanExit(1) } changeBtc = btcsofar - (spendBtc + feeBtc) if *verbose { fmt.Printf("Spending %d out of %d outputs...\n", len(tx.TxIn), len(unspentOuts)) } // Build transaction outputs: for o := range sendTo { outs, er := btc.NewSpendOutputs(sendTo[o].addr, sendTo[o].amount, testnet) if er != nil { fmt.Println("ERROR:", er.Error()) cleanExit(1) } tx.TxOut = append(tx.TxOut, outs...) } if changeBtc > 0 { // Add one more output (with the change) chad := get_change_addr() if *verbose { fmt.Println("Sending change", changeBtc, "to", chad.String()) } outs, er := btc.NewSpendOutputs(chad, changeBtc, testnet) if er != nil { fmt.Println("ERROR:", er.Error()) cleanExit(1) } tx.TxOut = append(tx.TxOut, outs...) } if *message != "" { // Add NULL output with an arbitrary message scr := new(bytes.Buffer) scr.WriteByte(0x6a) // OP_RETURN btc.WritePutLen(scr, uint32(len(*message))) scr.Write([]byte(*message)) tx.TxOut = append(tx.TxOut, &btc.TxOut{Value: 0, Pk_script: scr.Bytes()}) } signed := sign_tx(tx) write_tx_file(tx) if apply2bal && signed { apply_to_balance(tx) } }
// prepare a signed transaction func make_signed_tx() { // Make an empty transaction tx := new(btc.Tx) tx.Version = 1 tx.Lock_time = 0 // Select as many inputs as we need to pay the full amount (with the fee) var btcsofar uint64 for inpcnt := 0; inpcnt < len(unspentOuts); inpcnt++ { uo := UO(unspentOuts[inpcnt]) // add the input to our transaction: tin := new(btc.TxIn) tin.Input = *unspentOuts[inpcnt] tin.Sequence = 0xffffffff tx.TxIn = append(tx.TxIn, tin) btcsofar += uo.Value if !*useallinputs && (btcsofar >= spendBtc+feeBtc) { break } } changeBtc = btcsofar - (spendBtc + feeBtc) if *verbose { fmt.Printf("Spending %d out of %d outputs...\n", len(tx.TxIn), len(unspentOuts)) } // Build transaction outputs: for o := range sendTo { outs, er := btc.NewSpendOutputs(sendTo[o].addr, sendTo[o].amount, testnet) if er != nil { fmt.Println("ERROR:", er.Error()) os.Exit(1) } tx.TxOut = append(tx.TxOut, outs...) } if changeBtc > 0 { // Add one more output (with the change) chad := get_change_addr() if *verbose { fmt.Println("Sending change", changeBtc, "to", chad.String()) } outs, er := btc.NewSpendOutputs(chad, changeBtc, testnet) if er != nil { fmt.Println("ERROR:", er.Error()) os.Exit(1) } tx.TxOut = append(tx.TxOut, outs...) } if *message != "" { // Add NULL output with an arbitrary message scr := new(bytes.Buffer) scr.WriteByte(0x6a) // OP_RETURN btc.WritePutLen(scr, uint32(len(*message))) scr.Write([]byte(*message)) tx.TxOut = append(tx.TxOut, &btc.TxOut{Value: 0, Pk_script: scr.Bytes()}) } if *hashes { dump_hashes_to_sign(tx) } else { signed := sign_tx(tx) write_tx_file(tx) if apply2bal && signed { apply_to_balance(tx) } } }
func VerifyTxScript(pkScr []byte, amount uint64, i int, tx *btc.Tx, ver_flags uint32) (result bool) { if VerifyConsensus != nil { defer func() { // We call CompareToConsensus inside another function to wait for final "result" VerifyConsensus(pkScr, amount, i, tx, ver_flags, result) }() } sigScr := tx.TxIn[i].ScriptSig if (ver_flags&VER_SIGPUSHONLY) != 0 && !btc.IsPushOnly(sigScr) { if DBG_ERR { fmt.Println("Not push only") } return false } if DBG_SCR { fmt.Println("VerifyTxScript", tx.Hash.String(), i+1, "/", len(tx.TxIn)) fmt.Println("sigScript:", hex.EncodeToString(sigScr[:])) fmt.Println("_pkScript:", hex.EncodeToString(pkScr)) fmt.Printf("flagz:%x\n", ver_flags) } var stack, stackCopy scrStack if !evalScript(sigScr, amount, &stack, tx, i, ver_flags, SIGVERSION_BASE) { if DBG_ERR { if tx != nil { fmt.Println("VerifyTxScript", tx.Hash.String(), i+1, "/", len(tx.TxIn)) } fmt.Println("sigScript failed :", hex.EncodeToString(sigScr[:])) fmt.Println("pkScript:", hex.EncodeToString(pkScr[:])) } return } if DBG_SCR { fmt.Println("\nsigScr verified OK") //stack.print() fmt.Println() } if (ver_flags&VER_P2SH) != 0 && stack.size() > 0 { // copy the stack content to stackCopy stackCopy.copy_from(&stack) } if !evalScript(pkScr, amount, &stack, tx, i, ver_flags, SIGVERSION_BASE) { if DBG_SCR { fmt.Println("* pkScript failed :", hex.EncodeToString(pkScr[:])) fmt.Println("* VerifyTxScript", tx.Hash.String(), i+1, "/", len(tx.TxIn)) fmt.Println("* sigScript:", hex.EncodeToString(sigScr[:])) } return } if stack.size() == 0 { if DBG_SCR { fmt.Println("* stack empty after executing scripts:", hex.EncodeToString(pkScr[:])) } return } if !stack.topBool(-1) { if DBG_SCR { fmt.Println("* FALSE on stack after executing scripts:", hex.EncodeToString(pkScr[:])) } return } // Bare witness programs var witnessversion int var witnessprogram []byte var hadWitness bool var witness witness_ctx if (ver_flags & VER_WITNESS) != 0 { if tx.SegWit != nil { for _, wd := range tx.SegWit[i] { witness.stack.push(wd) } } witnessversion, witnessprogram = btc.IsWitnessProgram(pkScr) if DBG_SCR { fmt.Println("------------witnessversion:", witnessversion, " witnessprogram:", hex.EncodeToString(witnessprogram)) } if witnessprogram != nil { hadWitness = true if len(sigScr) != 0 { if DBG_ERR { fmt.Println("SCRIPT_ERR_WITNESS_MALLEATED") } return } if !VerifyWitnessProgram(&witness, amount, tx, i, witnessversion, witnessprogram, ver_flags) { if DBG_ERR { fmt.Println("VerifyWitnessProgram failed A") } return false } // Bypass the cleanstack check at the end. The actual stack is obviously not clean // for witness programs. stack.resize(1) } else { if DBG_SCR { fmt.Println("No witness program") } } } else { if DBG_SCR { fmt.Println("Witness flag off") } } // Additional validation for spend-to-script-hash transactions: if (ver_flags&VER_P2SH) != 0 && btc.IsPayToScript(pkScr) { if DBG_SCR { fmt.Println() fmt.Println() fmt.Println(" ******************* Looks like P2SH script ********************* ") stack.print() } if DBG_SCR { fmt.Println("sigScr len", len(sigScr), hex.EncodeToString(sigScr)) } if !btc.IsPushOnly(sigScr) { if DBG_ERR { fmt.Println("P2SH is not push only") } return } // Restore stack. stack = stackCopy pubKey2 := stack.pop() if DBG_SCR { fmt.Println("pubKey2:", hex.EncodeToString(pubKey2)) } if !evalScript(pubKey2, amount, &stack, tx, i, ver_flags, SIGVERSION_BASE) { if DBG_ERR { fmt.Println("P2SH extra verification failed") } return } if stack.size() == 0 { if DBG_SCR { fmt.Println("* P2SH stack empty after executing script:", hex.EncodeToString(pubKey2)) } return } if !stack.topBool(-1) { if DBG_SCR { fmt.Println("* FALSE on stack after executing P2SH script:", hex.EncodeToString(pubKey2)) } return } if (ver_flags & VER_WITNESS) != 0 { witnessversion, witnessprogram = btc.IsWitnessProgram(pubKey2) if DBG_SCR { fmt.Println("============witnessversion:", witnessversion, " witnessprogram:", hex.EncodeToString(witnessprogram)) } if witnessprogram != nil { hadWitness = true bt := new(bytes.Buffer) btc.WritePutLen(bt, uint32(len(pubKey2))) bt.Write(pubKey2) if !bytes.Equal(sigScr, bt.Bytes()) { if DBG_ERR { fmt.Println(hex.EncodeToString(sigScr)) fmt.Println(hex.EncodeToString(bt.Bytes())) fmt.Println("SCRIPT_ERR_WITNESS_MALLEATED_P2SH") } return } if !VerifyWitnessProgram(&witness, amount, tx, i, witnessversion, witnessprogram, ver_flags) { if DBG_ERR { fmt.Println("VerifyWitnessProgram failed B") } return false } // Bypass the cleanstack check at the end. The actual stack is obviously not clean // for witness programs. stack.resize(1) } } } if (ver_flags & VER_CLEANSTACK) != 0 { if (ver_flags & VER_P2SH) == 0 { panic("VER_CLEANSTACK without VER_P2SH") } if DBG_SCR { fmt.Println("stack size", stack.size()) } if stack.size() != 1 { if DBG_ERR { fmt.Println("Stack not clean") } return } } if (ver_flags & VER_WITNESS) != 0 { // We can't check for correct unexpected witness data if P2SH was off, so require // that WITNESS implies P2SH. Otherwise, going from WITNESS->P2SH+WITNESS would be // possible, which is not a softfork. if (ver_flags & VER_P2SH) == 0 { panic("VER_WITNESS must be used with P2SH") } if !hadWitness && !witness.IsNull() { if DBG_ERR { fmt.Println("SCRIPT_ERR_WITNESS_UNEXPECTED", len(tx.SegWit)) } return } } result = true return true }