// This example demonstrates creating a script which pays to a bitcoin address. // It also prints the created script hex and uses the DisasmString function to // display the disassembled script. func ExamplePayToAddrScript() { // Parse the address to send the coins to into a btcutil.Address // which is useful to ensure the accuracy of the address and determine // the address type. It is also required for the upcoming call to // PayToAddrScript. addressStr := "12gpXQVcCL2qhTNQgyLVdCFG2Qs2px98nV" address, err := btcutil.DecodeAddress(addressStr, &btcnet.MainNetParams) if err != nil { fmt.Println(err) return } // Create a public key script that pays to the address. script, err := btcscript.PayToAddrScript(address) if err != nil { fmt.Println(err) return } fmt.Printf("Script Hex: %x\n", script) disasm, err := btcscript.DisasmString(script) if err != nil { fmt.Println(err) return } fmt.Println("Script Disassembly:", disasm) // Output: // Script Hex: 76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac // Script Disassembly: OP_DUP OP_HASH160 128004ff2fcaf13b2b91eb654b1dc2b674f7ec61 OP_EQUALVERIFY OP_CHECKSIG }
// createVoutList returns a slice of JSON objects for the outputs of the passed // transaction. func createVoutList(mtx *btcwire.MsgTx, net btcwire.BitcoinNet) ([]btcjson.Vout, error) { voutList := make([]btcjson.Vout, len(mtx.TxOut)) for i, v := range mtx.TxOut { voutList[i].N = i voutList[i].Value = float64(v.Value) / float64(btcutil.SatoshiPerBitcoin) disbuf, err := btcscript.DisasmString(v.PkScript) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } voutList[i].ScriptPubKey.Asm = disbuf voutList[i].ScriptPubKey.Hex = hex.EncodeToString(v.PkScript) // Ignore the error here since an error means the script // couldn't parse and there is no additional information about // it anyways. scriptClass, addrs, reqSigs, _ := btcscript.ExtractPkScriptAddrs(v.PkScript, net) voutList[i].ScriptPubKey.Type = scriptClass.String() voutList[i].ScriptPubKey.ReqSigs = reqSigs if addrs == nil { voutList[i].ScriptPubKey.Addresses = nil } else { voutList[i].ScriptPubKey.Addresses = make([]string, len(addrs)) for j, addr := range addrs { voutList[i].ScriptPubKey.Addresses[j] = addr.EncodeAddress() } } } return voutList, nil }
// createVinList returns a slice of JSON objects for the inputs of the passed // transaction. func createVinList(mtx *btcwire.MsgTx) ([]btcjson.Vin, error) { tx := btcutil.NewTx(mtx) vinList := make([]btcjson.Vin, len(mtx.TxIn)) for i, v := range mtx.TxIn { if btcchain.IsCoinBase(tx) { vinList[i].Coinbase = hex.EncodeToString(v.SignatureScript) } else { vinList[i].Txid = v.PreviousOutpoint.Hash.String() vinList[i].Vout = int(v.PreviousOutpoint.Index) disbuf, err := btcscript.DisasmString(v.SignatureScript) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } vinList[i].ScriptSig = new(btcjson.ScriptSig) vinList[i].ScriptSig.Asm = disbuf vinList[i].ScriptSig.Hex = hex.EncodeToString(v.SignatureScript) } vinList[i].Sequence = v.Sequence } return vinList, nil }
func PayToAddrScript(addressStr string) { // Parse the address to send the coins to into a btcutil.Address // which is useful to ensure the accuracy of the address and determine // the address type. It is also required for the upcoming call to // PayToAddrScript. address, err := btcutil.DecodeAddress(addressStr, &btcnet.MainNetParams) handle(err) // Create a public key script that pays to the address. script, err := btcscript.PayToAddrScript(address) handle(err) fmt.Printf("Script Hex: %x\n", script) disasm, err := btcscript.DisasmString(script) handle(err) fmt.Println("Script Disassembly:", disasm) }
// handleDecodeScript handles decodescript commands. func handleDecodeScript(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) { c := cmd.(*btcjson.DecodeScriptCmd) // Convert the hex script to bytes. script, err := hex.DecodeString(c.HexScript) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInvalidParameter.Code, Message: fmt.Sprintf("argument must be hexadecimal "+ "string (not %q)", c.HexScript), } } // The disassembled string will contain [error] inline if the script // doesn't fully parse, so ignore the error here. disbuf, _ := btcscript.DisasmString(script) // Get information about the script. // Ignore the error here since an error means the script couldn't parse // and there is no additinal information about it anyways. net := s.server.btcnet scriptClass, addrs, reqSigs, _ := btcscript.ExtractPkScriptAddrs(script, net) addresses := make([]string, len(addrs)) for i, addr := range addrs { addresses[i] = addr.EncodeAddress() } // Convert the script itself to a pay-to-script-hash address. p2sh, err := btcutil.NewAddressScriptHash(script, net) if err != nil { return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } // Generate and return the reply. reply := btcjson.DecodeScriptResult{ Asm: disbuf, ReqSigs: reqSigs, Type: scriptClass.String(), Addresses: addresses, P2sh: p2sh.EncodeAddress(), } return reply, nil }
// jsonRPCRead is the main function that handles reading messages, getting // the data the message requests, and writing the reply. func jsonRPCRead(w http.ResponseWriter, r *http.Request, s *rpcServer) { _ = spew.Dump r.Close = true if atomic.LoadInt32(&s.shutdown) != 0 { return } var rawReply btcjson.Reply body, err := btcjson.GetRaw(r.Body) if err != nil { log.Errorf("[RPCS] Error getting json message: %v", err) return } var message btcjson.Message err = json.Unmarshal(body, &message) if err != nil { log.Errorf("[RPCS] Error unmarshalling json message: %v", err) jsonError := btcjson.Error{ Code: -32700, Message: "Parse error", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: nil, } log.Tracef("[RPCS] reply: %v", rawReply) msg, err := btcjson.MarshallAndSend(rawReply, w) if err != nil { log.Errorf(msg) return } log.Debugf(msg) return } log.Tracef("[RPCS] received: %v", message) // Deal with commands switch message.Method { case "stop": rawReply = btcjson.Reply{ Result: "btcd stopping.", Error: nil, Id: &message.Id, } s.server.Stop() case "getblockcount": _, maxidx, _ := s.server.db.NewestSha() rawReply = btcjson.Reply{ Result: maxidx, Error: nil, Id: &message.Id, } // btcd does not do mining so we can hardcode replies here. case "getgenerate": rawReply = btcjson.Reply{ Result: false, Error: nil, Id: &message.Id, } case "setgenerate": rawReply = btcjson.Reply{ Result: nil, Error: nil, Id: &message.Id, } case "gethashespersec": rawReply = btcjson.Reply{ Result: 0, Error: nil, Id: &message.Id, } case "getblockhash": var f interface{} err = json.Unmarshal(body, &f) m := f.(map[string]interface{}) var idx float64 for _, v := range m { switch vv := v.(type) { case []interface{}: for _, u := range vv { idx, _ = u.(float64) } default: } } sha, err := s.server.db.FetchBlockShaByHeight(int64(idx)) if err != nil { log.Errorf("[RCPS] Error getting block: %v", err) jsonError := btcjson.Error{ Code: -1, Message: "Block number out of range.", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } log.Tracef("[RPCS] reply: %v", rawReply) break } rawReply = btcjson.Reply{ Result: sha.String(), Error: nil, Id: &message.Id, } case "getblock": var f interface{} err = json.Unmarshal(body, &f) m := f.(map[string]interface{}) var hash string for _, v := range m { switch vv := v.(type) { case []interface{}: for _, u := range vv { hash, _ = u.(string) } default: } } sha, err := btcwire.NewShaHashFromStr(hash) if err != nil { log.Errorf("[RPCS] Error generating sha: %v", err) jsonError := btcjson.Error{ Code: -5, Message: "Block not found", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } log.Tracef("[RPCS] reply: %v", rawReply) break } blk, err := s.server.db.FetchBlockBySha(sha) if err != nil { log.Errorf("[RPCS] Error fetching sha: %v", err) jsonError := btcjson.Error{ Code: -5, Message: "Block not found", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } log.Tracef("[RPCS] reply: %v", rawReply) break } idx := blk.Height() buf, err := blk.Bytes() if err != nil { log.Errorf("[RPCS] Error fetching block: %v", err) jsonError := btcjson.Error{ Code: -5, Message: "Block not found", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } log.Tracef("[RPCS] reply: %v", rawReply) break } txList, _ := blk.TxShas() txNames := make([]string, len(txList)) for i, v := range txList { txNames[i] = v.String() } _, maxidx, err := s.server.db.NewestSha() if err != nil { log.Errorf("[RPCS] Cannot get newest sha: %v", err) return } blockHeader := &blk.MsgBlock().Header blockReply := btcjson.BlockResult{ Hash: hash, Version: blockHeader.Version, MerkleRoot: blockHeader.MerkleRoot.String(), PreviousHash: blockHeader.PrevBlock.String(), Nonce: blockHeader.Nonce, Time: blockHeader.Timestamp.Unix(), Confirmations: uint64(1 + maxidx - idx), Height: idx, Tx: txNames, Size: len(buf), Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), Difficulty: getDifficultyRatio(blockHeader.Bits), } // Get next block unless we are already at the top. if idx < maxidx { shaNext, err := s.server.db.FetchBlockShaByHeight(int64(idx + 1)) if err != nil { log.Errorf("[RPCS] No next block: %v", err) } else { blockReply.NextHash = shaNext.String() } } rawReply = btcjson.Reply{ Result: blockReply, Error: nil, Id: &message.Id, } case "getrawtransaction": var f interface{} err = json.Unmarshal(body, &f) m := f.(map[string]interface{}) var tx string var verbose float64 for _, v := range m { switch vv := v.(type) { case []interface{}: for _, u := range vv { switch uu := u.(type) { case string: tx = uu case float64: verbose = uu default: } } default: } } if int(verbose) != 1 { // Don't return details // not used yet } else { txSha, _ := btcwire.NewShaHashFromStr(tx) var txS *btcwire.MsgTx txList, err := s.server.db.FetchTxBySha(txSha) if err != nil { log.Errorf("[RPCS] Error fetching tx: %v", err) jsonError := btcjson.Error{ Code: -5, Message: "No information available about transaction", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } log.Tracef("[RPCS] reply: %v", rawReply) break } lastTx := len(txList) - 1 txS = txList[lastTx].Tx blksha := txList[lastTx].BlkSha blk, err := s.server.db.FetchBlockBySha(blksha) if err != nil { log.Errorf("[RPCS] Error fetching sha: %v", err) jsonError := btcjson.Error{ Code: -5, Message: "Block not found", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } log.Tracef("[RPCS] reply: %v", rawReply) break } idx := blk.Height() txOutList := txS.TxOut voutList := make([]btcjson.Vout, len(txOutList)) txInList := txS.TxIn vinList := make([]btcjson.Vin, len(txInList)) for i, v := range txInList { vinList[i].Sequence = float64(v.Sequence) disbuf, _ := btcscript.DisasmString(v.SignatureScript) vinList[i].ScriptSig.Asm = strings.Replace(disbuf, " ", "", -1) vinList[i].Vout = i + 1 log.Debugf(disbuf) } for i, v := range txOutList { voutList[i].N = i voutList[i].Value = float64(v.Value) / 100000000 isbuf, _ := btcscript.DisasmString(v.PkScript) voutList[i].ScriptPubKey.Asm = isbuf voutList[i].ScriptPubKey.ReqSig = strings.Count(isbuf, "OP_CHECKSIG") _, addr, err := btcscript.ScriptToAddress(v.PkScript) if err != nil { log.Errorf("[RPCS] Error getting address for %v: %v", txSha, err) } else { addrList := make([]string, 1) addrList[0] = addr voutList[i].ScriptPubKey.Addresses = addrList } } _, maxidx, err := s.server.db.NewestSha() if err != nil { log.Errorf("[RPCS] Cannot get newest sha: %v", err) return } confirmations := uint64(1 + maxidx - idx) blockHeader := &blk.MsgBlock().Header txReply := btcjson.TxRawResult{ Txid: tx, Vout: voutList, Vin: vinList, Version: txS.Version, LockTime: txS.LockTime, // This is not a typo, they are identical in // bitcoind as well. Time: blockHeader.Timestamp.Unix(), Blocktime: blockHeader.Timestamp.Unix(), BlockHash: blksha.String(), Confirmations: confirmations, } rawReply = btcjson.Reply{ Result: txReply, Error: nil, Id: &message.Id, } } case "decoderawtransaction": var f interface{} err = json.Unmarshal(body, &f) m := f.(map[string]interface{}) var hash string for _, v := range m { switch vv := v.(type) { case []interface{}: for _, u := range vv { hash, _ = u.(string) } default: } } spew.Dump(hash) txReply := btcjson.TxRawDecodeResult{} rawReply = btcjson.Reply{ Result: txReply, Error: nil, Id: &message.Id, } default: jsonError := btcjson.Error{ Code: -32601, Message: "Method not found", } rawReply = btcjson.Reply{ Result: nil, Error: &jsonError, Id: &message.Id, } } msg, err := btcjson.MarshallAndSend(rawReply, w) if err != nil { log.Errorf(msg) return } log.Debugf(msg) return }
// handleGetRawTransaction implements the getrawtransaction command. func handleGetRawTransaction(s *rpcServer, cmd btcjson.Cmd, walletNotification chan []byte) (interface{}, error) { c := cmd.(*btcjson.GetRawTransactionCmd) if c.Verbose { // TODO: check error code. tx is not checked before // this point. txSha, _ := btcwire.NewShaHashFromStr(c.Txid) txList, err := s.server.db.FetchTxBySha(txSha) if err != nil { log.Errorf("RPCS: Error fetching tx: %v", err) return nil, btcjson.ErrNoTxInfo } lastTx := len(txList) - 1 txS := txList[lastTx].Tx blksha := txList[lastTx].BlkSha blk, err := s.server.db.FetchBlockBySha(blksha) if err != nil { log.Errorf("RPCS: Error fetching sha: %v", err) return nil, btcjson.ErrBlockNotFound } idx := blk.Height() txOutList := txS.TxOut voutList := make([]btcjson.Vout, len(txOutList)) txInList := txS.TxIn vinList := make([]btcjson.Vin, len(txInList)) for i, v := range txInList { vinList[i].Sequence = float64(v.Sequence) disbuf, _ := btcscript.DisasmString(v.SignatureScript) vinList[i].ScriptSig.Asm = strings.Replace(disbuf, " ", "", -1) vinList[i].Vout = i + 1 log.Debugf(disbuf) } for i, v := range txOutList { voutList[i].N = i voutList[i].Value = float64(v.Value) / 100000000 isbuf, _ := btcscript.DisasmString(v.PkScript) voutList[i].ScriptPubKey.Asm = isbuf voutList[i].ScriptPubKey.ReqSig = strings.Count(isbuf, "OP_CHECKSIG") _, addrhash, err := btcscript.ScriptToAddrHash(v.PkScript) if err != nil { // TODO: set and return error? log.Errorf("RPCS: Error getting address hash for %v: %v", txSha, err) } if addr, err := btcutil.EncodeAddress(addrhash, s.server.btcnet); err != nil { // TODO: set and return error? addrList := make([]string, 1) addrList[0] = addr voutList[i].ScriptPubKey.Addresses = addrList } } _, maxidx, err := s.server.db.NewestSha() if err != nil { log.Errorf("RPCS: Cannot get newest sha: %v", err) return nil, btcjson.ErrNoNewestBlockInfo } confirmations := uint64(1 + maxidx - idx) blockHeader := &blk.MsgBlock().Header txReply := btcjson.TxRawResult{ Txid: c.Txid, Vout: voutList, Vin: vinList, Version: txS.Version, LockTime: txS.LockTime, // This is not a typo, they are identical in // bitcoind as well. Time: blockHeader.Timestamp.Unix(), Blocktime: blockHeader.Timestamp.Unix(), BlockHash: blksha.String(), Confirmations: confirmations, } return txReply, nil } else { // Don't return details // not used yet return nil, nil } }