diff --git a/cmd/txsigner/config.go b/cmd/txsigner/config.go new file mode 100644 index 000000000..d0346c3a5 --- /dev/null +++ b/cmd/txsigner/config.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + "github.com/daglabs/btcd/dagconfig" + "github.com/jessevdk/go-flags" + "os" +) + +var activeNetParams = &dagconfig.MainNetParams + +type config struct { + Transaction string `long:"transaction" short:"t" description:"Unsigned transaction in HEX format" required:"true"` + PrivateKey string `long:"private-key" short:"p" description:"Private key" required:"true"` + TestNet bool `long:"testnet" description:"Use the test network"` + RegressionTest bool `long:"regtest" description:"Use the regression test network"` + SimNet bool `long:"simnet" description:"Use the simulation test network"` + DevNet bool `long:"devnet" description:"Use the development test network"` +} + +func parseCommandLine() (*config, error) { + cfg := &config{} + parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag) + _, err := parser.Parse() + // Multiple networks can't be selected simultaneously. + funcName := "loadConfig" + numNets := 0 + // Count number of network flags passed; assign active network params + // while we're at it + if cfg.TestNet { + numNets++ + activeNetParams = &dagconfig.TestNetParams + } + if cfg.RegressionTest { + numNets++ + activeNetParams = &dagconfig.RegressionNetParams + } + if cfg.SimNet { + numNets++ + activeNetParams = &dagconfig.SimNetParams + } + if cfg.DevNet { + numNets++ + activeNetParams = &dagconfig.DevNetParams + } + if numNets > 1 { + str := "%s: The testnet, regtest, simnet and devent params can't be " + + "used together -- choose one of the four" + err := fmt.Errorf(str, funcName) + fmt.Fprintln(os.Stderr, err) + parser.WriteHelp(os.Stderr) + return nil, err + } + + return cfg, err +} diff --git a/cmd/txsigner/txsigner.go b/cmd/txsigner/txsigner.go new file mode 100644 index 000000000..55c191bcc --- /dev/null +++ b/cmd/txsigner/txsigner.go @@ -0,0 +1,88 @@ +package main + +import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/daglabs/btcd/btcec" + "github.com/daglabs/btcd/txscript" + "github.com/daglabs/btcd/util" + "github.com/daglabs/btcd/wire" + "os" +) + +func main() { + cfg, err := parseCommandLine() + if err != nil { + printErrorAndExit(err, "Failed to parse arguments") + } + + privateKey, err := parsePrivateKey(cfg.PrivateKey) + if err != nil { + printErrorAndExit(err, "Failed to decode private key") + } + + transaction, err := parseTransaction(cfg.Transaction) + if err != nil { + printErrorAndExit(err, "Failed to decode transaction") + } + + scriptPubKey, err := createScriptPubKey(privateKey.PubKey()) + if err != nil { + printErrorAndExit(err, "Failed to create scriptPubKey") + } + + err = signTransaction(transaction, privateKey, scriptPubKey) + if err != nil { + printErrorAndExit(err, "Failed to sign transaction") + } + + serializedTransaction, err := serializeTransaction(transaction) + if err != nil { + printErrorAndExit(err, "Failed to serialize transaction") + } + + fmt.Printf("Signed Transaction (hex): %s\n\n", serializedTransaction) +} + +func parsePrivateKey(privateKeyHex string) (*btcec.PrivateKey, error) { + privateKeyBytes, err := hex.DecodeString(privateKeyHex) + privateKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes) + return privateKey, err +} + +func parseTransaction(transactionHex string) (*wire.MsgTx, error) { + serializedTx, err := hex.DecodeString(transactionHex) + var transaction wire.MsgTx + err = transaction.Deserialize(bytes.NewReader(serializedTx)) + return &transaction, err +} + +func createScriptPubKey(publicKey *btcec.PublicKey) ([]byte, error) { + p2pkhAddress, err := util.NewAddressPubKeyHashFromPublicKey(publicKey.SerializeCompressed(), activeNetParams.Prefix) + scriptPubKey, err := txscript.PayToAddrScript(p2pkhAddress) + return scriptPubKey, err +} + +func signTransaction(transaction *wire.MsgTx, privateKey *btcec.PrivateKey, scriptPubKey []byte) error { + for i, transactionInput := range transaction.TxIn { + signatureScript, err := txscript.SignatureScript(transaction, i, scriptPubKey, txscript.SigHashAll, privateKey, true) + if err != nil { + return err + } + transactionInput.SignatureScript = signatureScript + } + return nil +} + +func serializeTransaction(transaction *wire.MsgTx) (string, error) { + buf := bytes.NewBuffer(make([]byte, 0, transaction.SerializeSize())) + err := transaction.Serialize(buf) + serializedTransaction := hex.EncodeToString(buf.Bytes()) + return serializedTransaction, err +} + +func printErrorAndExit(err error, message string) { + fmt.Fprintf(os.Stderr, "%s: %s", message, err) + os.Exit(1) +}