diff --git a/cmd/erisdb/main.go b/cmd/erisdb/main.go index 9de6b6b69c4df0b177acc0c039669760bff2b711..a3ef77a82c44cc4c1b463f25a92875cfac9067e8 100644 --- a/cmd/erisdb/main.go +++ b/cmd/erisdb/main.go @@ -8,14 +8,21 @@ import ( // TODO the input stuff. func main() { + args := os.Args[1:] var baseDir string - if len(os.Args) == 2 { - baseDir = os.Args[1] + var inProc bool + if len(args) > 0 { + baseDir = args[0] + if len(args) > 1 { + if args[1] == "inproc" { + inProc = true + } + } } else { baseDir = os.Getenv("HOME") + "/.erisdb" } - proc, errSt := edb.ServeErisDB(baseDir) + proc, errSt := edb.ServeErisDB(baseDir, inProc) if errSt != nil { panic(errSt.Error()) } diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000000000000000000000000000000000000..21c6b81f8ef0fcfcc48bcd2284583811c315b0eb --- /dev/null +++ b/config/config.go @@ -0,0 +1,117 @@ + +package config + +import ( + "github.com/naoina/toml" + "sync" + "time" + + . "github.com/tendermint/go-common" +) + +type Config interface { + Get(key string) interface{} + GetBool(key string) bool + GetFloat64(key string) float64 + GetInt(key string) int + GetString(key string) string + GetStringMap(key string) map[string]interface{} + GetStringMapString(key string) map[string]string + GetStringSlice(key string) []string + GetTime(key string) time.Time + IsSet(key string) bool + Set(key string, value interface{}) +} + +type MapConfig struct { + required map[string]struct{} // blows up if trying to use before setting. + data map[string]interface{} +} + +func ReadMapConfigFromFile(filePath string) (MapConfig, error) { + var configData = make(map[string]interface{}) + fileBytes := MustReadFile(filePath) + err := toml.Unmarshal(fileBytes, configData) + if err != nil { + return MapConfig{}, err + } + return NewMapConfig(configData), nil +} + +func NewMapConfig(data map[string]interface{}) MapConfig { + if data == nil { + data = make(map[string]interface{}) + } + return MapConfig{ + required: make(map[string]struct{}), + data: data, + } +} + +func (cfg MapConfig) Get(key string) interface{} { + if _, ok := cfg.required[key]; ok { + PanicSanity(Fmt("config key %v is required but was not set.", key)) + } + return cfg.data[key] +} +func (cfg MapConfig) GetBool(key string) bool { return cfg.Get(key).(bool) } +func (cfg MapConfig) GetFloat64(key string) float64 { return cfg.Get(key).(float64) } +func (cfg MapConfig) GetInt(key string) int { return cfg.Get(key).(int) } +func (cfg MapConfig) GetString(key string) string { return cfg.Get(key).(string) } +func (cfg MapConfig) GetStringMap(key string) map[string]interface{} { + return cfg.Get(key).(map[string]interface{}) +} +func (cfg MapConfig) GetStringMapString(key string) map[string]string { + return cfg.Get(key).(map[string]string) +} +func (cfg MapConfig) GetStringSlice(key string) []string { return cfg.Get(key).([]string) } +func (cfg MapConfig) GetTime(key string) time.Time { return cfg.Get(key).(time.Time) } +func (cfg MapConfig) IsSet(key string) bool { _, ok := cfg.data[key]; return ok } +func (cfg MapConfig) Set(key string, value interface{}) { + delete(cfg.required, key) + cfg.data[key] = value +} +func (cfg MapConfig) SetDefault(key string, value interface{}) { + delete(cfg.required, key) + if cfg.IsSet(key) { + return + } + cfg.data[key] = value +} +func (cfg MapConfig) SetRequired(key string) { + if cfg.IsSet(key) { + return + } + cfg.required[key] = struct{}{} +} + +//-------------------------------------------------------------------------------- +// A little convenient hack to notify listeners upon config changes. + +type Configurable func(Config) + +var mtx sync.Mutex +var globalConfig Config +var confs []Configurable + +func OnConfig(conf func(Config)) { + mtx.Lock() + defer mtx.Unlock() + + confs = append(confs, conf) + if globalConfig != nil { + conf(globalConfig) + } +} + +func ApplyConfig(config Config) { + mtx.Lock() + globalConfig = config + confsCopy := make([]Configurable, len(confs)) + copy(confsCopy, confs) + mtx.Unlock() + + for _, conf := range confsCopy { + conf(config) + } +} diff --git a/config/tendermint/config.go b/config/tendermint/config.go new file mode 100644 index 0000000000000000000000000000000000000000..25c85efa3874885d086c9ba80e0c1746122a9d2f --- /dev/null +++ b/config/tendermint/config.go @@ -0,0 +1,104 @@ +package tendermint + +import ( + "os" + "path" + "strings" + + . "github.com/tendermint/go-common" + cfg "github.com/tendermint/go-config" +) + +func getTMRoot(rootDir string) string { + if rootDir == "" { + rootDir = os.Getenv("TMROOT") + } + if rootDir == "" { + rootDir = os.Getenv("HOME") + "/.tendermint" + } + return rootDir +} + +func initTMRoot(rootDir string) { + rootDir = getTMRoot(rootDir) + EnsureDir(rootDir, 0700) + + configFilePath := path.Join(rootDir, "config.toml") + + // Write default config file if missing. + if !FileExists(configFilePath) { + // Ask user for moniker + // moniker := cfg.Prompt("Type hostname: ", "anonymous") + MustWriteFile(configFilePath, []byte(defaultConfig("anonymous")), 0644) + } +} + +func GetConfig(rootDir string) cfg.Config { + rootDir = getTMRoot(rootDir) + initTMRoot(rootDir) + + configFilePath := path.Join(rootDir, "config.toml") + mapConfig, err := cfg.ReadMapConfigFromFile(configFilePath) + if err != nil { + Exit(Fmt("Could not read config: %v", err)) + } + + // Set defaults or panic + if mapConfig.IsSet("chain_id") { + Exit("Cannot set 'chain_id' via config.toml") + } + if mapConfig.IsSet("revision_file") { + Exit("Cannot set 'revision_file' via config.toml. It must match what's in the Makefile") + } + mapConfig.SetRequired("chain_id") // blows up if you try to use it before setting. + mapConfig.SetDefault("erisdb_genesis_file", rootDir+"/erisdb_genesis.json") + mapConfig.SetDefault("tendermint_genesis_file", rootDir+"/tendermint_genesis.json") + mapConfig.SetDefault("genesis_file", rootDir+"/tendermint_genesis.json") // copy tendermint_genesis_file + mapConfig.SetDefault("proxy_app", "tcp://127.0.0.1:46658") + mapConfig.SetDefault("moniker", "anonymous") + mapConfig.SetDefault("node_laddr", "0.0.0.0:46656") + // mapConfig.SetDefault("seeds", "goldenalchemist.chaintest.net:46656") + mapConfig.SetDefault("fast_sync", true) + mapConfig.SetDefault("skip_upnp", false) + mapConfig.SetDefault("addrbook_file", rootDir+"/addrbook.json") + mapConfig.SetDefault("priv_validator_file", rootDir+"/priv_validator.json") + mapConfig.SetDefault("db_backend", "leveldb") + mapConfig.SetDefault("db_dir", rootDir+"/data") + mapConfig.SetDefault("log_level", "info") + mapConfig.SetDefault("rpc_laddr", "0.0.0.0:46657") + mapConfig.SetDefault("prof_laddr", "") + mapConfig.SetDefault("revision_file", rootDir+"/revision") + mapConfig.SetDefault("cswal", rootDir+"/data/cswal") + mapConfig.SetDefault("cswal_light", false) + + mapConfig.SetDefault("block_size", 10000) + mapConfig.SetDefault("timeout_propose", 3000) + mapConfig.SetDefault("timeout_propose_delta", 500) + mapConfig.SetDefault("timeout_prevote", 1000) + mapConfig.SetDefault("timeout_prevote_delta", 500) + mapConfig.SetDefault("timeout_precommit", 1000) + mapConfig.SetDefault("timeout_precommit_delta", 500) + mapConfig.SetDefault("timeout_commit", 1000) + mapConfig.SetDefault("mempool_recheck", true) + mapConfig.SetDefault("mempool_broadcast", true) + + return mapConfig +} + +var defaultConfigTmpl = `# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +proxy_app = "tcp://127.0.0.1:46658" +moniker = "__MONIKER__" +node_laddr = "0.0.0.0:46656" +seeds = "" +fast_sync = true +db_backend = "leveldb" +log_level = "notice" +rpc_laddr = "0.0.0.0:46657" +` + +func defaultConfig(moniker string) (defaultConfig string) { + defaultConfig = strings.Replace(defaultConfigTmpl, "__MONIKER__", moniker, -1) + return +} diff --git a/config/tendermint/genesis.json b/config/tendermint/genesis.json new file mode 100644 index 0000000000000000000000000000000000000000..eca00696edb4414df42fe6770cc23cde5a758529 --- /dev/null +++ b/config/tendermint/genesis.json @@ -0,0 +1,75 @@ +{ + "chain_id": "tendermint_testnet_11.c", + "accounts": [ + { + "address": "9FCBA7F840A0BFEBBE755E853C9947270A912D04", + "amount": 1991999998000000 + }, + { + "address": "964B1493BBE3312278B7DEB94C39149F7899A345", + "amount": 100000000000000 + }, + { + "address": "B9FA4AB462B9C6BF6A62DB4AE77C9E7087209A04", + "amount": 1000000000000 + }, + { + "address": "F171824590D69386F709E7B6704B369C5A370D60", + "amount": 1000000000000 + }, + { + "address": "56EFE746A13D9A6054AC89C3E2A361C2DB8B9EAE", + "amount": 1000000000000 + }, + { + "address": "7C2E032D8407EDF66A04D88CF0E1D9B15D98AE2D", + "amount": 1000000000000 + }, + { + "address": "636EF5823E082AD66EBC203FD4DFB1031F0C61CA", + "amount": 1000000000000 + }, + { + "address": "9008419E6351360A59B124E707E4CA2A5BFB9BE6", + "amount": 1000000000000 + }, + { + "address": "C78F48919B8A4030AD3E5ED643F8D2302E41953D", + "amount": 1000000000000 + }, + { + "address": "5290AC90CE2422DDC3F91F6A246F7E3C542EA51A", + "amount": 1000000000000 + }, + { + "address": "A88A61069B6660F30F65E8786AFDD4F1D8F625E9", + "amount": 1000000 + }, + { + "address": "EE2EE9247973B4AFC3867CFE5F415410AC251B61", + "amount": 1000000 + } + ], + "validators": [ + { + "pub_key": [1, "178EC6008A4364508979C70CBF100BD4BCBAA12DDE6251F5F486B4FD09014F06"], + "amount": 100000000000 + }, + { + "pub_key": [1, "2A77777CC51467DE42350D4A8F34720D527734189BE64C7A930DD169E1FED3C6"], + "amount": 100000000000 + }, + { + "pub_key": [1, "3718E69D09B11B3AD3FA31AEF07EC416D2AEED241CACE7B0F30AE9803FFB0F08"], + "amount": 100000000000 + }, + { + "pub_key": [1, "C6B0440DEACD1E4CF1C736CEB8E38E788B700BA2B2045A55CB657A455CF5F889"], + "amount": 100000000000 + }, + { + "pub_key": [1, "3BA1190D54F91EFBF8B0125F7EC116AD4BA2894B6EE38564A5D5FD3230D91F7B"], + "amount": 100000000000 + } + ] +} diff --git a/config/tendermint/logrotate.config b/config/tendermint/logrotate.config new file mode 100644 index 0000000000000000000000000000000000000000..73eaf74e74b50cb744de5dfba2dfa3835f0f5c45 --- /dev/null +++ b/config/tendermint/logrotate.config @@ -0,0 +1,22 @@ +// If you wanted to use logrotate, I suppose this might be the config you want. +// Instead, I'll just write our own, that way we don't need sudo to install. + +$HOME/.tendermint/logs/tendermint.log { + missingok + notifempty + rotate 12 + daily + size 10M + compress + delaycompress +} + +$HOME/.barak/logs/barak.log { + missingok + notifempty + rotate 12 + weekly + size 10M + compress + delaycompress +} diff --git a/config/tendermint_test/config.go b/config/tendermint_test/config.go new file mode 100644 index 0000000000000000000000000000000000000000..b90fe25491b6860808dfd9a5b7204a2456e435c6 --- /dev/null +++ b/config/tendermint_test/config.go @@ -0,0 +1,151 @@ +// Import this in all *_test.go files to initialize ~/.tendermint_test. + +package tendermint_test + +import ( + "os" + "path" + "strings" + + . "github.com/tendermint/go-common" + cfg "github.com/tendermint/go-config" +) + +func init() { + // Creates ~/.tendermint_test + EnsureDir(os.Getenv("HOME")+"/.tendermint_test", 0700) +} + +func ResetConfig(path string) { + rootDir := os.Getenv("HOME") + "/.tendermint_test/" + path + cfg.ApplyConfig(GetConfig(rootDir)) +} + +func initTMRoot(rootDir string) { + // Remove ~/.tendermint_test_bak + if FileExists(rootDir + "_bak") { + err := os.RemoveAll(rootDir + "_bak") + if err != nil { + PanicSanity(err.Error()) + } + } + // Move ~/.tendermint_test to ~/.tendermint_test_bak + if FileExists(rootDir) { + err := os.Rename(rootDir, rootDir+"_bak") + if err != nil { + PanicSanity(err.Error()) + } + } + // Create new dir + EnsureDir(rootDir, 0700) + + configFilePath := path.Join(rootDir, "config.toml") + genesisFilePath := path.Join(rootDir, "genesis.json") + privFilePath := path.Join(rootDir, "priv_validator.json") + + // Write default config file if missing. + if !FileExists(configFilePath) { + // Ask user for moniker + // moniker := cfg.Prompt("Type hostname: ", "anonymous") + MustWriteFile(configFilePath, []byte(defaultConfig("anonymous")), 0644) + } + if !FileExists(genesisFilePath) { + MustWriteFile(genesisFilePath, []byte(defaultGenesis), 0644) + } + // we always overwrite the priv val + MustWriteFile(privFilePath, []byte(defaultPrivValidator), 0644) +} + +func GetConfig(rootDir string) cfg.Config { + initTMRoot(rootDir) + + configFilePath := path.Join(rootDir, "config.toml") + mapConfig, err := cfg.ReadMapConfigFromFile(configFilePath) + if err != nil { + Exit(Fmt("Could not read config: %v", err)) + } + + // Set defaults or panic + if mapConfig.IsSet("chain_id") { + Exit("Cannot set 'chain_id' via config.toml") + } + mapConfig.SetDefault("chain_id", "tendermint_test") + mapConfig.SetDefault("genesis_file", rootDir+"/genesis.json") + mapConfig.SetDefault("proxy_app", "dummy") + mapConfig.SetDefault("moniker", "anonymous") + mapConfig.SetDefault("node_laddr", "0.0.0.0:36656") + mapConfig.SetDefault("fast_sync", false) + mapConfig.SetDefault("skip_upnp", true) + mapConfig.SetDefault("addrbook_file", rootDir+"/addrbook.json") + mapConfig.SetDefault("priv_validator_file", rootDir+"/priv_validator.json") + mapConfig.SetDefault("db_backend", "memdb") + mapConfig.SetDefault("db_dir", rootDir+"/data") + mapConfig.SetDefault("log_level", "debug") + mapConfig.SetDefault("rpc_laddr", "0.0.0.0:36657") + mapConfig.SetDefault("prof_laddr", "") + mapConfig.SetDefault("revision_file", rootDir+"/revision") + mapConfig.SetDefault("cswal", rootDir+"/data/cswal") + mapConfig.SetDefault("cswal_light", false) + + mapConfig.SetDefault("block_size", 10000) + mapConfig.SetDefault("timeout_propose", 100) + mapConfig.SetDefault("timeout_propose_delta", 1) + mapConfig.SetDefault("timeout_prevote", 1) + mapConfig.SetDefault("timeout_prevote_delta", 1) + mapConfig.SetDefault("timeout_precommit", 1) + mapConfig.SetDefault("timeout_precommit_delta", 1) + mapConfig.SetDefault("timeout_commit", 1) + mapConfig.SetDefault("mempool_recheck", true) + mapConfig.SetDefault("mempool_broadcast", true) + + return mapConfig +} + +var defaultConfigTmpl = `# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +proxy_app = "dummy" +moniker = "__MONIKER__" +node_laddr = "0.0.0.0:36656" +seeds = "" +fast_sync = false +db_backend = "memdb" +log_level = "debug" +rpc_laddr = "0.0.0.0:36657" +` + +func defaultConfig(moniker string) (defaultConfig string) { + defaultConfig = strings.Replace(defaultConfigTmpl, "__MONIKER__", moniker, -1) + return +} + +var defaultGenesis = `{ + "genesis_time": "0001-01-01T00:00:00.000Z", + "chain_id": "tendermint_test", + "validators": [ + { + "pub_key": [ + 1, + "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" + ], + "amount": 10, + "name": "" + } + ], + "app_hash": "" +}` + +var defaultPrivValidator = `{ + "address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456", + "pub_key": [ + 1, + "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" + ], + "priv_key": [ + 1, + "27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" + ], + "last_height": 0, + "last_round": 0, + "last_step": 0 +}` diff --git a/erisdb/pipe/blockchain.go b/erisdb/pipe/blockchain.go index 1aaf79ce73fa644719450d3fb3f8a3ac102a8b7f..ff905304f4345cc6bde71c0713e7c4440dd11dd2 100644 --- a/erisdb/pipe/blockchain.go +++ b/erisdb/pipe/blockchain.go @@ -7,10 +7,9 @@ import ( "strings" "sync" + "github.com/eris-ltd/eris-db/state" dbm "github.com/tendermint/go-db" "github.com/tendermint/tendermint/types" - - "github.com/eris-ltd/eris-db/state" ) const BLOCK_MAX = 50 @@ -44,7 +43,7 @@ func newBlockchain(blockStore BlockStore) *blockchain { func (this *blockchain) Info() (*BlockchainInfo, error) { chainId := config.GetString("chain_id") db := dbm.NewMemDB() - _, genesisState := state.MakeGenesisStateFromFile(db, config.GetString("genesis_file")) + _, genesisState := state.MakeGenesisStateFromFile(db, config.GetString("erisdb_genesis_file")) genesisHash := genesisState.Hash() latestHeight := this.blockStore.Height() @@ -70,7 +69,7 @@ func (this *blockchain) ChainId() (string, error) { // Get the hash of the genesis block. func (this *blockchain) GenesisHash() ([]byte, error) { db := dbm.NewMemDB() - _, genesisState := state.MakeGenesisStateFromFile(db, config.GetString("genesis_file")) + _, genesisState := state.MakeGenesisStateFromFile(db, config.GetString("erisdb_genesis_file")) return genesisState.Hash(), nil } diff --git a/erisdb/serve.go b/erisdb/serve.go index ec998c56feba1fef9f0ec1b5b9272bb8228b025b..203e42618a7226df35cb3a0ada7d33746a253c91 100644 --- a/erisdb/serve.go +++ b/erisdb/serve.go @@ -4,10 +4,12 @@ package erisdb import ( "bytes" + "fmt" + "io/ioutil" "path" + "strings" + "sync" - sm "github.com/eris-ltd/eris-db/state" - stypes "github.com/eris-ltd/eris-db/state/types" . "github.com/tendermint/go-common" cfg "github.com/tendermint/go-config" dbm "github.com/tendermint/go-db" @@ -15,15 +17,19 @@ import ( "github.com/tendermint/go-p2p" "github.com/tendermint/go-wire" "github.com/tendermint/log15" - tmcfg "github.com/tendermint/tendermint/config/tendermint" + "github.com/tendermint/tendermint/node" + "github.com/tendermint/tendermint/proxy" + "github.com/tendermint/tendermint/types" + tmspcli "github.com/tendermint/tmsp/client" + tmsp "github.com/tendermint/tmsp/server" + tmcfg "github.com/eris-ltd/eris-db/config/tendermint" ep "github.com/eris-ltd/eris-db/erisdb/pipe" - evm "github.com/eris-ltd/eris-db/evm" "github.com/eris-ltd/eris-db/server" - + sm "github.com/eris-ltd/eris-db/state" + stypes "github.com/eris-ltd/eris-db/state/types" edbapp "github.com/eris-ltd/eris-db/tmsp" - tmsp "github.com/tendermint/tmsp/server" ) const ERISDB_VERSION = "0.11.5" @@ -36,7 +42,7 @@ var tmConfig cfg.Config // with a tmsp listener for talking to tendermint core. // To start listening for incoming requests, call 'Start()' on the process. // Make sure to register any start event listeners first -func ServeErisDB(workDir string) (*server.ServeProcess, error) { +func ServeErisDB(workDir string, inProc bool) (*server.ServeProcess, error) { log.Info("ErisDB Serve initializing.") errEns := EnsureDir(workDir, 0777) @@ -69,19 +75,15 @@ func ServeErisDB(workDir string) (*server.ServeProcess, error) { tmConfig.Set("version", TENDERMINT_VERSION) cfg.ApplyConfig(tmConfig) // Notify modules of new config - // Set the node up. - // nodeRd := make(chan struct{}) - // nd := node.NewNode() - // Load the application state // The app state used to be managed by tendermint node, // but is now managed by ErisDB. // The tendermint core only stores the blockchain (history of txs) - stateDB := dbm.GetDB("state") + stateDB := dbm.GetDB("app_state") state := sm.LoadState(stateDB) var genDoc *stypes.GenesisDoc if state == nil { - genDoc, state = sm.MakeGenesisStateFromFile(stateDB, config.GetString("genesis_file")) + genDoc, state = sm.MakeGenesisStateFromFile(stateDB, config.GetString("erisdb_genesis_file")) state.Save() // write the gendoc to db buf, n, err := new(bytes.Buffer), new(int), new(error) @@ -103,20 +105,25 @@ func ServeErisDB(workDir string) (*server.ServeProcess, error) { evsw := events.NewEventSwitch() evsw.Start() + app := edbapp.NewErisDBApp(state, evsw) app.SetHostAddress(sConf.Consensus.TendermintHost) - evm.SetDebug(sConf.Logging.VMLog) - - // Start the tmsp listener for state update commands - go func() { - // TODO config - _, err := tmsp.NewServer(sConf.Consensus.TMSPListener, app) - if err != nil { - // TODO: play nice - Exit(err.Error()) - } - }() + if inProc { + fmt.Println("Starting tm node in proc") + startTMNode(app) + } else { + fmt.Println("Starting tmsp listener") + // Start the tmsp listener for state update commands + go func() { + // TODO config + _, err := tmsp.NewServer(sConf.Consensus.TMSPListener, app) + if err != nil { + // TODO: play nice + Exit(err.Error()) + } + }() + } // Load supporting objects. pipe := ep.NewPipe(app, evsw) @@ -138,6 +145,51 @@ func ServeErisDB(workDir string) (*server.ServeProcess, error) { return proc, nil } +func startTMNode(app *edbapp.ErisDBApp) { + // get the genesis + genDocFile := config.GetString("tendermint_genesis_file") + jsonBlob, err := ioutil.ReadFile(genDocFile) + if err != nil { + Exit(Fmt("Couldn't read GenesisDoc file: %v", err)) + } + genDoc := types.GenesisDocFromJSON(jsonBlob) + if genDoc.ChainID == "" { + PanicSanity(Fmt("Genesis doc %v must include non-empty chain_id", genDocFile)) + } + config.Set("chain_id", genDoc.ChainID) + config.Set("genesis_doc", genDoc) + + // Get PrivValidator + privValidatorFile := config.GetString("priv_validator_file") + privValidator := types.LoadOrGenPrivValidator(privValidatorFile) + nd := node.NewNode(privValidator, func(addr string, hash []byte) proxy.AppConn { + // TODO: Check the hash + return tmspcli.NewLocalClient(new(sync.Mutex), app) + }) + + l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), config.GetBool("skip_upnp")) + nd.AddListener(l) + if err := nd.Start(); err != nil { + Exit(Fmt("Failed to start node: %v", err)) + } + + log.Notice("Started node", "nodeInfo", nd.NodeInfo()) + + // If seedNode is provided by config, dial out. + if config.GetString("seeds") != "" { + seeds := strings.Split(config.GetString("seeds"), ",") + nd.DialSeeds(seeds) + } + + // Run the RPC server. + if config.GetString("rpc_laddr") != "" { + _, err := nd.StartRPC() + if err != nil { + PanicCrisis(err) + } + } +} + // Private. Create a new node. func startNode(nd *node.Node, ready chan struct{}, shutDown <-chan struct{}) { laddr := tmConfig.GetString("node_laddr") diff --git a/glide.lock b/glide.lock index 226dff2ba05c59b86df298b61b98aab1e00e6d9c..ac08c3454b66eee4e43d68fed8e99d6b1c4b3dd5 100644 --- a/glide.lock +++ b/glide.lock @@ -1,8 +1,14 @@ hash: ebfa16e47dd0e0b486fc4435f53391b6bd9df5cb6dda53377ad2a0cde065c056 -updated: 2016-04-08T18:00:31.443217762+01:00 +updated: 2016-04-19T16:32:29.646405777+01:00 imports: +- name: github.com/btcsuite/btcd + version: 474547b211c8f5566d094478318fcfd3c2c838d4 + subpackages: + - btcec +- name: github.com/btcsuite/fastsha256 + version: 302ad4db268b46f9ebda3078f6f7397f96047735 - name: github.com/gin-gonic/gin - version: 233291e4e2d016084f9e42e786e45dad8858ace4 + version: 542be2fe77724f800fcab7eb6c01a4e597fb8506 subpackages: - binding - render @@ -11,7 +17,7 @@ imports: subpackages: - proto - name: github.com/golang/snappy - version: cef980a12b316c5b7e5bb3a8e168eb43ae999a88 + version: 774a97396f7bfa4165e9dcf4bfa0747ea3edcc02 - name: github.com/gorilla/websocket version: e2e3d8414d0fbae04004f151979f4e27c6747fe7 - name: github.com/inconshreveable/log15 @@ -34,7 +40,7 @@ imports: - name: github.com/sfreiberg/gotwilio version: f024bbefe80fdb7bcc8c43b6add05dae97744e0e - name: github.com/stretchr/testify - version: 0744955171b0b6e1871ff9d7366abc2b7a7fcec0 + version: c5d7a69bf8a2c9c374798160849c071093e41dd1 - name: github.com/syndtr/goleveldb version: 93fc893f2dadb96ffde441c7546cc67ea290a3a8 subpackages: @@ -66,7 +72,7 @@ imports: - name: github.com/tendermint/go-config version: c47b67203b070d8bea835a928d50cb739972c48a - name: github.com/tendermint/go-crypto - version: 3f0d9b3f29f30e5d0cbc2cef04fa45e5a606c622 + version: d57d5ff3c925149e62f3a3c3f6ecaf4c8250d678 - name: github.com/tendermint/go-db version: a7878f1d0d8eaebf15f87bc2df15f7a1088cce7f - name: github.com/tendermint/go-events @@ -90,21 +96,21 @@ imports: - name: github.com/tendermint/log15 version: 6e460758f10ef42a4724b8e4a82fee59aaa0e41d - name: github.com/tendermint/tendermint - version: 39d72d581144b1b98487b012ec6b7255c5839c50 + version: 0df4a723e9d189db26577a2a7b17652e8f1fd2f4 subpackages: - config/tendermint - consensus - node - rpc/core/types - types + - proxy - blockchain - mempool - - proxy - rpc/core - state - version - name: github.com/tendermint/tmsp - version: 82a411fca58b3b1e2ac8fc3a6784a17579012d68 + version: d471b06bd8ddb12f7275d49422b9b376dbdd84ad subpackages: - server - types @@ -114,7 +120,7 @@ imports: - name: github.com/tommy351/gin-cors version: dc91dec6313ae4db53481bf3b29cf6b94bf80357 - name: golang.org/x/crypto - version: 3fbbcd23f1cb824e69491a5930cfeff09b12f4d2 + version: 2f6fccd33b9b1fc23ebb73ad4890698820f7174d subpackages: - ripemd160 - nacl/secretbox @@ -130,7 +136,7 @@ imports: - context - netutil - name: golang.org/x/sys - version: a80ff226bd294315526e7ae48959d55635143ea7 + version: f64b50fbea64174967a8882830d621a18ee1548e subpackages: - unix - name: gopkg.in/fatih/set.v0 @@ -139,4 +145,6 @@ imports: version: c193cecd124b5cc722d7ee5538e945bdb3348435 - name: gopkg.in/tylerb/graceful.v1 version: 9a3d4236b03bb5d26f7951134d248f9d5510d599 +- name: gopkg.in/yaml.v2 + version: a83829b6f1293c91addabc89d0571c246397bbf4 devImports: [] diff --git a/vendor/github.com/btcsuite/btcd/.gitignore b/vendor/github.com/btcsuite/btcd/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..5713f6891f1fcf708313705901cf1bdcd432cc99 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/.gitignore @@ -0,0 +1,33 @@ +# Temp files +*~ + +# Databases +btcd.db +*-shm +*-wal + +# Log files +*.log + +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/vendor/github.com/btcsuite/btcd/.travis.yml b/vendor/github.com/btcsuite/btcd/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..d003fea979cc516c6c21768edbc557b0da785af0 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/.travis.yml @@ -0,0 +1,15 @@ +language: go +go: + - 1.5.4 + - 1.6.1 +sudo: false +before_install: + - gotools=golang.org/x/tools +install: + - go get -d -t -v ./... + - go get -v $gotools/cmd/cover + - go get -v github.com/bradfitz/goimports + - go get -v github.com/golang/lint/golint +script: + - export PATH=$PATH:$HOME/gopath/bin + - ./goclean.sh diff --git a/vendor/github.com/btcsuite/btcd/CHANGES b/vendor/github.com/btcsuite/btcd/CHANGES new file mode 100644 index 0000000000000000000000000000000000000000..99b2fc13ffc960e1c1a0273e2ee4c4f4cdc16d28 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/CHANGES @@ -0,0 +1,955 @@ +============================================================================ +User visible changes for btcd + A full-node bitcoin implementation written in Go +============================================================================ + +Changes in 0.12.0 (Fri Nov 20 2015) + - Protocol and network related changes: + - Add a new checkpoint at block height 382320 (#555) + - Implement BIP0065 which includes support for version 4 blocks, a new + consensus opcode (OP_CHECKLOCKTIMEVERIFY) that enforces transaction + lock times, and a double-threshold switchover mechanism (#535, #459, + #455) + - Implement BIP0111 which provides a new bloom filter service flag and + hence provides support for protocol version 70011 (#499) + - Add a new parameter --nopeerbloomfilters to allow disabling bloom + filter support (#499) + - Reject non-canonically encoded variable length integers (#507) + - Add mainnet peer discovery DNS seed (seed.bitcoin.jonasschnelli.ch) + (#496) + - Correct reconnect handling for persistent peers (#463, #464) + - Ignore requests for block headers if not fully synced (#444) + - Add CLI support for specifying the zone id on IPv6 addresses (#538) + - Fix a couple of issues where the initial block sync could stall (#518, + #229, #486) + - Fix an issue which prevented the --onion option from working as + intended (#446) + - Transaction relay (memory pool) changes: + - Require transactions to only include signatures encoded with the + canonical 'low-s' encoding (#512) + - Add a new parameter --minrelaytxfee to allow the minimum transaction + fee in BTC/kB to be overridden (#520) + - Retain memory pool transactions when they redeem another one that is + removed when a block is accepted (#539) + - Do not send reject messages for a transaction if it is valid but + causes an orphan transaction which depends on it to be determined + as invalid (#546) + - Refrain from attempting to add orphans to the memory pool multiple + times when the transaction they redeem is added (#551) + - Modify minimum transaction fee calculations to scale based on bytes + instead of full kilobyte boundaries (#521, #537) + - Implement signature cache: + - Provides a limited memory cache of validated signatures which is a + huge optimization when verifying blocks for transactions that are + already in the memory pool (#506) + - Add a new parameter '--sigcachemaxsize' which allows the size of the + new cache to be manually changed if desired (#506) + - Mining support changes: + - Notify getblocktemplate long polling clients when a block is pushed + via submitblock (#488) + - Speed up getblocktemplate by making use of the new signature cache + (#506) + - RPC changes: + - Implement getmempoolinfo command (#453) + - Implement getblockheader command (#461) + - Modify createrawtransaction command to accept a new optional parameter + 'locktime' (#529) + - Modify listunspent result to include the 'spendable' field (#440) + - Modify getinfo command to include 'errors' field (#511) + - Add timestamps to blockconnected and blockdisconnected notifications + (#450) + - Several modifications to searchrawtranscations command: + - Accept a new optional parameter 'vinextra' which causes the results + to include information about the outputs referenced by a transaction's + inputs (#485, #487) + - Skip entries in the mempool too (#495) + - Accept a new optional parameter 'reverse' to return the results in + reverse order (most recent to oldest) (#497) + - Accept a new optional parameter 'filteraddrs' which causes the + results to only include inputs and outputs which involve the + provided addresses (#516) + - Change the notification order to notify clients about mined + transactions (recvtx, redeemingtx) before the blockconnected + notification (#449) + - Update verifymessage RPC to use the standard algorithm so it is + compatible with other implementations (#515) + - Improve ping statistics by pinging on an interval (#517) + - Websocket changes: + - Implement session command which returns a per-session unique id (#500, + #503) + - btcctl utility changes: + - Add getmempoolinfo command (#453) + - Add getblockheader command (#461) + - Add getwalletinfo command (#471) + - Notable developer-related package changes: + - Introduce a new peer package which acts a common base for creating and + concurrently managing bitcoin network peers (#445) + - Various cleanup of the new peer package (#528, #531, #524, #534, + #549) + - Blocks heights now consistently use int32 everywhere (#481) + - The BlockHeader type in the wire package now provides the BtcDecode + and BtcEncode methods (#467) + - Update wire package to recognize BIP0064 (getutxo) service bit (#489) + - Export LockTimeThreshold constant from txscript package (#454) + - Export MaxDataCarrierSize constant from txscript package (#466) + - Provide new IsUnspendable function from the txscript package (#478) + - Export variable length string functions from the wire package (#514) + - Export DNS Seeds for each network from the chaincfg package (#544) + - Preliminary work towards separating the memory pool into a separate + package (#525, #548) + - Misc changes: + - Various documentation updates (#442, #462, #465, #460, #470, #473, + #505, #530, #545) + - Add installation instructions for gentoo (#542) + - Ensure an error is shown if OS limits can't be set at startup (#498) + - Tighten the standardness checks for multisig scripts (#526) + - Test coverage improvement (#468, #494, #527, #543, #550) + - Several optimizations (#457, #474, #475, #476, #508, #509) + - Minor code cleanup and refactoring (#472, #479, #482, #519, #540) + - Contributors (alphabetical order): + - Ben Echols + - Bruno Clermont + - danda + - Daniel Krawisz + - Dario Nieuwenhuis + - Dave Collins + - David Hill + - Javed Khan + - Jonathan Gillham + - Joseph Becher + - Josh Rickmar + - Justus Ranvier + - Mawuli Adzoe + - Olaoluwa Osuntokun + - Rune T. Aune + +Changes in 0.11.1 (Wed May 27 2015) + - Protocol and network related changes: + - Use correct sub-command in reject message for rejected transactions + (#436, #437) + - Add a new parameter --torisolation which forces new circuits for each + connection when using tor (#430) + - Transaction relay (memory pool) changes: + - Reduce the default number max number of allowed orphan transactions + to 1000 (#419) + - Add a new parameter --maxorphantx which allows the maximum number of + orphan transactions stored in the mempool to be specified (#419) + - RPC changes: + - Modify listtransactions result to include the 'involveswatchonly' and + 'vout' fields (#427) + - Update getrawtransaction result to omit the 'confirmations' field + when it is 0 (#420, #422) + - Update signrawtransaction result to include errors (#423) + - btcctl utility changes: + - Add gettxoutproof command (#428) + - Add verifytxoutproof command (#428) + - Notable developer-related package changes: + - The btcec package now provides the ability to perform ECDH + encryption and decryption (#375) + - The block and header validation in the blockchain package has been + split to help pave the way toward concurrent downloads (#386) + - Misc changes: + - Minor peer optimization (#433) + - Contributors (alphabetical order): + - Dave Collins + - David Hill + - Federico Bond + - Ishbir Singh + - Josh Rickmar + +Changes in 0.11.0 (Wed May 06 2015) + - Protocol and network related changes: + - **IMPORTANT: Update is required due to the following point** + - Correct a few corner cases in script handling which could result in + forking from the network on non-standard transactions (#425) + - Add a new checkpoint at block height 352940 (#418) + - Optimized script execution (#395, #400, #404, #409) + - Fix a case that could lead stalled syncs (#138, #296) + - Network address manager changes: + - Implement eclipse attack countermeasures as proposed in + http://cs-people.bu.edu/heilman/eclipse (#370, #373) + - Optional address indexing changes: + - Fix an issue where a reorg could cause an orderly shutdown when the + address index is active (#340, #357) + - Transaction relay (memory pool) changes: + - Increase maximum allowed space for nulldata transactions to 80 bytes + (#331) + - Implement support for the following rules specified by BIP0062: + - The S value in ECDSA signature must be at most half the curve order + (rule 5) (#349) + - Script execution must result in a single non-zero value on the stack + (rule 6) (#347) + - NOTE: All 7 rules of BIP0062 are now implemented + - Use network adjusted time in finalized transaction checks to improve + consistency across nodes (#332) + - Process orphan transactions on acceptance of new transactions (#345) + - RPC changes: + - Add support for a limited RPC user which is not allowed admin level + operations on the server (#363) + - Implement node command for more unified control over connected peers + (#79, #341) + - Implement generate command for regtest/simnet to support + deterministically mining a specified number of blocks (#362, #407) + - Update searchrawtransactions to return the matching transactions in + order (#354) + - Correct an issue with searchrawtransactions where it could return + duplicates (#346, #354) + - Increase precision of 'difficulty' field in getblock result to 8 + (#414, #415) + - Omit 'nextblockhash' field from getblock result when it is empty + (#416, #417) + - Add 'id' and 'timeoffset' fields to getpeerinfo result (#335) + - Websocket changes: + - Implement new commands stopnotifyspent, stopnotifyreceived, + stopnotifyblocks, and stopnotifynewtransactions to allow clients to + cancel notification registrations (#122, #342) + - btcctl utility changes: + - A single dash can now be used as an argument to cause that argument to + be read from stdin (#348) + - Add generate command + - Notable developer-related package changes: + - The new version 2 btcjson package has now replaced the deprecated + version 1 package (#368) + - The btcec package now performs all signing using RFC6979 deterministic + signatures (#358, #360) + - The txscript package has been significantly cleaned up and had a few + API changes (#387, #388, #389, #390, #391, #392, #393, #395, #396, + #400, #403, #404, #405, #406, #408, #409, #410, #412) + - A new PkScriptLocs function has been added to the wire package MsgTx + type which provides callers that deal with scripts optimization + opportunities (#343) + - Misc changes: + - Minor wire hashing optimizations (#366, #367) + - Other minor internal optimizations + - Contributors (alphabetical order): + - Alex Akselrod + - Arne Brutschy + - Chris Jepson + - Daniel Krawisz + - Dave Collins + - David Hill + - Jimmy Song + - Jonas Nick + - Josh Rickmar + - Olaoluwa Osuntokun + - Oleg Andreev + +Changes in 0.10.0 (Sun Mar 01 2015) + - Protocol and network related changes: + - Add a new checkpoint at block height 343185 + - Implement BIP066 which includes support for version 3 blocks, a new + consensus rule which prevents non-DER encoded signatures, and a + double-threshold switchover mechanism + - Rather than announcing all known addresses on getaddr requests which + can possibly result in multiple messages, randomize the results and + limit them to the max allowed by a single message (1000 addresses) + - Add more reserved IP spaces to the address manager + - Transaction relay (memory pool) changes: + - Make transactions which contain reserved opcodes nonstandard + - No longer accept or relay free and low-fee transactions that have + insufficient priority to be mined in the next block + - Implement support for the following rules specified by BIP0062: + - ECDSA signature must use strict DER encoding (rule 1) + - The signature script must only contain push operations (rule 2) + - All push operations must use the smallest possible encoding (rule 3) + - All stack values interpreted as a number must be encoding using the + shortest possible form (rule 4) + - NOTE: Rule 1 was already enforced, however the entire script now + evaluates to false rather than only the signature verification as + required by BIP0062 + - Allow transactions with nulldata transaction outputs to be treated as + standard + - Mining support changes: + - Modify the getblocktemplate RPC to generate and return block templates + for version 3 blocks which are compatible with BIP0066 + - Allow getblocktemplate to serve blocks when the current time is + less than the minimum allowed time for a generated block template + (https://github.com/btcsuite/btcd/issues/209) + - Crypto changes: + - Optimize scalar multiplication by the base point by using a + pre-computed table which results in approximately a 35% speedup + (https://github.com/btcsuite/btcec/issues/2) + - Optimize general scalar multiplication by using the secp256k1 + endomorphism which results in approximately a 17-20% speedup + (https://github.com/btcsuite/btcec/issues/1) + - Optimize general scalar multiplication by using non-adjacent form + which results in approximately an additional 8% speedup + (https://github.com/btcsuite/btcec/issues/3) + - Implement optional address indexing: + - Add a new parameter --addrindex which will enable the creation of an + address index which can be queried to determine all transactions which + involve a given address + (https://github.com/btcsuite/btcd/issues/190) + - Add a new logging subsystem for address index related operations + - Support new searchrawtransactions RPC + (https://github.com/btcsuite/btcd/issues/185) + - RPC changes: + - Require TLS version 1.2 as the minimum version for all TLS connections + - Provide support for disabling TLS when only listening on localhost + (https://github.com/btcsuite/btcd/pull/192) + - Modify help output for all commands to provide much more consistent + and detailed information + - Correct case in getrawtransaction which would refuse to serve certain + transactions with invalid scripts + (https://github.com/btcsuite/btcd/issues/210) + - Correct error handling in the getrawtransaction RPC which could lead + to a crash in rare cases + (https://github.com/btcsuite/btcd/issues/196) + - Update getinfo RPC to include the appropriate 'timeoffset' calculated + from the median network time + - Modify listreceivedbyaddress result type to include txids field so it + is compatible + - Add 'iswatchonly' field to validateaddress result + - Add 'startingpriority' and 'currentpriority' fields to getrawmempool + (https://github.com/btcsuite/btcd/issues/178) + - Don't omit the 'confirmations' field from getrawtransaction when it is + zero + - Websocket changes: + - Modify the behavior of the rescan command to automatically register + for notifications about transactions paying to rescanned addresses + or spending outputs from the final rescan utxo set when the rescan + is through the best block in the chain + - btcctl utility changes: + - Make the list of commands available via the -l option rather than + dumping the entire list on usage errors + - Alphabetize and categorize the list of commands by chain and wallet + - Make the help option only show the help options instead of also + dumping all of the commands + - Make the usage syntax much more consistent and correct a few cases of + misnamed fields + (https://github.com/btcsuite/btcd/issues/305) + - Improve usage errors to show the specific parameter number, reason, + and error code + - Only show the usage for specific command is shown when a valid command + is provided with invalid parameters + - Add support for a SOCK5 proxy + - Modify output for integer fields (such as timestamps) to display + normally instead in scientific notation + - Add invalidateblock command + - Add reconsiderblock command + - Add createnewaccount command + - Add renameaccount command + - Add searchrawtransactions command + - Add importaddress command + - Add importpubkey command + - showblock utility changes: + - Remove utility in favor of the RPC getblock method + - Notable developer-related package changes: + - Many of the core packages have been relocated into the btcd repository + (https://github.com/btcsuite/btcd/issues/214) + - A new version of the btcjson package that has been completely + redesigned from the ground up based based upon how the project has + evolved and lessons learned while using it since it was first written + is now available in the btcjson/v2/btcjson directory + - This will ultimately replace the current version so anyone making + use of this package will need to update their code accordingly + - The btcec package now provides better facilities for working directly + with its public and private keys without having to mix elements from + the ecdsa package + - Update the script builder to ensure all rules specified by BIP0062 are + adhered to when creating scripts + - The blockchain package now provides a MedianTimeSource interface and + concrete implementation for providing time samples from remote peers + and using that data to calculate an offset against the local time + - Misc changes: + - Fix a slow memory leak due to tickers not being stopped + (https://github.com/btcsuite/btcd/issues/189) + - Fix an issue where a mix of orphans and SPV clients could trigger a + condition where peers would no longer be served + (https://github.com/btcsuite/btcd/issues/231) + - The RPC username and password can now contain symbols which previously + conflicted with special symbols used in URLs + - Improve handling of obtaining random nonces to prevent cases where it + could error when not enough entropy was available + - Improve handling of home directory creation errors such as in the case + of unmounted symlinks (https://github.com/btcsuite/btcd/issues/193) + - Improve the error reporting for rejected transactions to include the + inputs which are missing and/or being double spent + - Update sample config file with new options and correct a comment + regarding the fact the RPC server only listens on localhost by default + (https://github.com/btcsuite/btcd/issues/218) + - Update the continuous integration builds to run several tools which + help keep code quality high + - Significant amount of internal code cleanup and improvements + - Other minor internal optimizations + - Code Contributors (alphabetical order): + - Beldur + - Ben Holden-Crowther + - Dave Collins + - David Evans + - David Hill + - Guilherme Salgado + - Javed Khan + - Jimmy Song + - John C. Vernaleo + - Jonathan Gillham + - Josh Rickmar + - Michael Ford + - Michail Kargakis + - kac + - Olaoluwa Osuntokun + +Changes in 0.9.0 (Sat Sep 20 2014) + - Protocol and network related changes: + - Add a new checkpoint at block height 319400 + - Add support for BIP0037 bloom filters + (https://github.com/conformal/btcd/issues/132) + - Implement BIP0061 reject handling and hence support for protocol + version 70002 (https://github.com/conformal/btcd/issues/133) + - Add testnet DNS seeds for peer discovery (testnet-seed.alexykot.me + and testnet-seed.bitcoin.schildbach.de) + - Add mainnet DNS seed for peer discovery (seeds.bitcoin.open-nodes.org) + - Make multisig transactions with non-null dummy data nonstandard + (https://github.com/conformal/btcd/issues/131) + - Make transactions with an excessive number of signature operations + nonstandard + - Perform initial DNS lookups concurrently which allows connections + more quickly + - Improve the address manager to significantly reduce memory usage and + add tests + - Remove orphan transactions when they appear in a mined block + (https://github.com/conformal/btcd/issues/166) + - Apply incremental back off on connection retries for persistent peers + that give invalid replies to mirror the logic used for failed + connections (https://github.com/conformal/btcd/issues/103) + - Correct rate-limiting of free and low-fee transactions + - Mining support changes: + - Implement getblocktemplate RPC with the following support: + (https://github.com/conformal/btcd/issues/124) + - BIP0022 Non-Optional Sections + - BIP0022 Long Polling + - BIP0023 Basic Pool Extensions + - BIP0023 Mutation coinbase/append + - BIP0023 Mutations time, time/increment, and time/decrement + - BIP0023 Mutation transactions/add + - BIP0023 Mutations prevblock, coinbase, and generation + - BIP0023 Block Proposals + - Implement built-in concurrent CPU miner + (https://github.com/conformal/btcd/issues/137) + NOTE: CPU mining on mainnet is pointless. This has been provided + for testing purposes such as for the new simulation test network + - Add --generate flag to enable CPU mining + - Deprecate the --getworkkey flag in favor of --miningaddr which + specifies which addresses generated blocks will choose from to pay + the subsidy to + - RPC changes: + - Implement gettxout command + (https://github.com/conformal/btcd/issues/141) + - Implement validateaddress command + - Implement verifymessage command + - Mark getunconfirmedbalance RPC as wallet-only + - Mark getwalletinfo RPC as wallet-only + - Update getgenerate, setgenerate, gethashespersec, and getmininginfo + to return the appropriate information about new CPU mining status + - Modify getpeerinfo pingtime and pingwait field types to float64 so + they are compatible + - Improve disconnect handling for normal HTTP clients + - Make error code returns for invalid hex more consistent + - Websocket changes: + - Switch to a new more efficient websocket package + (https://github.com/conformal/btcd/issues/134) + - Add rescanfinished notification + - Modify the rescanprogress notification to include block hash as well + as height (https://github.com/conformal/btcd/issues/151) + - btcctl utility changes: + - Accept --simnet flag which automatically selects the appropriate port + and TLS certificates needed to communicate with btcd and btcwallet on + the simulation test network + - Fix createrawtransaction command to send amounts denominated in BTC + - Add estimatefee command + - Add estimatepriority command + - Add getmininginfo command + - Add getnetworkinfo command + - Add gettxout command + - Add lockunspent command + - Add signrawtransaction command + - addblock utility changes: + - Accept --simnet flag which automatically selects the appropriate port + and TLS certificates needed to communicate with btcd and btcwallet on + the simulation test network + - Notable developer-related package changes: + - Provide a new bloom package in btcutil which allows creating and + working with BIP0037 bloom filters + - Provide a new hdkeychain package in btcutil which allows working with + BIP0032 hierarchical deterministic key chains + - Introduce a new btcnet package which houses network parameters + - Provide new simnet network (--simnet) which is useful for private + simulation testing + - Enforce low S values in serialized signatures as detailed in BIP0062 + - Return errors from all methods on the btcdb.Db interface + (https://github.com/conformal/btcdb/issues/5) + - Allow behavior flags to alter btcchain.ProcessBlock + (https://github.com/conformal/btcchain/issues/5) + - Provide a new SerializeSize API for blocks + (https://github.com/conformal/btcwire/issues/19) + - Several of the core packages now work with Google App Engine + - Misc changes: + - Correct an issue where the database could corrupt under certain + circumstances which would require a new chain download + - Slightly optimize deserialization + - Use the correct IP block for he.net + - Fix an issue where it was possible the block manager could hang on + shutdown + - Update sample config file so the comments are on a separate line + rather than the end of a line so they are not interpreted as settings + (https://github.com/conformal/btcd/issues/135) + - Correct an issue where getdata requests were not being properly + throttled which could lead to larger than necessary memory usage + - Always show help when given the help flag even when the config file + contains invalid entries + - General code cleanup and minor optimizations + +Changes in 0.8.0-beta (Sun May 25 2014) + - Btcd is now Beta (https://github.com/conformal/btcd/issues/130) + - Add a new checkpoint at block height 300255 + - Protocol and network related changes: + - Lower the minimum transaction relay fee to 1000 satoshi to match + recent reference client changes + (https://github.com/conformal/btcd/issues/100) + - Raise the maximum signature script size to support standard 15-of-15 + multi-signature pay-to-sript-hash transactions with compressed pubkeys + to remain compatible with the reference client + (https://github.com/conformal/btcd/issues/128) + - Reduce max bytes allowed for a standard nulldata transaction to 40 for + compatibility with the reference client + - Introduce a new btcnet package which houses all of the network params + for each network (mainnet, testnet3, regtest) to ultimately enable + easier addition and tweaking of networks without needing to change + several packages + - Fix several script discrepancies found by reference client test data + - Add new DNS seed for peer discovery (seed.bitnodes.io) + - Reduce the max known inventory cache from 20000 items to 1000 items + - Fix an issue where unknown inventory types could lead to a hung peer + - Implement inventory rebroadcast handler for sendrawtransaction + (https://github.com/conformal/btcd/issues/99) + - Update user agent to fully support BIP0014 + (https://github.com/conformal/btcwire/issues/10) + - Implement initial mining support: + - Add a new logging subsystem for mining related operations + - Implement infrastructure for creating block templates + - Provide options to control block template creation settings + - Support the getwork RPC + - Allow address identifiers to apply to more than one network since both + testnet3 and the regression test network unfortunately use the same + identifier + - RPC changes: + - Set the content type for HTTP POST RPC connections to application/json + (https://github.com/conformal/btcd/issues/121) + - Modified the RPC server startup so it only requires at least one valid + listen interface + - Correct an error path where it was possible certain errors would not + be returned + - Implement getwork command + (https://github.com/conformal/btcd/issues/125) + - Update sendrawtransaction command to reject orphans + - Update sendrawtransaction command to include the reason a transaction + was rejected + - Update getinfo command to populate connection count field + - Update getinfo command to include relay fee field + (https://github.com/conformal/btcd/issues/107) + - Allow transactions submitted with sendrawtransaction to bypass the + rate limiter + - Allow the getcurrentnet and getbestblock extensions to be accessed via + HTTP POST in addition to Websockets + (https://github.com/conformal/btcd/issues/127) + - Websocket changes: + - Rework notifications to ensure they are delivered in the order they + occur + - Rename notifynewtxs command to notifyreceived (funds received) + - Rename notifyallnewtxs command to notifynewtransactions + - Rename alltx notification to txaccepted + - Rename allverbosetx notification to txacceptedverbose + (https://github.com/conformal/btcd/issues/98) + - Add rescan progress notification + - Add recvtx notification + - Add redeemingtx notification + - Modify notifyspent command to accept an array of outpoints + (https://github.com/conformal/btcd/issues/123) + - Significantly optimize the rescan command to yield up to a 60x speed + increase + - btcctl utility changes: + - Add createencryptedwallet command + - Add getblockchaininfo command + - Add importwallet commmand + - Add addmultisigaddress commmand + - Add setgenerate command + - Accept --testnet and --wallet flags which automatically select + the appropriate port and TLS certificates needed to communicate + with btcd and btcwallet (https://github.com/conformal/btcd/issues/112) + - Allow path expansion from config file entries + (https://github.com/conformal/btcd/issues/113) + - Minor refactor simplify handling of options + - addblock utility changes: + - Improve logging by making it consistent with the logging provided by + btcd (https://github.com/conformal/btcd/issues/90) + - Improve several package APIs for developers: + - Add new amount type for consistently handling monetary values + - Add new coin selector API + - Add new WIF (Wallet Import Format) API + - Add new crypto types for private keys and signatures + - Add new API to sign transactions including script merging and hash + types + - Expose function to extract all pushed data from a script + (https://github.com/conformal/btcscript/issues/8) + - Misc changes: + - Optimize address manager shuffling to do 67% less work on average + - Resolve a couple of benign data races found by the race detector + (https://github.com/conformal/btcd/issues/101) + - Add IP address to all peer related errors to clarify which peer is the + cause (https://github.com/conformal/btcd/issues/102) + - Fix a UPNP case issue that prevented the --upnp option from working + with some UPNP servers + - Update documentation in the sample config file regarding debug levels + - Adjust some logging levels to improve debug messages + - Improve the throughput of query messages to the block manager + - Several minor optimizations to reduce GC churn and enhance speed + - Other minor refactoring + - General code cleanup + +Changes in 0.7.0 (Thu Feb 20 2014) + - Fix an issue when parsing scripts which contain a multi-signature script + which require zero signatures such as testnet block + 000000001881dccfeda317393c261f76d09e399e15e27d280e5368420f442632 + (https://github.com/conformal/btcscript/issues/7) + - Add check to ensure all transactions accepted to mempool only contain + canonical data pushes (https://github.com/conformal/btcscript/issues/6) + - Fix an issue causing excessive memory consumption + - Significantly rework and improve the websocket notification system: + - Each client is now independent so slow clients no longer limit the + speed of other connected clients + - Potentially long-running operations such as rescans are now run in + their own handler and rate-limited to one operation at a time without + preventing simultaneous requests from the same client for the faster + requests or notifications + - A couple of scenarios which could cause shutdown to hang have been + resolved + - Update notifynewtx notifications to support all address types instead + of only pay-to-pubkey-hash + - Provide a --rpcmaxwebsockets option to allow limiting the number of + concurrent websocket clients + - Add a new websocket command notifyallnewtxs to request notifications + (https://github.com/conformal/btcd/issues/86) (thanks @flammit) + - Improve btcctl utility in the following ways: + - Add getnetworkhashps command + - Add gettransaction command (wallet-specific) + - Add signmessage command (wallet-specific) + - Update getwork command to accept + - Continue cleanup and work on implementing the RPC API: + - Implement getnettotals command + (https://github.com/conformal/btcd/issues/84) + - Implement networkhashps command + (https://github.com/conformal/btcd/issues/87) + - Update getpeerinfo to always include syncnode field even when false + - Remove help addenda for getpeerinfo now that it supports all fields + - Close standard RPC connections on auth failure + - Provide a --rpcmaxclients option to allow limiting the number of + concurrent RPC clients (https://github.com/conformal/btcd/issues/68) + - Include IP address in RPC auth failure log messages + - Resolve a rather harmless data races found by the race detector + (https://github.com/conformal/btcd/issues/94) + - Increase block priority size and max standard transaction size to 50k + and 100k, respectively (https://github.com/conformal/btcd/issues/71) + - Add rate limiting of free transactions to the memory pool to prevent + penny flooding (https://github.com/conformal/btcd/issues/40) + - Provide a --logdir option (https://github.com/conformal/btcd/issues/95) + - Change the default log file path to include the network + - Add a new ScriptBuilder interface to btcscript to support creation of + custom scripts (https://github.com/conformal/btcscript/issues/5) + - General code cleanup + +Changes in 0.6.0 (Tue Feb 04 2014) + - Fix an issue when parsing scripts which contain invalid signatures that + caused a chain fork on block + 0000000000000001e4241fd0b3469a713f41c5682605451c05d3033288fb2244 + - Correct an issue which could lead to an error in removeBlockNode + (https://github.com/conformal/btcchain/issues/4) + - Improve addblock utility as follows: + - Check imported blocks against all chain rules and checkpoints + - Skip blocks which are already known so you can stop and restart the + import or start the import after you have already downloaded a portion + of the chain + - Correct an issue where the utility did not shutdown cleanly after + processing all blocks + - Add error on attempt to import orphan blocks + - Improve error handling and reporting + - Display statistics after input file has been fully processed + - Rework, optimize, and improve headers-first mode: + - Resuming the chain sync from any point before the final checkpoint + will now use headers-first mode + (https://github.com/conformal/btcd/issues/69) + - Verify all checkpoints as opposed to only the final one + - Reduce and bound memory usage + - Rollback to the last known good point when a header does not match a + checkpoint + - Log information about what is happening with headers + - Improve btcctl utility in the following ways: + - Add getaddednodeinfo command + - Add getnettotals command + - Add getblocktemplate command (wallet-specific) + - Add getwork command (wallet-specific) + - Add getnewaddress command (wallet-specific) + - Add walletpassphrasechange command (wallet-specific) + - Add walletlock command (wallet-specific) + - Add sendfrom command (wallet-specific) + - Add sendmany command (wallet-specific) + - Add settxfee command (wallet-specific) + - Add listsinceblock command (wallet-specific) + - Add listaccounts command (wallet-specific) + - Add keypoolrefill command (wallet-specific) + - Add getreceivedbyaccount command (wallet-specific) + - Add getrawchangeaddress command (wallet-specific) + - Add gettxoutsetinfo command (wallet-specific) + - Add listaddressgroupings command (wallet-specific) + - Add listlockunspent command (wallet-specific) + - Add listlock command (wallet-specific) + - Add listreceivedbyaccount command (wallet-specific) + - Add validateaddress command (wallet-specific) + - Add verifymessage command (wallet-specific) + - Add sendtoaddress command (wallet-specific) + - Continue cleanup and work on implementing the RPC API: + - Implement submitblock command + (https://github.com/conformal/btcd/issues/61) + - Implement help command + - Implement ping command + - Implement getaddednodeinfo command + (https://github.com/conformal/btcd/issues/78) + - Implement getinfo command + - Update getpeerinfo to support bytesrecv and bytessent + (https://github.com/conformal/btcd/issues/83) + - Improve and correct several RPC server and websocket areas: + - Change the connection endpoint for websockets from /wallet to /ws + (https://github.com/conformal/btcd/issues/80) + - Implement an alternative authentication for websockets so clients + such as javascript from browsers that don't support setting HTTP + headers can authenticate (https://github.com/conformal/btcd/issues/77) + - Add an authentication deadline for RPC connections + (https://github.com/conformal/btcd/issues/68) + - Use standard authentication failure responses for RPC connections + - Make automatically generated certificate more standard so it works + from client such as node.js and Firefox + - Correct some minor issues which could prevent the RPC server from + shutting down in an orderly fashion + - Make all websocket notifications require registration + - Change the data sent over websockets to text since it is JSON-RPC + - Allow connections that do not have an Origin header set + - Expose and track the number of bytes read and written per peer + (https://github.com/conformal/btcwire/issues/6) + - Correct an issue with sendrawtransaction when invoked via websockets + which prevented a minedtx notification from being added + - Rescan operations issued from remote wallets are no stopped when + the wallet disconnects mid-operation + (https://github.com/conformal/btcd/issues/66) + - Several optimizations related to fetching block information from the + database + - General code cleanup + +Changes in 0.5.0 (Mon Jan 13 2014) + - Optimize initial block download by introducing a new mode which + downloads the block headers first (up to the final checkpoint) + - Improve peer handling to remove the potential for slow peers to cause + sluggishness amongst all peers + (https://github.com/conformal/btcd/issues/63) + - Fix an issue where the initial block sync could stall when the sync peer + disconnects (https://github.com/conformal/btcd/issues/62) + - Correct an issue where --externalip was doing a DNS lookup on the full + host:port instead of just the host portion + (https://github.com/conformal/btcd/issues/38) + - Fix an issue which could lead to a panic on chain switches + (https://github.com/conformal/btcd/issues/70) + - Improve btcctl utility in the following ways: + - Show getdifficulty output as floating point to 6 digits of precision + - Show all JSON object replies formatted as standard JSON + - Allow btcctl getblock to accept optional params + - Add getaccount command (wallet-specific) + - Add getaccountaddress command (wallet-specific) + - Add sendrawtransaction command + - Continue cleanup and work on implementing RPC API calls + - Update getrawmempool to support new optional verbose flag + - Update getrawtransaction to match the reference client + - Update getblock to support new optional verbose flag + - Update raw transactions to fully match the reference client including + support for all transaction types and address types + - Correct getrawmempool fee field to return BTC instead of Satoshi + - Correct getpeerinfo service flag to return 8 digit string so it + matches the reference client + - Correct verifychain to return a boolean + - Implement decoderawtransaction command + - Implement createrawtransaction command + - Implement decodescript command + - Implement gethashespersec command + - Allow RPC handler overrides when invoked via a websocket versus + legacy connection + - Add new DNS seed for peer discovery + - Display user agent on new valid peer log message + (https://github.com/conformal/btcd/issues/64) + - Notify wallet when new transactions that pay to registered addresses + show up in the mempool before being mined into a block + - Support a tor-specific proxy in addition to a normal proxy + (https://github.com/conformal/btcd/issues/47) + - Remove deprecated sqlite3 imports from utilities + - Remove leftover profile write from addblock utility + - Quite a bit of code cleanup and refactoring to improve maintainability + +Changes in 0.4.0 (Thu Dec 12 2013) + - Allow listen interfaces to be specified via --listen instead of only the + port (https://github.com/conformal/btcd/issues/33) + - Allow listen interfaces for the RPC server to be specified via + --rpclisten instead of only the port + (https://github.com/conformal/btcd/issues/34) + - Only disable listening when --connect or --proxy are used when no + --listen interface are specified + (https://github.com/conformal/btcd/issues/10) + - Add several new standard transaction checks to transaction memory pool: + - Support nulldata scripts as standard + - Only allow a max of one nulldata output per transaction + - Enforce a maximum of 3 public keys in multi-signature transactions + - The number of signatures in multi-signature transactions must not + exceed the number of public keys + - The number of inputs to a signature script must match the expected + number of inputs for the script type + - The number of inputs pushed onto the stack by a redeeming signature + script must match the number of inputs consumed by the referenced + public key script + - When a block is connected, remove any transactions from the memory pool + which are now double spends as a result of the newly connected + transactions + - Don't relay transactions resurrected during a chain switch since + other peers will also be switching chains and therefore already know + about them + - Cleanup a few cases where rejected transactions showed as an error + rather than as a rejected transaction + - Ignore the default configuration file when --regtest (regression test + mode) is specified + - Implement TLS support for RPC including automatic certificate generation + - Support HTTP authentication headers for web sockets + - Update address manager to recognize and properly work with Tor + addresses (https://github.com/conformal/btcd/issues/36) and + (https://github.com/conformal/btcd/issues/37) + - Improve btcctl utility in the following ways: + - Add the ability to specify a configuration file + - Add a default entry for the RPC cert to point to the location + it will likely be in the btcd home directory + - Implement --version flag + - Provide a --notls option to support non-TLS configurations + - Fix a couple of minor races found by the Go race detector + - Improve logging + - Allow logging level to be specified on a per subsystem basis + (https://github.com/conformal/btcd/issues/48) + - Allow logging levels to be dynamically changed via RPC + (https://github.com/conformal/btcd/issues/15) + - Implement a rolling log file with a max of 10MB per file and a + rotation size of 3 which results in a max logging size of 30 MB + - Correct a minor issue with the rescanning websocket call + (https://github.com/conformal/btcd/issues/54) + - Fix a race with pushing address messages that could lead to a panic + (https://github.com/conformal/btcd/issues/58) + - Improve which external IP address is reported to peers based on which + interface they are connected through + (https://github.com/conformal/btcd/issues/35) + - Add --externalip option to allow an external IP address to be specified + for cases such as tor hidden services or advanced network configurations + (https://github.com/conformal/btcd/issues/38) + - Add --upnp option to support automatic port mapping via UPnP + (https://github.com/conformal/btcd/issues/51) + - Update Ctrl+C interrupt handler to properly sync address manager and + remove the UPnP port mapping (if needed) + - Continue cleanup and work on implementing RPC API calls + - Add importprivkey (import private key) command to btcctl + - Update getrawtransaction to provide addresses properly, support + new verbose param, and match the reference implementation with the + exception of MULTISIG (thanks @flammit) + - Update getblock with new verbose flag (thanks @flammit) + - Add listtransactions command to btcctl + - Add getbalance command to btcctl + - Add basic support for btcd to run as a native Windows service + (https://github.com/conformal/btcd/issues/42) + - Package addblock utility with Windows MSIs + - Add support for TravisCI (continuous build integration) + - Cleanup some documentation and usage + - Several other minor bug fixes and general code cleanup + +Changes in 0.3.3 (Wed Nov 13 2013) + - Significantly improve initial block chain download speed + (https://github.com/conformal/btcd/issues/20) + - Add a new checkpoint at block height 267300 + - Optimize most recently used inventory handling + (https://github.com/conformal/btcd/issues/21) + - Optimize duplicate transaction input check + (https://github.com/conformal/btcchain/issues/2) + - Optimize transaction hashing + (https://github.com/conformal/btcd/issues/25) + - Rework and optimize wallet listener notifications + (https://github.com/conformal/btcd/issues/22) + - Optimize serialization and deserialization + (https://github.com/conformal/btcd/issues/27) + - Add support for minimum transaction fee to memory pool acceptance + (https://github.com/conformal/btcd/issues/29) + - Improve leveldb database performance by removing explicit GC call + - Fix an issue where Ctrl+C was not always finishing orderly database + shutdown + - Fix an issue in the script handling for OP_CHECKSIG + - Impose max limits on all variable length protocol entries to prevent + abuse from malicious peers + - Enforce DER signatures for transactions allowed into the memory pool + - Separate the debug profile http server from the RPC server + - Rework of the RPC code to improve performance and make the code cleaner + - The getrawtransaction RPC call now properly checks the memory pool + before consulting the db (https://github.com/conformal/btcd/issues/26) + - Add support for the following RPC calls: getpeerinfo, getconnectedcount, + addnode, verifychain + (https://github.com/conformal/btcd/issues/13) + (https://github.com/conformal/btcd/issues/17) + - Implement rescan websocket extension to allow wallet rescans + - Use correct paths for application data storage for all supported + operating systems (https://github.com/conformal/btcd/issues/30) + - Add a default redirect to the http profiling page when accessing the + http profile server + - Add a new --cpuprofile option which can be used to generate CPU + profiling data on platforms that support it + - Several other minor performance optimizations + - Other minor bug fixes and general code cleanup + +Changes in 0.3.2 (Tue Oct 22 2013) + - Fix an issue that could cause the download of the block chain to stall + (https://github.com/conformal/btcd/issues/12) + - Remove deprecated sqlite as an available database backend + - Close sqlite compile issue as sqlite has now been removed + (https://github.com/conformal/btcd/issues/11) + - Change default RPC ports to 8334 (mainnet) and 18334 (testnet) + - Continue cleanup and work on implementing RPC API calls + - Add support for the following RPC calls: getrawmempool, + getbestblockhash, decoderawtransaction, getdifficulty, + getconnectioncount, getpeerinfo, and addnode + - Improve the btcctl utility that is used to issue JSON-RPC commands + - Fix an issue preventing btcd from cleanly shutting down with the RPC + stop command + - Add a number of database interface tests to ensure backends implement + the expected interface + - Expose some additional information from btcscript to be used for + identifying "standard"" transactions + - Add support for plan9 - thanks @mischief + (https://github.com/conformal/btcd/pull/19) + - Other minor bug fixes and general code cleanup + +Changes in 0.3.1-alpha (Tue Oct 15 2013) + - Change default database to leveldb + NOTE: This does mean you will have to redownload the block chain. Since we + are still in alpha, we didn't feel writing a converter was worth the time as + it would take away from more important issues at this stage + - Add a warning if there are multiple block chain databases of different types + - Fix issue with unexpected EOF in leveldb -- https://github.com/conformal/btcd/issues/18 + - Fix issue preventing block 21066 on testnet -- https://github.com/conformal/btcchain/issues/1 + - Fix issue preventing block 96464 on testnet -- https://github.com/conformal/btcscript/issues/1 + - Optimize transaction lookups + - Correct a few cases of list removal that could result in improper cleanup + of no longer needed orphans + - Add functionality to increase ulimits on non-Windows platforms + - Add support for mempool command which allows remote peers to query the + transaction memory pool via the bitcoin protocol + - Clean up logging a bit + - Add a flag to disable checkpoints for developers + - Add a lot of useful debug logging such as message summaries + - Other minor bug fixes and general code cleanup + +Initial Release 0.3.0-alpha (Sat Oct 05 2013): + - Initial release diff --git a/vendor/github.com/btcsuite/btcd/LICENSE b/vendor/github.com/btcsuite/btcd/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..ea6a3d2de48f5e141d558b24a176eb0edbeafa1d --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2013-2016 The btcsuite developers + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/github.com/btcsuite/btcd/README.md b/vendor/github.com/btcsuite/btcd/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8525bb49eeb89d0c0ded1515a39d1dd3d113557b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/README.md @@ -0,0 +1,145 @@ +btcd +==== + +[] +(https://travis-ci.org/btcsuite/btcd) + +btcd is an alternative full node bitcoin implementation written in Go (golang). + +This project is currently under active development and is in a Beta state. It +is extremely stable and has been in production use since October 2013. + +It properly downloads, validates, and serves the block chain using the exact +rules (including bugs) for block acceptance as Bitcoin Core. We have taken +great care to avoid btcd causing a fork to the block chain. It passes all of +the 'official' block acceptance tests +(https://github.com/TheBlueMatt/test-scripts) as well as all of the JSON test +data in the Bitcoin Core code. + +It also relays newly mined blocks, maintains a transaction pool, and relays +individual transactions that have not yet made it into a block. It ensures all +transactions admitted to the pool follow the rules required by the block chain +and also includes the same checks which filter transactions based on +miner requirements ("standard" transactions) as Bitcoin Core. + +One key difference between btcd and Bitcoin Core is that btcd does *NOT* include +wallet functionality and this was a very intentional design decision. See the +blog entry [here](https://blog.conformal.com/btcd-not-your-moms-bitcoin-daemon) +for more details. This means you can't actually make or receive payments +directly with btcd. That functionality is provided by the +[btcwallet](https://github.com/btcsuite/btcwallet) and +[Paymetheus](https://github.com/btcsuite/Paymetheus) (Windows-only) projects +which are both under active development. + +## Requirements + +[Go](http://golang.org) 1.5 or newer. + +## Installation + +#### Windows - MSI Available + +https://github.com/btcsuite/btcd/releases + +#### Linux/BSD/MacOSX/POSIX - Build from Source + +- Install Go according to the installation instructions here: + http://golang.org/doc/install + +- Ensure Go was installed properly and is a supported version: + +```bash +$ go version +$ go env GOROOT GOPATH +``` + +NOTE: The `GOROOT` and `GOPATH` above must not be the same path. It is +recommended that `GOPATH` is set to a directory in your home directory such as +`~/goprojects` to avoid write permission issues. + +- Run the following command to obtain btcd, all dependencies, and install it: + +```bash +$ go get -u github.com/btcsuite/btcd/... +``` + +- btcd (and utilities) will now be installed in either ```$GOROOT/bin``` or + ```$GOPATH/bin``` depending on your configuration. If you did not already + add the bin directory to your system path during Go installation, we + recommend you do so now. + +## Updating + +#### Windows + +Install a newer MSI + +#### Linux/BSD/MacOSX/POSIX - Build from Source + +- Run the following command to update btcd, all dependencies, and install it: + +```bash +$ go get -u -v github.com/btcsuite/btcd/... +``` + +## Getting Started + +btcd has several configuration options avilable to tweak how it runs, but all +of the basic operations described in the intro section work with zero +configuration. + +#### Windows (Installed from MSI) + +Launch btcd from your Start menu. + +#### Linux/BSD/POSIX/Source + +```bash +$ ./btcd +```` + +## IRC + +- irc.freenode.net +- channel #btcd +- [webchat](https://webchat.freenode.net/?channels=btcd) + +## Mailing lists + +- btcd: discussion of btcd and its packages. +- btcd-commits: readonly mail-out of source code changes. + +To subscribe to a given list, send email to list+subscribe@opensource.conformal.com + +## Issue Tracker + +The [integrated github issue tracker](https://github.com/btcsuite/btcd/issues) +is used for this project. + +## Documentation + +The documentation is a work-in-progress. It is located in the [docs](https://github.com/btcsuite/btcd/tree/master/docs) folder. + +## GPG Verification Key + +All official release tags are signed by Conformal so users can ensure the code +has not been tampered with and is coming from the btcsuite developers. To +verify the signature perform the following: + +- Download the public key from the Conformal website at + https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt + +- Import the public key into your GPG keyring: + ```bash + gpg --import GIT-GPG-KEY-conformal.txt + ``` + +- Verify the release tag with the following command where `TAG_NAME` is a + placeholder for the specific tag: + ```bash + git tag -v TAG_NAME + ``` + +## License + +btcd is licensed under the [copyfree](http://copyfree.org) ISC License. diff --git a/vendor/github.com/btcsuite/btcd/addrmgr/addrmanager.go b/vendor/github.com/btcsuite/btcd/addrmgr/addrmanager.go new file mode 100644 index 0000000000000000000000000000000000000000..9ac321881a33d974d2f4b747e25904c544519d58 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/addrmgr/addrmanager.go @@ -0,0 +1,1100 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package addrmgr + +import ( + "container/list" + crand "crypto/rand" // for seeding + "encoding/base32" + "encoding/binary" + "encoding/json" + "fmt" + "io" + "math/rand" + "net" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/btcsuite/btcd/wire" +) + +// AddrManager provides a concurrency safe address manager for caching potential +// peers on the bitcoin network. +type AddrManager struct { + mtx sync.Mutex + peersFile string + lookupFunc func(string) ([]net.IP, error) + rand *rand.Rand + key [32]byte + addrIndex map[string]*KnownAddress // address key to ka for all addrs. + addrNew [newBucketCount]map[string]*KnownAddress + addrTried [triedBucketCount]*list.List + started int32 + shutdown int32 + wg sync.WaitGroup + quit chan struct{} + nTried int + nNew int + lamtx sync.Mutex + localAddresses map[string]*localAddress +} + +type serializedKnownAddress struct { + Addr string + Src string + Attempts int + TimeStamp int64 + LastAttempt int64 + LastSuccess int64 + // no refcount or tried, that is available from context. +} + +type serializedAddrManager struct { + Version int + Key [32]byte + Addresses []*serializedKnownAddress + NewBuckets [newBucketCount][]string // string is NetAddressKey + TriedBuckets [triedBucketCount][]string +} + +type localAddress struct { + na *wire.NetAddress + score AddressPriority +} + +// AddressPriority type is used to describe the hierarchy of local address +// discovery methods. +type AddressPriority int + +const ( + // InterfacePrio signifies the address is on a local interface + InterfacePrio AddressPriority = iota + + // BoundPrio signifies the address has been explicitly bounded to. + BoundPrio + + // UpnpPrio signifies the address was obtained from UPnP. + UpnpPrio + + // HTTPPrio signifies the address was obtained from an external HTTP service. + HTTPPrio + + // ManualPrio signifies the address was provided by --externalip. + ManualPrio +) + +const ( + // needAddressThreshold is the number of addresses under which the + // address manager will claim to need more addresses. + needAddressThreshold = 1000 + + newAddressBufferSize = 50 + + // dumpAddressInterval is the interval used to dump the address + // cache to disk for future use. + dumpAddressInterval = time.Minute * 10 + + // triedBucketSize is the maximum number of addresses in each + // tried address bucket. + triedBucketSize = 256 + + // triedBucketCount is the number of buckets we split tried + // addresses over. + triedBucketCount = 64 + + // newBucketSize is the maximum number of addresses in each new address + // bucket. + newBucketSize = 64 + + // newBucketCount is the number of buckets that we spread new addresses + // over. + newBucketCount = 1024 + + // triedBucketsPerGroup is the number of tried buckets over which an + // address group will be spread. + triedBucketsPerGroup = 8 + + // newBucketsPerGroup is the number of new buckets over which an + // source address group will be spread. + newBucketsPerGroup = 64 + + // newBucketsPerAddress is the number of buckets a frequently seen new + // address may end up in. + newBucketsPerAddress = 8 + + // numMissingDays is the number of days before which we assume an + // address has vanished if we have not seen it announced in that long. + numMissingDays = 30 + + // numRetries is the number of tried without a single success before + // we assume an address is bad. + numRetries = 3 + + // maxFailures is the maximum number of failures we will accept without + // a success before considering an address bad. + maxFailures = 10 + + // minBadDays is the number of days since the last success before we + // will consider evicting an address. + minBadDays = 7 + + // getAddrMax is the most addresses that we will send in response + // to a getAddr (in practise the most addresses we will return from a + // call to AddressCache()). + getAddrMax = 2500 + + // getAddrPercent is the percentage of total addresses known that we + // will share with a call to AddressCache. + getAddrPercent = 23 + + // serialisationVersion is the current version of the on-disk format. + serialisationVersion = 1 +) + +// updateAddress is a helper function to either update an address already known +// to the address manager, or to add the address if not already known. +func (a *AddrManager) updateAddress(netAddr, srcAddr *wire.NetAddress) { + // Filter out non-routable addresses. Note that non-routable + // also includes invalid and local addresses. + if !IsRoutable(netAddr) { + return + } + + addr := NetAddressKey(netAddr) + ka := a.find(netAddr) + if ka != nil { + // TODO(oga) only update addresses periodically. + // Update the last seen time and services. + // note that to prevent causing excess garbage on getaddr + // messages the netaddresses in addrmaanger are *immutable*, + // if we need to change them then we replace the pointer with a + // new copy so that we don't have to copy every na for getaddr. + if netAddr.Timestamp.After(ka.na.Timestamp) || + (ka.na.Services&netAddr.Services) != + netAddr.Services { + + naCopy := *ka.na + naCopy.Timestamp = netAddr.Timestamp + naCopy.AddService(netAddr.Services) + ka.na = &naCopy + } + + // If already in tried, we have nothing to do here. + if ka.tried { + return + } + + // Already at our max? + if ka.refs == newBucketsPerAddress { + return + } + + // The more entries we have, the less likely we are to add more. + // likelihood is 2N. + factor := int32(2 * ka.refs) + if a.rand.Int31n(factor) != 0 { + return + } + } else { + // Make a copy of the net address to avoid races since it is + // updated elsewhere in the addrmanager code and would otherwise + // change the actual netaddress on the peer. + netAddrCopy := *netAddr + ka = &KnownAddress{na: &netAddrCopy, srcAddr: srcAddr} + a.addrIndex[addr] = ka + a.nNew++ + // XXX time penalty? + } + + bucket := a.getNewBucket(netAddr, srcAddr) + + // Already exists? + if _, ok := a.addrNew[bucket][addr]; ok { + return + } + + // Enforce max addresses. + if len(a.addrNew[bucket]) > newBucketSize { + log.Tracef("new bucket is full, expiring old") + a.expireNew(bucket) + } + + // Add to new bucket. + ka.refs++ + a.addrNew[bucket][addr] = ka + + log.Tracef("Added new address %s for a total of %d addresses", addr, + a.nTried+a.nNew) +} + +// expireNew makes space in the new buckets by expiring the really bad entries. +// If no bad entries are available we look at a few and remove the oldest. +func (a *AddrManager) expireNew(bucket int) { + // First see if there are any entries that are so bad we can just throw + // them away. otherwise we throw away the oldest entry in the cache. + // Bitcoind here chooses four random and just throws the oldest of + // those away, but we keep track of oldest in the initial traversal and + // use that information instead. + var oldest *KnownAddress + for k, v := range a.addrNew[bucket] { + if v.isBad() { + log.Tracef("expiring bad address %v", k) + delete(a.addrNew[bucket], k) + v.refs-- + if v.refs == 0 { + a.nNew-- + delete(a.addrIndex, k) + } + continue + } + if oldest == nil { + oldest = v + } else if !v.na.Timestamp.After(oldest.na.Timestamp) { + oldest = v + } + } + + if oldest != nil { + key := NetAddressKey(oldest.na) + log.Tracef("expiring oldest address %v", key) + + delete(a.addrNew[bucket], key) + oldest.refs-- + if oldest.refs == 0 { + a.nNew-- + delete(a.addrIndex, key) + } + } +} + +// pickTried selects an address from the tried bucket to be evicted. +// We just choose the eldest. Bitcoind selects 4 random entries and throws away +// the older of them. +func (a *AddrManager) pickTried(bucket int) *list.Element { + var oldest *KnownAddress + var oldestElem *list.Element + for e := a.addrTried[bucket].Front(); e != nil; e = e.Next() { + ka := e.Value.(*KnownAddress) + if oldest == nil || oldest.na.Timestamp.After(ka.na.Timestamp) { + oldestElem = e + oldest = ka + } + + } + return oldestElem +} + +func (a *AddrManager) getNewBucket(netAddr, srcAddr *wire.NetAddress) int { + // bitcoind: + // doublesha256(key + sourcegroup + int64(doublesha256(key + group + sourcegroup))%bucket_per_source_group) % num_new_buckets + + data1 := []byte{} + data1 = append(data1, a.key[:]...) + data1 = append(data1, []byte(GroupKey(netAddr))...) + data1 = append(data1, []byte(GroupKey(srcAddr))...) + hash1 := wire.DoubleSha256(data1) + hash64 := binary.LittleEndian.Uint64(hash1) + hash64 %= newBucketsPerGroup + var hashbuf [8]byte + binary.LittleEndian.PutUint64(hashbuf[:], hash64) + data2 := []byte{} + data2 = append(data2, a.key[:]...) + data2 = append(data2, GroupKey(srcAddr)...) + data2 = append(data2, hashbuf[:]...) + + hash2 := wire.DoubleSha256(data2) + return int(binary.LittleEndian.Uint64(hash2) % newBucketCount) +} + +func (a *AddrManager) getTriedBucket(netAddr *wire.NetAddress) int { + // bitcoind hashes this as: + // doublesha256(key + group + truncate_to_64bits(doublesha256(key)) % buckets_per_group) % num_buckets + data1 := []byte{} + data1 = append(data1, a.key[:]...) + data1 = append(data1, []byte(NetAddressKey(netAddr))...) + hash1 := wire.DoubleSha256(data1) + hash64 := binary.LittleEndian.Uint64(hash1) + hash64 %= triedBucketsPerGroup + var hashbuf [8]byte + binary.LittleEndian.PutUint64(hashbuf[:], hash64) + data2 := []byte{} + data2 = append(data2, a.key[:]...) + data2 = append(data2, GroupKey(netAddr)...) + data2 = append(data2, hashbuf[:]...) + + hash2 := wire.DoubleSha256(data2) + return int(binary.LittleEndian.Uint64(hash2) % triedBucketCount) +} + +// addressHandler is the main handler for the address manager. It must be run +// as a goroutine. +func (a *AddrManager) addressHandler() { + dumpAddressTicker := time.NewTicker(dumpAddressInterval) + defer dumpAddressTicker.Stop() +out: + for { + select { + case <-dumpAddressTicker.C: + a.savePeers() + + case <-a.quit: + break out + } + } + a.savePeers() + a.wg.Done() + log.Trace("Address handler done") +} + +// savePeers saves all the known addresses to a file so they can be read back +// in at next run. +func (a *AddrManager) savePeers() { + a.mtx.Lock() + defer a.mtx.Unlock() + + // First we make a serialisable datastructure so we can encode it to + // json. + sam := new(serializedAddrManager) + sam.Version = serialisationVersion + copy(sam.Key[:], a.key[:]) + + sam.Addresses = make([]*serializedKnownAddress, len(a.addrIndex)) + i := 0 + for k, v := range a.addrIndex { + ska := new(serializedKnownAddress) + ska.Addr = k + ska.TimeStamp = v.na.Timestamp.Unix() + ska.Src = NetAddressKey(v.srcAddr) + ska.Attempts = v.attempts + ska.LastAttempt = v.lastattempt.Unix() + ska.LastSuccess = v.lastsuccess.Unix() + // Tried and refs are implicit in the rest of the structure + // and will be worked out from context on unserialisation. + sam.Addresses[i] = ska + i++ + } + for i := range a.addrNew { + sam.NewBuckets[i] = make([]string, len(a.addrNew[i])) + j := 0 + for k := range a.addrNew[i] { + sam.NewBuckets[i][j] = k + j++ + } + } + for i := range a.addrTried { + sam.TriedBuckets[i] = make([]string, a.addrTried[i].Len()) + j := 0 + for e := a.addrTried[i].Front(); e != nil; e = e.Next() { + ka := e.Value.(*KnownAddress) + sam.TriedBuckets[i][j] = NetAddressKey(ka.na) + j++ + } + } + + w, err := os.Create(a.peersFile) + if err != nil { + log.Errorf("Error opening file %s: %v", a.peersFile, err) + return + } + enc := json.NewEncoder(w) + defer w.Close() + if err := enc.Encode(&sam); err != nil { + log.Errorf("Failed to encode file %s: %v", a.peersFile, err) + return + } +} + +// loadPeers loads the known address from the saved file. If empty, missing, or +// malformed file, just don't load anything and start fresh +func (a *AddrManager) loadPeers() { + a.mtx.Lock() + defer a.mtx.Unlock() + + err := a.deserializePeers(a.peersFile) + if err != nil { + log.Errorf("Failed to parse file %s: %v", a.peersFile, err) + // if it is invalid we nuke the old one unconditionally. + err = os.Remove(a.peersFile) + if err != nil { + log.Warnf("Failed to remove corrupt peers file %s: %v", + a.peersFile, err) + } + a.reset() + return + } + log.Infof("Loaded %d addresses from file '%s'", a.numAddresses(), a.peersFile) +} + +func (a *AddrManager) deserializePeers(filePath string) error { + + _, err := os.Stat(filePath) + if os.IsNotExist(err) { + return nil + } + r, err := os.Open(filePath) + if err != nil { + return fmt.Errorf("%s error opening file: %v", filePath, err) + } + defer r.Close() + + var sam serializedAddrManager + dec := json.NewDecoder(r) + err = dec.Decode(&sam) + if err != nil { + return fmt.Errorf("error reading %s: %v", filePath, err) + } + + if sam.Version != serialisationVersion { + return fmt.Errorf("unknown version %v in serialized "+ + "addrmanager", sam.Version) + } + copy(a.key[:], sam.Key[:]) + + for _, v := range sam.Addresses { + ka := new(KnownAddress) + ka.na, err = a.DeserializeNetAddress(v.Addr) + if err != nil { + return fmt.Errorf("failed to deserialize netaddress "+ + "%s: %v", v.Addr, err) + } + ka.srcAddr, err = a.DeserializeNetAddress(v.Src) + if err != nil { + return fmt.Errorf("failed to deserialize netaddress "+ + "%s: %v", v.Src, err) + } + ka.attempts = v.Attempts + ka.lastattempt = time.Unix(v.LastAttempt, 0) + ka.lastsuccess = time.Unix(v.LastSuccess, 0) + a.addrIndex[NetAddressKey(ka.na)] = ka + } + + for i := range sam.NewBuckets { + for _, val := range sam.NewBuckets[i] { + ka, ok := a.addrIndex[val] + if !ok { + return fmt.Errorf("newbucket contains %s but "+ + "none in address list", val) + } + + if ka.refs == 0 { + a.nNew++ + } + ka.refs++ + a.addrNew[i][val] = ka + } + } + for i := range sam.TriedBuckets { + for _, val := range sam.TriedBuckets[i] { + ka, ok := a.addrIndex[val] + if !ok { + return fmt.Errorf("Newbucket contains %s but "+ + "none in address list", val) + } + + ka.tried = true + a.nTried++ + a.addrTried[i].PushBack(ka) + } + } + + // Sanity checking. + for k, v := range a.addrIndex { + if v.refs == 0 && !v.tried { + return fmt.Errorf("address %s after serialisation "+ + "with no references", k) + } + + if v.refs > 0 && v.tried { + return fmt.Errorf("address %s after serialisation "+ + "which is both new and tried!", k) + } + } + + return nil +} + +// DeserializeNetAddress converts a given address string to a *wire.NetAddress +func (a *AddrManager) DeserializeNetAddress(addr string) (*wire.NetAddress, error) { + host, portStr, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + port, err := strconv.ParseUint(portStr, 10, 16) + if err != nil { + return nil, err + } + + return a.HostToNetAddress(host, uint16(port), wire.SFNodeNetwork) +} + +// Start begins the core address handler which manages a pool of known +// addresses, timeouts, and interval based writes. +func (a *AddrManager) Start() { + // Already started? + if atomic.AddInt32(&a.started, 1) != 1 { + return + } + + log.Trace("Starting address manager") + + // Load peers we already know about from file. + a.loadPeers() + + // Start the address ticker to save addresses periodically. + a.wg.Add(1) + go a.addressHandler() +} + +// Stop gracefully shuts down the address manager by stopping the main handler. +func (a *AddrManager) Stop() error { + if atomic.AddInt32(&a.shutdown, 1) != 1 { + log.Warnf("Address manager is already in the process of " + + "shutting down") + return nil + } + + log.Infof("Address manager shutting down") + close(a.quit) + a.wg.Wait() + return nil +} + +// AddAddresses adds new addresses to the address manager. It enforces a max +// number of addresses and silently ignores duplicate addresses. It is +// safe for concurrent access. +func (a *AddrManager) AddAddresses(addrs []*wire.NetAddress, srcAddr *wire.NetAddress) { + a.mtx.Lock() + defer a.mtx.Unlock() + + for _, na := range addrs { + a.updateAddress(na, srcAddr) + } +} + +// AddAddress adds a new address to the address manager. It enforces a max +// number of addresses and silently ignores duplicate addresses. It is +// safe for concurrent access. +func (a *AddrManager) AddAddress(addr, srcAddr *wire.NetAddress) { + a.mtx.Lock() + defer a.mtx.Unlock() + + a.updateAddress(addr, srcAddr) +} + +// AddAddressByIP adds an address where we are given an ip:port and not a +// wire.NetAddress. +func (a *AddrManager) AddAddressByIP(addrIP string) error { + // Split IP and port + addr, portStr, err := net.SplitHostPort(addrIP) + if err != nil { + return err + } + // Put it in wire.Netaddress + var na wire.NetAddress + na.Timestamp = time.Now() + na.IP = net.ParseIP(addr) + if na.IP == nil { + return fmt.Errorf("invalid ip address %s", addr) + } + port, err := strconv.ParseUint(portStr, 10, 0) + if err != nil { + return fmt.Errorf("invalid port %s: %v", portStr, err) + } + na.Port = uint16(port) + a.AddAddress(&na, &na) // XXX use correct src address + return nil +} + +// NumAddresses returns the number of addresses known to the address manager. +func (a *AddrManager) numAddresses() int { + return a.nTried + a.nNew +} + +// NumAddresses returns the number of addresses known to the address manager. +func (a *AddrManager) NumAddresses() int { + a.mtx.Lock() + defer a.mtx.Unlock() + + return a.numAddresses() +} + +// NeedMoreAddresses returns whether or not the address manager needs more +// addresses. +func (a *AddrManager) NeedMoreAddresses() bool { + a.mtx.Lock() + defer a.mtx.Unlock() + + return a.numAddresses() < needAddressThreshold +} + +// AddressCache returns the current address cache. It must be treated as +// read-only (but since it is a copy now, this is not as dangerous). +func (a *AddrManager) AddressCache() []*wire.NetAddress { + a.mtx.Lock() + defer a.mtx.Unlock() + if a.nNew+a.nTried == 0 { + return nil + } + + allAddr := make([]*wire.NetAddress, a.nNew+a.nTried) + i := 0 + // Iteration order is undefined here, but we randomise it anyway. + for _, v := range a.addrIndex { + allAddr[i] = v.na + i++ + } + + numAddresses := len(allAddr) * getAddrPercent / 100 + if numAddresses > getAddrMax { + numAddresses = getAddrMax + } + + // Fisher-Yates shuffle the array. We only need to do the first + // `numAddresses' since we are throwing the rest. + for i := 0; i < numAddresses; i++ { + // pick a number between current index and the end + j := rand.Intn(len(allAddr)-i) + i + allAddr[i], allAddr[j] = allAddr[j], allAddr[i] + } + + // slice off the limit we are willing to share. + return allAddr[:numAddresses] +} + +// reset resets the address manager by reinitialising the random source +// and allocating fresh empty bucket storage. +func (a *AddrManager) reset() { + + a.addrIndex = make(map[string]*KnownAddress) + + // fill key with bytes from a good random source. + io.ReadFull(crand.Reader, a.key[:]) + for i := range a.addrNew { + a.addrNew[i] = make(map[string]*KnownAddress) + } + for i := range a.addrTried { + a.addrTried[i] = list.New() + } +} + +// HostToNetAddress returns a netaddress given a host address. If the address is +// a tor .onion address this will be taken care of. else if the host is not an +// IP address it will be resolved (via tor if required). +func (a *AddrManager) HostToNetAddress(host string, port uint16, services wire.ServiceFlag) (*wire.NetAddress, error) { + // tor address is 16 char base32 + ".onion" + var ip net.IP + if len(host) == 22 && host[16:] == ".onion" { + // go base32 encoding uses capitals (as does the rfc + // but tor and bitcoind tend to user lowercase, so we switch + // case here. + data, err := base32.StdEncoding.DecodeString( + strings.ToUpper(host[:16])) + if err != nil { + return nil, err + } + prefix := []byte{0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43} + ip = net.IP(append(prefix, data...)) + } else if ip = net.ParseIP(host); ip == nil { + ips, err := a.lookupFunc(host) + if err != nil { + return nil, err + } + if len(ips) == 0 { + return nil, fmt.Errorf("no addresses found for %s", host) + } + ip = ips[0] + } + + return wire.NewNetAddressIPPort(ip, port, services), nil +} + +// ipString returns a string for the ip from the provided NetAddress. If the +// ip is in the range used for tor addresses then it will be transformed into +// the relevant .onion address. +func ipString(na *wire.NetAddress) string { + if IsOnionCatTor(na) { + // We know now that na.IP is long enogh. + base32 := base32.StdEncoding.EncodeToString(na.IP[6:]) + return strings.ToLower(base32) + ".onion" + } + + return na.IP.String() +} + +// NetAddressKey returns a string key in the form of ip:port for IPv4 addresses +// or [ip]:port for IPv6 addresses. +func NetAddressKey(na *wire.NetAddress) string { + port := strconv.FormatUint(uint64(na.Port), 10) + + return net.JoinHostPort(ipString(na), port) +} + +// GetAddress returns a single address that should be routable. It picks a +// random one from the possible addresses with preference given to ones that +// have not been used recently and should not pick 'close' addresses +// consecutively. +func (a *AddrManager) GetAddress(class string) *KnownAddress { + // Protect concurrent access. + a.mtx.Lock() + defer a.mtx.Unlock() + + if a.numAddresses() == 0 { + return nil + } + + // Use a 50% chance for choosing between tried and new table entries. + if a.nTried > 0 && (a.nNew == 0 || a.rand.Intn(2) == 0) { + // Tried entry. + large := 1 << 30 + factor := 1.0 + for { + // pick a random bucket. + bucket := a.rand.Intn(len(a.addrTried)) + if a.addrTried[bucket].Len() == 0 { + continue + } + + // Pick a random entry in the list + e := a.addrTried[bucket].Front() + for i := + a.rand.Int63n(int64(a.addrTried[bucket].Len())); i > 0; i-- { + e = e.Next() + } + ka := e.Value.(*KnownAddress) + randval := a.rand.Intn(large) + if float64(randval) < (factor * ka.chance() * float64(large)) { + log.Tracef("Selected %v from tried bucket", + NetAddressKey(ka.na)) + return ka + } + factor *= 1.2 + } + } else { + // new node. + // XXX use a closure/function to avoid repeating this. + large := 1 << 30 + factor := 1.0 + for { + // Pick a random bucket. + bucket := a.rand.Intn(len(a.addrNew)) + if len(a.addrNew[bucket]) == 0 { + continue + } + // Then, a random entry in it. + var ka *KnownAddress + nth := a.rand.Intn(len(a.addrNew[bucket])) + for _, value := range a.addrNew[bucket] { + if nth == 0 { + ka = value + } + nth-- + } + randval := a.rand.Intn(large) + if float64(randval) < (factor * ka.chance() * float64(large)) { + log.Tracef("Selected %v from new bucket", + NetAddressKey(ka.na)) + return ka + } + factor *= 1.2 + } + } +} + +func (a *AddrManager) find(addr *wire.NetAddress) *KnownAddress { + return a.addrIndex[NetAddressKey(addr)] +} + +// Attempt increases the given address' attempt counter and updates +// the last attempt time. +func (a *AddrManager) Attempt(addr *wire.NetAddress) { + a.mtx.Lock() + defer a.mtx.Unlock() + + // find address. + // Surely address will be in tried by now? + ka := a.find(addr) + if ka == nil { + return + } + // set last tried time to now + ka.attempts++ + ka.lastattempt = time.Now() +} + +// Connected Marks the given address as currently connected and working at the +// current time. The address must already be known to AddrManager else it will +// be ignored. +func (a *AddrManager) Connected(addr *wire.NetAddress) { + a.mtx.Lock() + defer a.mtx.Unlock() + + ka := a.find(addr) + if ka == nil { + return + } + + // Update the time as long as it has been 20 minutes since last we did + // so. + now := time.Now() + if now.After(ka.na.Timestamp.Add(time.Minute * 20)) { + // ka.na is immutable, so replace it. + naCopy := *ka.na + naCopy.Timestamp = time.Now() + ka.na = &naCopy + } +} + +// Good marks the given address as good. To be called after a successful +// connection and version exchange. If the address is unknown to the address +// manager it will be ignored. +func (a *AddrManager) Good(addr *wire.NetAddress) { + a.mtx.Lock() + defer a.mtx.Unlock() + + ka := a.find(addr) + if ka == nil { + return + } + + // ka.Timestamp is not updated here to avoid leaking information + // about currently connected peers. + now := time.Now() + ka.lastsuccess = now + ka.lastattempt = now + ka.attempts = 0 + + // move to tried set, optionally evicting other addresses if neeed. + if ka.tried { + return + } + + // ok, need to move it to tried. + + // remove from all new buckets. + // record one of the buckets in question and call it the `first' + addrKey := NetAddressKey(addr) + oldBucket := -1 + for i := range a.addrNew { + // we check for existence so we can record the first one + if _, ok := a.addrNew[i][addrKey]; ok { + delete(a.addrNew[i], addrKey) + ka.refs-- + if oldBucket == -1 { + oldBucket = i + } + } + } + a.nNew-- + + if oldBucket == -1 { + // What? wasn't in a bucket after all.... Panic? + return + } + + bucket := a.getTriedBucket(ka.na) + + // Room in this tried bucket? + if a.addrTried[bucket].Len() < triedBucketSize { + ka.tried = true + a.addrTried[bucket].PushBack(ka) + a.nTried++ + return + } + + // No room, we have to evict something else. + entry := a.pickTried(bucket) + rmka := entry.Value.(*KnownAddress) + + // First bucket it would have been put in. + newBucket := a.getNewBucket(rmka.na, rmka.srcAddr) + + // If no room in the original bucket, we put it in a bucket we just + // freed up a space in. + if len(a.addrNew[newBucket]) >= newBucketSize { + newBucket = oldBucket + } + + // replace with ka in list. + ka.tried = true + entry.Value = ka + + rmka.tried = false + rmka.refs++ + + // We don't touch a.nTried here since the number of tried stays the same + // but we decemented new above, raise it again since we're putting + // something back. + a.nNew++ + + rmkey := NetAddressKey(rmka.na) + log.Tracef("Replacing %s with %s in tried", rmkey, addrKey) + + // We made sure there is space here just above. + a.addrNew[newBucket][rmkey] = rmka +} + +// AddLocalAddress adds na to the list of known local addresses to advertise +// with the given priority. +func (a *AddrManager) AddLocalAddress(na *wire.NetAddress, priority AddressPriority) error { + if !IsRoutable(na) { + return fmt.Errorf("address %s is not routable", na.IP) + } + + a.lamtx.Lock() + defer a.lamtx.Unlock() + + key := NetAddressKey(na) + la, ok := a.localAddresses[key] + if !ok || la.score < priority { + if ok { + la.score = priority + 1 + } else { + a.localAddresses[key] = &localAddress{ + na: na, + score: priority, + } + } + } + return nil +} + +// getReachabilityFrom returns the relative reachability of the provided local +// address to the provided remote address. +func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int { + const ( + Unreachable = 0 + Default = iota + Teredo + Ipv6Weak + Ipv4 + Ipv6Strong + Private + ) + + if !IsRoutable(remoteAddr) { + return Unreachable + } + + if IsOnionCatTor(remoteAddr) { + if IsOnionCatTor(localAddr) { + return Private + } + + if IsRoutable(localAddr) && IsIPv4(localAddr) { + return Ipv4 + } + + return Default + } + + if IsRFC4380(remoteAddr) { + if !IsRoutable(localAddr) { + return Default + } + + if IsRFC4380(localAddr) { + return Teredo + } + + if IsIPv4(localAddr) { + return Ipv4 + } + + return Ipv6Weak + } + + if IsIPv4(remoteAddr) { + if IsRoutable(localAddr) && IsIPv4(localAddr) { + return Ipv4 + } + return Unreachable + } + + /* ipv6 */ + var tunnelled bool + // Is our v6 is tunnelled? + if IsRFC3964(localAddr) || IsRFC6052(localAddr) || IsRFC6145(localAddr) { + tunnelled = true + } + + if !IsRoutable(localAddr) { + return Default + } + + if IsRFC4380(localAddr) { + return Teredo + } + + if IsIPv4(localAddr) { + return Ipv4 + } + + if tunnelled { + // only prioritise ipv6 if we aren't tunnelling it. + return Ipv6Weak + } + + return Ipv6Strong +} + +// GetBestLocalAddress returns the most appropriate local address to use +// for the given remote address. +func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.NetAddress { + a.lamtx.Lock() + defer a.lamtx.Unlock() + + bestreach := 0 + var bestscore AddressPriority + var bestAddress *wire.NetAddress + for _, la := range a.localAddresses { + reach := getReachabilityFrom(la.na, remoteAddr) + if reach > bestreach || + (reach == bestreach && la.score > bestscore) { + bestreach = reach + bestscore = la.score + bestAddress = la.na + } + } + if bestAddress != nil { + log.Debugf("Suggesting address %s:%d for %s:%d", bestAddress.IP, + bestAddress.Port, remoteAddr.IP, remoteAddr.Port) + } else { + log.Debugf("No worthy address for %s:%d", remoteAddr.IP, + remoteAddr.Port) + + // Send something unroutable if nothing suitable. + bestAddress = &wire.NetAddress{ + Timestamp: time.Now(), + Services: wire.SFNodeNetwork, + Port: 0, + } + if !IsIPv4(remoteAddr) && !IsOnionCatTor(remoteAddr) { + bestAddress.IP = net.IPv6zero + } else { + bestAddress.IP = net.IPv4zero + } + } + + return bestAddress +} + +// New returns a new bitcoin address manager. +// Use Start to begin processing asynchronous address updates. +func New(dataDir string, lookupFunc func(string) ([]net.IP, error)) *AddrManager { + am := AddrManager{ + peersFile: filepath.Join(dataDir, "peers.json"), + lookupFunc: lookupFunc, + rand: rand.New(rand.NewSource(time.Now().UnixNano())), + quit: make(chan struct{}), + localAddresses: make(map[string]*localAddress), + } + am.reset() + return &am +} diff --git a/vendor/github.com/btcsuite/btcd/addrmgr/addrmanager_test.go b/vendor/github.com/btcsuite/btcd/addrmgr/addrmanager_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3442d36027c592965871cb79a429160385279d5a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/addrmgr/addrmanager_test.go @@ -0,0 +1,488 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package addrmgr_test + +import ( + "errors" + "fmt" + "net" + "reflect" + "testing" + "time" + + "github.com/btcsuite/btcd/addrmgr" + "github.com/btcsuite/btcd/wire" +) + +// naTest is used to describe a test to be performed against the NetAddressKey +// method. +type naTest struct { + in wire.NetAddress + want string +} + +// naTests houses all of the tests to be performed against the NetAddressKey +// method. +var naTests = make([]naTest, 0) + +// Put some IP in here for convenience. Points to google. +var someIP = "173.194.115.66" + +// addNaTests +func addNaTests() { + // IPv4 + // Localhost + addNaTest("127.0.0.1", 8333, "127.0.0.1:8333") + addNaTest("127.0.0.1", 8334, "127.0.0.1:8334") + + // Class A + addNaTest("1.0.0.1", 8333, "1.0.0.1:8333") + addNaTest("2.2.2.2", 8334, "2.2.2.2:8334") + addNaTest("27.253.252.251", 8335, "27.253.252.251:8335") + addNaTest("123.3.2.1", 8336, "123.3.2.1:8336") + + // Private Class A + addNaTest("10.0.0.1", 8333, "10.0.0.1:8333") + addNaTest("10.1.1.1", 8334, "10.1.1.1:8334") + addNaTest("10.2.2.2", 8335, "10.2.2.2:8335") + addNaTest("10.10.10.10", 8336, "10.10.10.10:8336") + + // Class B + addNaTest("128.0.0.1", 8333, "128.0.0.1:8333") + addNaTest("129.1.1.1", 8334, "129.1.1.1:8334") + addNaTest("180.2.2.2", 8335, "180.2.2.2:8335") + addNaTest("191.10.10.10", 8336, "191.10.10.10:8336") + + // Private Class B + addNaTest("172.16.0.1", 8333, "172.16.0.1:8333") + addNaTest("172.16.1.1", 8334, "172.16.1.1:8334") + addNaTest("172.16.2.2", 8335, "172.16.2.2:8335") + addNaTest("172.16.172.172", 8336, "172.16.172.172:8336") + + // Class C + addNaTest("193.0.0.1", 8333, "193.0.0.1:8333") + addNaTest("200.1.1.1", 8334, "200.1.1.1:8334") + addNaTest("205.2.2.2", 8335, "205.2.2.2:8335") + addNaTest("223.10.10.10", 8336, "223.10.10.10:8336") + + // Private Class C + addNaTest("192.168.0.1", 8333, "192.168.0.1:8333") + addNaTest("192.168.1.1", 8334, "192.168.1.1:8334") + addNaTest("192.168.2.2", 8335, "192.168.2.2:8335") + addNaTest("192.168.192.192", 8336, "192.168.192.192:8336") + + // IPv6 + // Localhost + addNaTest("::1", 8333, "[::1]:8333") + addNaTest("fe80::1", 8334, "[fe80::1]:8334") + + // Link-local + addNaTest("fe80::1:1", 8333, "[fe80::1:1]:8333") + addNaTest("fe91::2:2", 8334, "[fe91::2:2]:8334") + addNaTest("fea2::3:3", 8335, "[fea2::3:3]:8335") + addNaTest("feb3::4:4", 8336, "[feb3::4:4]:8336") + + // Site-local + addNaTest("fec0::1:1", 8333, "[fec0::1:1]:8333") + addNaTest("fed1::2:2", 8334, "[fed1::2:2]:8334") + addNaTest("fee2::3:3", 8335, "[fee2::3:3]:8335") + addNaTest("fef3::4:4", 8336, "[fef3::4:4]:8336") +} + +func addNaTest(ip string, port uint16, want string) { + nip := net.ParseIP(ip) + na := wire.NetAddress{ + Timestamp: time.Now(), + Services: wire.SFNodeNetwork, + IP: nip, + Port: port, + } + test := naTest{na, want} + naTests = append(naTests, test) +} + +func lookupFunc(host string) ([]net.IP, error) { + return nil, errors.New("not implemented") +} + +func TestStartStop(t *testing.T) { + n := addrmgr.New("teststartstop", lookupFunc) + n.Start() + err := n.Stop() + if err != nil { + t.Fatalf("Address Manager failed to stop: %v", err) + } +} + +func TestAddAddressByIP(t *testing.T) { + fmtErr := fmt.Errorf("") + addrErr := &net.AddrError{} + var tests = []struct { + addrIP string + err error + }{ + { + someIP + ":8333", + nil, + }, + { + someIP, + addrErr, + }, + { + someIP[:12] + ":8333", + fmtErr, + }, + { + someIP + ":abcd", + fmtErr, + }, + } + + amgr := addrmgr.New("testaddressbyip", nil) + for i, test := range tests { + err := amgr.AddAddressByIP(test.addrIP) + if test.err != nil && err == nil { + t.Errorf("TestGood test %d failed expected an error and got none", i) + continue + } + if test.err == nil && err != nil { + t.Errorf("TestGood test %d failed expected no error and got one", i) + continue + } + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("TestGood test %d failed got %v, want %v", i, + reflect.TypeOf(err), reflect.TypeOf(test.err)) + continue + } + } +} + +func TestAddLocalAddress(t *testing.T) { + var tests = []struct { + address wire.NetAddress + priority addrmgr.AddressPriority + valid bool + }{ + { + wire.NetAddress{IP: net.ParseIP("192.168.0.100")}, + addrmgr.InterfacePrio, + false, + }, + { + wire.NetAddress{IP: net.ParseIP("204.124.1.1")}, + addrmgr.InterfacePrio, + true, + }, + { + wire.NetAddress{IP: net.ParseIP("204.124.1.1")}, + addrmgr.BoundPrio, + true, + }, + { + wire.NetAddress{IP: net.ParseIP("::1")}, + addrmgr.InterfacePrio, + false, + }, + { + wire.NetAddress{IP: net.ParseIP("fe80::1")}, + addrmgr.InterfacePrio, + false, + }, + { + wire.NetAddress{IP: net.ParseIP("2620:100::1")}, + addrmgr.InterfacePrio, + true, + }, + } + amgr := addrmgr.New("testaddlocaladdress", nil) + for x, test := range tests { + result := amgr.AddLocalAddress(&test.address, test.priority) + if result == nil && !test.valid { + t.Errorf("TestAddLocalAddress test #%d failed: %s should have "+ + "been accepted", x, test.address.IP) + continue + } + if result != nil && test.valid { + t.Errorf("TestAddLocalAddress test #%d failed: %s should not have "+ + "been accepted", x, test.address.IP) + continue + } + } +} + +func TestAttempt(t *testing.T) { + n := addrmgr.New("testattempt", lookupFunc) + + // Add a new address and get it + err := n.AddAddressByIP(someIP + ":8333") + if err != nil { + t.Fatalf("Adding address failed: %v", err) + } + ka := n.GetAddress("any") + + if !ka.LastAttempt().IsZero() { + t.Errorf("Address should not have attempts, but does") + } + + na := ka.NetAddress() + n.Attempt(na) + + if ka.LastAttempt().IsZero() { + t.Errorf("Address should have an attempt, but does not") + } +} + +func TestConnected(t *testing.T) { + n := addrmgr.New("testconnected", lookupFunc) + + // Add a new address and get it + err := n.AddAddressByIP(someIP + ":8333") + if err != nil { + t.Fatalf("Adding address failed: %v", err) + } + ka := n.GetAddress("any") + na := ka.NetAddress() + na.Timestamp = time.Now().Add(time.Hour * -1) // make it an hour ago + + n.Connected(na) + + if !ka.NetAddress().Timestamp.After(na.Timestamp) { + t.Errorf("Address should have a new timestamp, but does not") + } +} + +func TestNeedMoreAddresses(t *testing.T) { + n := addrmgr.New("testneedmoreaddresses", lookupFunc) + addrsToAdd := 1500 + b := n.NeedMoreAddresses() + if b == false { + t.Errorf("Expected that we need more addresses") + } + addrs := make([]*wire.NetAddress, addrsToAdd) + now := time.Now() + + var err error + for i := 0; i < addrsToAdd; i++ { + s := fmt.Sprintf("%d.%d.173.147:8333", i/128+60, i%128+60) + addrs[i], err = n.DeserializeNetAddress(s) + if err != nil { + t.Errorf("Failed to turn %s into an address: %v", s, err) + } + } + + srcAddr := &wire.NetAddress{ + Timestamp: now, + Services: 0, + IP: net.IPv4(173, 144, 173, 111), + Port: 8333, + } + + n.AddAddresses(addrs, srcAddr) + numAddrs := n.NumAddresses() + if numAddrs > addrsToAdd { + t.Errorf("Number of addresses is too many %d vs %d", numAddrs, addrsToAdd) + } + + b = n.NeedMoreAddresses() + if b == true { + t.Errorf("Expected that we don't need more addresses") + } +} + +func TestGood(t *testing.T) { + n := addrmgr.New("testgood", lookupFunc) + addrsToAdd := 64 * 64 + addrs := make([]*wire.NetAddress, addrsToAdd) + now := time.Now() + + var err error + for i := 0; i < addrsToAdd; i++ { + s := fmt.Sprintf("%d.173.147.%d:8333", i/64+60, i%64+60) + addrs[i], err = n.DeserializeNetAddress(s) + if err != nil { + t.Errorf("Failed to turn %s into an address: %v", s, err) + } + } + + srcAddr := &wire.NetAddress{ + Timestamp: now, + Services: 0, + IP: net.IPv4(173, 144, 173, 111), + Port: 8333, + } + + n.AddAddresses(addrs, srcAddr) + for _, addr := range addrs { + n.Good(addr) + } + + numAddrs := n.NumAddresses() + if numAddrs >= addrsToAdd { + t.Errorf("Number of addresses is too many: %d vs %d", numAddrs, addrsToAdd) + } + + numCache := len(n.AddressCache()) + if numCache >= numAddrs/4 { + t.Errorf("Number of addresses in cache: got %d, want %d", numCache, numAddrs/4) + } +} + +func TestGetAddress(t *testing.T) { + n := addrmgr.New("testgetaddress", lookupFunc) + + // Get an address from an empty set (should error) + if rv := n.GetAddress("any"); rv != nil { + t.Errorf("GetAddress failed: got: %v want: %v\n", rv, nil) + } + + // Add a new address and get it + err := n.AddAddressByIP(someIP + ":8333") + if err != nil { + t.Fatalf("Adding address failed: %v", err) + } + ka := n.GetAddress("any") + if ka == nil { + t.Fatalf("Did not get an address where there is one in the pool") + } + if ka.NetAddress().IP.String() != someIP { + t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP) + } + + // Mark this as a good address and get it + n.Good(ka.NetAddress()) + ka = n.GetAddress("any") + if ka == nil { + t.Fatalf("Did not get an address where there is one in the pool") + } + if ka.NetAddress().IP.String() != someIP { + t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP) + } + + numAddrs := n.NumAddresses() + if numAddrs != 1 { + t.Errorf("Wrong number of addresses: got %d, want %d", numAddrs, 1) + } +} + +func TestGetBestLocalAddress(t *testing.T) { + localAddrs := []wire.NetAddress{ + {IP: net.ParseIP("192.168.0.100")}, + {IP: net.ParseIP("::1")}, + {IP: net.ParseIP("fe80::1")}, + {IP: net.ParseIP("2001:470::1")}, + } + + var tests = []struct { + remoteAddr wire.NetAddress + want0 wire.NetAddress + want1 wire.NetAddress + want2 wire.NetAddress + want3 wire.NetAddress + }{ + { + // Remote connection from public IPv4 + wire.NetAddress{IP: net.ParseIP("204.124.8.1")}, + wire.NetAddress{IP: net.IPv4zero}, + wire.NetAddress{IP: net.IPv4zero}, + wire.NetAddress{IP: net.ParseIP("204.124.8.100")}, + wire.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")}, + }, + { + // Remote connection from private IPv4 + wire.NetAddress{IP: net.ParseIP("172.16.0.254")}, + wire.NetAddress{IP: net.IPv4zero}, + wire.NetAddress{IP: net.IPv4zero}, + wire.NetAddress{IP: net.IPv4zero}, + wire.NetAddress{IP: net.IPv4zero}, + }, + { + // Remote connection from public IPv6 + wire.NetAddress{IP: net.ParseIP("2602:100:abcd::102")}, + wire.NetAddress{IP: net.IPv6zero}, + wire.NetAddress{IP: net.ParseIP("2001:470::1")}, + wire.NetAddress{IP: net.ParseIP("2001:470::1")}, + wire.NetAddress{IP: net.ParseIP("2001:470::1")}, + }, + /* XXX + { + // Remote connection from Tor + wire.NetAddress{IP: net.ParseIP("fd87:d87e:eb43::100")}, + wire.NetAddress{IP: net.IPv4zero}, + wire.NetAddress{IP: net.ParseIP("204.124.8.100")}, + wire.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")}, + }, + */ + } + + amgr := addrmgr.New("testgetbestlocaladdress", nil) + + // Test against default when there's no address + for x, test := range tests { + got := amgr.GetBestLocalAddress(&test.remoteAddr) + if !test.want0.IP.Equal(got.IP) { + t.Errorf("TestGetBestLocalAddress test1 #%d failed for remote address %s: want %s got %s", + x, test.remoteAddr.IP, test.want1.IP, got.IP) + continue + } + } + + for _, localAddr := range localAddrs { + amgr.AddLocalAddress(&localAddr, addrmgr.InterfacePrio) + } + + // Test against want1 + for x, test := range tests { + got := amgr.GetBestLocalAddress(&test.remoteAddr) + if !test.want1.IP.Equal(got.IP) { + t.Errorf("TestGetBestLocalAddress test1 #%d failed for remote address %s: want %s got %s", + x, test.remoteAddr.IP, test.want1.IP, got.IP) + continue + } + } + + // Add a public IP to the list of local addresses. + localAddr := wire.NetAddress{IP: net.ParseIP("204.124.8.100")} + amgr.AddLocalAddress(&localAddr, addrmgr.InterfacePrio) + + // Test against want2 + for x, test := range tests { + got := amgr.GetBestLocalAddress(&test.remoteAddr) + if !test.want2.IP.Equal(got.IP) { + t.Errorf("TestGetBestLocalAddress test2 #%d failed for remote address %s: want %s got %s", + x, test.remoteAddr.IP, test.want2.IP, got.IP) + continue + } + } + /* + // Add a tor generated IP address + localAddr = wire.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")} + amgr.AddLocalAddress(&localAddr, addrmgr.ManualPrio) + + // Test against want3 + for x, test := range tests { + got := amgr.GetBestLocalAddress(&test.remoteAddr) + if !test.want3.IP.Equal(got.IP) { + t.Errorf("TestGetBestLocalAddress test3 #%d failed for remote address %s: want %s got %s", + x, test.remoteAddr.IP, test.want3.IP, got.IP) + continue + } + } + */ +} + +func TestNetAddressKey(t *testing.T) { + addNaTests() + + t.Logf("Running %d tests", len(naTests)) + for i, test := range naTests { + key := addrmgr.NetAddressKey(&test.in) + if key != test.want { + t.Errorf("NetAddressKey #%d\n got: %s want: %s", i, key, test.want) + continue + } + } + +} diff --git a/vendor/github.com/btcsuite/btcd/addrmgr/cov_report.sh b/vendor/github.com/btcsuite/btcd/addrmgr/cov_report.sh new file mode 100644 index 0000000000000000000000000000000000000000..307f05b76ca185754fa8520291c95f9eea55756a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/addrmgr/cov_report.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# This script uses gocov to generate a test coverage report. +# The gocov tool my be obtained with the following command: +# go get github.com/axw/gocov/gocov +# +# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. + +# Check for gocov. +type gocov >/dev/null 2>&1 +if [ $? -ne 0 ]; then + echo >&2 "This script requires the gocov tool." + echo >&2 "You may obtain it with the following command:" + echo >&2 "go get github.com/axw/gocov/gocov" + exit 1 +fi +gocov test | gocov report diff --git a/vendor/github.com/btcsuite/btcd/addrmgr/doc.go b/vendor/github.com/btcsuite/btcd/addrmgr/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..0f1de09ff0e5dd1095649623b0b4980c5112b10f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/addrmgr/doc.go @@ -0,0 +1,38 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package addrmgr implements concurrency safe Bitcoin address manager. + +Address Manager Overview + +In order maintain the peer-to-peer Bitcoin network, there needs to be a source +of addresses to connect to as nodes come and go. The Bitcoin protocol provides +a the getaddr and addr messages to allow peers to communicate known addresses +with each other. However, there needs to a mechanism to store those results and +select peers from them. It is also important to note that remote peers can't +be trusted to send valid peers nor attempt to provide you with only peers they +control with malicious intent. + +With that in mind, this package provides a concurrency safe address manager for +caching and selecting peers in a non-determinstic manner. The general idea is +the caller adds addresses to the address manager and notifies it when addresses +are connected, known good, and attempted. The caller also requests addresses as +it needs them. + +The address manager internally segregates the addresses into groups and +non-deterministically selects groups in a cryptographically random manner. This +reduce the chances multiple addresses from the same nets are selected which +generally helps provide greater peer diversity, and perhaps more importantly, +drastically reduces the chances an attacker is able to coerce your peer into +only connecting to nodes they control. + +The address manager also understands routability and tor addresses and tries +hard to only return routable addresses. In addition, it uses the information +provided by the caller about connected, known good, and attempted addresses to +periodically purge peers which no longer appear to be good peers as well as +bias the selection toward known good peers. The general idea is to make a best +effort at only providing usuable addresses. +*/ +package addrmgr diff --git a/vendor/github.com/btcsuite/btcd/addrmgr/internal_test.go b/vendor/github.com/btcsuite/btcd/addrmgr/internal_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e50e923cf786c1738b215cb0679d99346029705c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/addrmgr/internal_test.go @@ -0,0 +1,25 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package addrmgr + +import ( + "time" + + "github.com/btcsuite/btcd/wire" +) + +func TstKnownAddressIsBad(ka *KnownAddress) bool { + return ka.isBad() +} + +func TstKnownAddressChance(ka *KnownAddress) float64 { + return ka.chance() +} + +func TstNewKnownAddress(na *wire.NetAddress, attempts int, + lastattempt, lastsuccess time.Time, tried bool, refs int) *KnownAddress { + return &KnownAddress{na: na, attempts: attempts, lastattempt: lastattempt, + lastsuccess: lastsuccess, tried: tried, refs: refs} +} diff --git a/vendor/github.com/btcsuite/btcd/addrmgr/knownaddress.go b/vendor/github.com/btcsuite/btcd/addrmgr/knownaddress.go new file mode 100644 index 0000000000000000000000000000000000000000..73b6f0f3c152ca1a1aaa8d607c7a2ab39267442b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/addrmgr/knownaddress.go @@ -0,0 +1,101 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package addrmgr + +import ( + "time" + + "github.com/btcsuite/btcd/wire" +) + +// KnownAddress tracks information about a known network address that is used +// to determine how viable an address is. +type KnownAddress struct { + na *wire.NetAddress + srcAddr *wire.NetAddress + attempts int + lastattempt time.Time + lastsuccess time.Time + tried bool + refs int // reference count of new buckets +} + +// NetAddress returns the underlying wire.NetAddress associated with the +// known address. +func (ka *KnownAddress) NetAddress() *wire.NetAddress { + return ka.na +} + +// LastAttempt returns the last time the known address was attempted. +func (ka *KnownAddress) LastAttempt() time.Time { + return ka.lastattempt +} + +// chance returns the selection probability for a known address. The priority +// depends upon how recently the address has been seen, how recently it was last +// attempted and how often attempts to connect to it have failed. +func (ka *KnownAddress) chance() float64 { + now := time.Now() + lastSeen := now.Sub(ka.na.Timestamp) + lastAttempt := now.Sub(ka.lastattempt) + + if lastSeen < 0 { + lastSeen = 0 + } + if lastAttempt < 0 { + lastAttempt = 0 + } + + c := 1.0 + + // Very recent attempts are less likely to be retried. + if lastAttempt < 10*time.Minute { + c *= 0.01 + } + + // Failed attempts deprioritise. + for i := ka.attempts; i > 0; i-- { + c /= 1.5 + } + + return c +} + +// isBad returns true if the address in question has not been tried in the last +// minute and meets one of the following criteria: +// 1) It claims to be from the future +// 2) It hasn't been seen in over a month +// 3) It has failed at least three times and never succeeded +// 4) It has failed ten times in the last week +// All addresses that meet these criteria are assumed to be worthless and not +// worth keeping hold of. +func (ka *KnownAddress) isBad() bool { + if ka.lastattempt.After(time.Now().Add(-1 * time.Minute)) { + return false + } + + // From the future? + if ka.na.Timestamp.After(time.Now().Add(10 * time.Minute)) { + return true + } + + // Over a month old? + if ka.na.Timestamp.Before(time.Now().Add(-1 * numMissingDays * time.Hour * 24)) { + return true + } + + // Never succeeded? + if ka.lastsuccess.IsZero() && ka.attempts >= numRetries { + return true + } + + // Hasn't succeeded in too long? + if !ka.lastsuccess.After(time.Now().Add(-1*minBadDays*time.Hour*24)) && + ka.attempts >= maxFailures { + return true + } + + return false +} diff --git a/vendor/github.com/btcsuite/btcd/addrmgr/knownaddress_test.go b/vendor/github.com/btcsuite/btcd/addrmgr/knownaddress_test.go new file mode 100644 index 0000000000000000000000000000000000000000..05609ec9f329dc0d7a2128006a2e9f933a72e525 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/addrmgr/knownaddress_test.go @@ -0,0 +1,112 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package addrmgr_test + +import ( + "math" + "testing" + "time" + + "github.com/btcsuite/btcd/addrmgr" + "github.com/btcsuite/btcd/wire" +) + +func TestChance(t *testing.T) { + var tests = []struct { + addr *addrmgr.KnownAddress + expected float64 + }{ + { + //Test normal case + addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: time.Now().Add(-35 * time.Second)}, + 0, time.Now().Add(-30*time.Minute), time.Now(), false, 0), + 1.0, + }, { + //Test case in which lastseen < 0 + addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: time.Now().Add(20 * time.Second)}, + 0, time.Now().Add(-30*time.Minute), time.Now(), false, 0), + 1.0, + }, { + //Test case in which lastattempt < 0 + addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: time.Now().Add(-35 * time.Second)}, + 0, time.Now().Add(30*time.Minute), time.Now(), false, 0), + 1.0 * .01, + }, { + //Test case in which lastattempt < ten minutes + addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: time.Now().Add(-35 * time.Second)}, + 0, time.Now().Add(-5*time.Minute), time.Now(), false, 0), + 1.0 * .01, + }, { + //Test case with several failed attempts. + addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: time.Now().Add(-35 * time.Second)}, + 2, time.Now().Add(-30*time.Minute), time.Now(), false, 0), + 1 / 1.5 / 1.5, + }, + } + + err := .0001 + for i, test := range tests { + chance := addrmgr.TstKnownAddressChance(test.addr) + if math.Abs(test.expected-chance) >= err { + t.Errorf("case %d: got %f, expected %f", i, chance, test.expected) + } + } +} + +func TestIsBad(t *testing.T) { + future := time.Now().Add(35 * time.Minute) + monthOld := time.Now().Add(-43 * time.Hour * 24) + secondsOld := time.Now().Add(-2 * time.Second) + minutesOld := time.Now().Add(-27 * time.Minute) + hoursOld := time.Now().Add(-5 * time.Hour) + zeroTime, _ := time.Parse("Jan 1, 1970 at 0:00am (GMT)", "Jan 1, 1970 at 0:00am (GMT)") + + futureNa := &wire.NetAddress{Timestamp: future} + minutesOldNa := &wire.NetAddress{Timestamp: minutesOld} + monthOldNa := &wire.NetAddress{Timestamp: monthOld} + currentNa := &wire.NetAddress{Timestamp: secondsOld} + + //Test addresses that have been tried in the last minute. + if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(futureNa, 3, secondsOld, zeroTime, false, 0)) { + t.Errorf("test case 1: addresses that have been tried in the last minute are not bad.") + } + if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(monthOldNa, 3, secondsOld, zeroTime, false, 0)) { + t.Errorf("test case 2: addresses that have been tried in the last minute are not bad.") + } + if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(currentNa, 3, secondsOld, zeroTime, false, 0)) { + t.Errorf("test case 3: addresses that have been tried in the last minute are not bad.") + } + if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(currentNa, 3, secondsOld, monthOld, true, 0)) { + t.Errorf("test case 4: addresses that have been tried in the last minute are not bad.") + } + if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(currentNa, 2, secondsOld, secondsOld, true, 0)) { + t.Errorf("test case 5: addresses that have been tried in the last minute are not bad.") + } + + //Test address that claims to be from the future. + if !addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(futureNa, 0, minutesOld, hoursOld, true, 0)) { + t.Errorf("test case 6: addresses that claim to be from the future are bad.") + } + + //Test address that has not been seen in over a month. + if !addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(monthOldNa, 0, minutesOld, hoursOld, true, 0)) { + t.Errorf("test case 7: addresses more than a month old are bad.") + } + + //It has failed at least three times and never succeeded. + if !addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(minutesOldNa, 3, minutesOld, zeroTime, true, 0)) { + t.Errorf("test case 8: addresses that have never succeeded are bad.") + } + + //It has failed ten times in the last week + if !addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(minutesOldNa, 10, minutesOld, monthOld, true, 0)) { + t.Errorf("test case 9: addresses that have not succeeded in too long are bad.") + } + + //Test an address that should work. + if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(minutesOldNa, 2, minutesOld, hoursOld, true, 0)) { + t.Errorf("test case 10: This should be a valid address.") + } +} diff --git a/vendor/github.com/btcsuite/btcd/addrmgr/log.go b/vendor/github.com/btcsuite/btcd/addrmgr/log.go new file mode 100644 index 0000000000000000000000000000000000000000..b3ebbd15d1411a253da764564dae4d9e977e05f4 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/addrmgr/log.go @@ -0,0 +1,32 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package addrmgr + +import ( + "github.com/btcsuite/btclog" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + DisableLog() +} + +// DisableLog disables all library log output. Logging output is disabled +// by default until either UseLogger or SetLogWriter are called. +func DisableLog() { + log = btclog.Disabled +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} diff --git a/vendor/github.com/btcsuite/btcd/addrmgr/network.go b/vendor/github.com/btcsuite/btcd/addrmgr/network.go new file mode 100644 index 0000000000000000000000000000000000000000..e891ad6fdaaead0647ba76eefcc02e28d10ad8bf --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/addrmgr/network.go @@ -0,0 +1,281 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package addrmgr + +import ( + "fmt" + "net" + + "github.com/btcsuite/btcd/wire" +) + +var ( + // rfc1918Nets specifies the IPv4 private address blocks as defined by + // by RFC1918 (10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16). + rfc1918Nets = []net.IPNet{ + ipNet("10.0.0.0", 8, 32), + ipNet("172.16.0.0", 12, 32), + ipNet("192.168.0.0", 16, 32), + } + + // rfc2544Net specifies the the IPv4 block as defined by RFC2544 + // (198.18.0.0/15) + rfc2544Net = ipNet("198.18.0.0", 15, 32) + + // rfc3849Net specifies the IPv6 documentation address block as defined + // by RFC3849 (2001:DB8::/32). + rfc3849Net = ipNet("2001:DB8::", 32, 128) + + // rfc3927Net specifies the IPv4 auto configuration address block as + // defined by RFC3927 (169.254.0.0/16). + rfc3927Net = ipNet("169.254.0.0", 16, 32) + + // rfc3964Net specifies the IPv6 to IPv4 encapsulation address block as + // defined by RFC3964 (2002::/16). + rfc3964Net = ipNet("2002::", 16, 128) + + // rfc4193Net specifies the IPv6 unique local address block as defined + // by RFC4193 (FC00::/7). + rfc4193Net = ipNet("FC00::", 7, 128) + + // rfc4380Net specifies the IPv6 teredo tunneling over UDP address block + // as defined by RFC4380 (2001::/32). + rfc4380Net = ipNet("2001::", 32, 128) + + // rfc4843Net specifies the IPv6 ORCHID address block as defined by + // RFC4843 (2001:10::/28). + rfc4843Net = ipNet("2001:10::", 28, 128) + + // rfc4862Net specifies the IPv6 stateless address autoconfiguration + // address block as defined by RFC4862 (FE80::/64). + rfc4862Net = ipNet("FE80::", 64, 128) + + // rfc5737Net specifies the IPv4 documentation address blocks as defined + // by RFC5737 (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24) + rfc5737Net = []net.IPNet{ + ipNet("192.0.2.0", 24, 32), + ipNet("198.51.100.0", 24, 32), + ipNet("203.0.113.0", 24, 32), + } + + // rfc6052Net specifies the IPv6 well-known prefix address block as + // defined by RFC6052 (64:FF9B::/96). + rfc6052Net = ipNet("64:FF9B::", 96, 128) + + // rfc6145Net specifies the IPv6 to IPv4 translated address range as + // defined by RFC6145 (::FFFF:0:0:0/96). + rfc6145Net = ipNet("::FFFF:0:0:0", 96, 128) + + // rfc6598Net specifies the IPv4 block as defined by RFC6598 (100.64.0.0/10) + rfc6598Net = ipNet("100.64.0.0", 10, 32) + + // onionCatNet defines the IPv6 address block used to support Tor. + // bitcoind encodes a .onion address as a 16 byte number by decoding the + // address prior to the .onion (i.e. the key hash) base32 into a ten + // byte number. It then stores the first 6 bytes of the address as + // 0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43. + // + // This is the same range used by OnionCat, which is part part of the + // RFC4193 unique local IPv6 range. + // + // In summary the format is: + // { magic 6 bytes, 10 bytes base32 decode of key hash } + onionCatNet = ipNet("fd87:d87e:eb43::", 48, 128) + + // zero4Net defines the IPv4 address block for address staring with 0 + // (0.0.0.0/8). + zero4Net = ipNet("0.0.0.0", 8, 32) + + // heNet defines the Hurricane Electric IPv6 address block. + heNet = ipNet("2001:470::", 32, 128) +) + +// ipNet returns a net.IPNet struct given the passed IP address string, number +// of one bits to include at the start of the mask, and the total number of bits +// for the mask. +func ipNet(ip string, ones, bits int) net.IPNet { + return net.IPNet{IP: net.ParseIP(ip), Mask: net.CIDRMask(ones, bits)} +} + +// IsIPv4 returns whether or not the given address is an IPv4 address. +func IsIPv4(na *wire.NetAddress) bool { + return na.IP.To4() != nil +} + +// IsLocal returns whether or not the given address is a local address. +func IsLocal(na *wire.NetAddress) bool { + return na.IP.IsLoopback() || zero4Net.Contains(na.IP) +} + +// IsOnionCatTor returns whether or not the passed address is in the IPv6 range +// used by bitcoin to support Tor (fd87:d87e:eb43::/48). Note that this range +// is the same range used by OnionCat, which is part of the RFC4193 unique local +// IPv6 range. +func IsOnionCatTor(na *wire.NetAddress) bool { + return onionCatNet.Contains(na.IP) +} + +// IsRFC1918 returns whether or not the passed address is part of the IPv4 +// private network address space as defined by RFC1918 (10.0.0.0/8, +// 172.16.0.0/12, or 192.168.0.0/16). +func IsRFC1918(na *wire.NetAddress) bool { + for _, rfc := range rfc1918Nets { + if rfc.Contains(na.IP) { + return true + } + } + return false +} + +// IsRFC2544 returns whether or not the passed address is part of the IPv4 +// address space as defined by RFC2544 (198.18.0.0/15) +func IsRFC2544(na *wire.NetAddress) bool { + return rfc2544Net.Contains(na.IP) +} + +// IsRFC3849 returns whether or not the passed address is part of the IPv6 +// documentation range as defined by RFC3849 (2001:DB8::/32). +func IsRFC3849(na *wire.NetAddress) bool { + return rfc3849Net.Contains(na.IP) +} + +// IsRFC3927 returns whether or not the passed address is part of the IPv4 +// autoconfiguration range as defined by RFC3927 (169.254.0.0/16). +func IsRFC3927(na *wire.NetAddress) bool { + return rfc3927Net.Contains(na.IP) +} + +// IsRFC3964 returns whether or not the passed address is part of the IPv6 to +// IPv4 encapsulation range as defined by RFC3964 (2002::/16). +func IsRFC3964(na *wire.NetAddress) bool { + return rfc3964Net.Contains(na.IP) +} + +// IsRFC4193 returns whether or not the passed address is part of the IPv6 +// unique local range as defined by RFC4193 (FC00::/7). +func IsRFC4193(na *wire.NetAddress) bool { + return rfc4193Net.Contains(na.IP) +} + +// IsRFC4380 returns whether or not the passed address is part of the IPv6 +// teredo tunneling over UDP range as defined by RFC4380 (2001::/32). +func IsRFC4380(na *wire.NetAddress) bool { + return rfc4380Net.Contains(na.IP) +} + +// IsRFC4843 returns whether or not the passed address is part of the IPv6 +// ORCHID range as defined by RFC4843 (2001:10::/28). +func IsRFC4843(na *wire.NetAddress) bool { + return rfc4843Net.Contains(na.IP) +} + +// IsRFC4862 returns whether or not the passed address is part of the IPv6 +// stateless address autoconfiguration range as defined by RFC4862 (FE80::/64). +func IsRFC4862(na *wire.NetAddress) bool { + return rfc4862Net.Contains(na.IP) +} + +// IsRFC5737 returns whether or not the passed address is part of the IPv4 +// documentation address space as defined by RFC5737 (192.0.2.0/24, +// 198.51.100.0/24, 203.0.113.0/24) +func IsRFC5737(na *wire.NetAddress) bool { + for _, rfc := range rfc5737Net { + if rfc.Contains(na.IP) { + return true + } + } + + return false +} + +// IsRFC6052 returns whether or not the passed address is part of the IPv6 +// well-known prefix range as defined by RFC6052 (64:FF9B::/96). +func IsRFC6052(na *wire.NetAddress) bool { + return rfc6052Net.Contains(na.IP) +} + +// IsRFC6145 returns whether or not the passed address is part of the IPv6 to +// IPv4 translated address range as defined by RFC6145 (::FFFF:0:0:0/96). +func IsRFC6145(na *wire.NetAddress) bool { + return rfc6145Net.Contains(na.IP) +} + +// IsRFC6598 returns whether or not the passed address is part of the IPv4 +// shared address space specified by RFC6598 (100.64.0.0/10) +func IsRFC6598(na *wire.NetAddress) bool { + return rfc6598Net.Contains(na.IP) +} + +// IsValid returns whether or not the passed address is valid. The address is +// considered invalid under the following circumstances: +// IPv4: It is either a zero or all bits set address. +// IPv6: It is either a zero or RFC3849 documentation address. +func IsValid(na *wire.NetAddress) bool { + // IsUnspecified returns if address is 0, so only all bits set, and + // RFC3849 need to be explicitly checked. + return na.IP != nil && !(na.IP.IsUnspecified() || + na.IP.Equal(net.IPv4bcast)) +} + +// IsRoutable returns whether or not the passed address is routable over +// the public internet. This is true as long as the address is valid and is not +// in any reserved ranges. +func IsRoutable(na *wire.NetAddress) bool { + return IsValid(na) && !(IsRFC1918(na) || IsRFC2544(na) || + IsRFC3927(na) || IsRFC4862(na) || IsRFC3849(na) || + IsRFC4843(na) || IsRFC5737(na) || IsRFC6598(na) || + IsLocal(na) || (IsRFC4193(na) && !IsOnionCatTor(na))) +} + +// GroupKey returns a string representing the network group an address is part +// of. This is the /16 for IPv4, the /32 (/36 for he.net) for IPv6, the string +// "local" for a local address, the string "tor:key" where key is the /4 of the +// onion address for tor address, and the string "unroutable" for an unroutable +// address. +func GroupKey(na *wire.NetAddress) string { + if IsLocal(na) { + return "local" + } + if !IsRoutable(na) { + return "unroutable" + } + if IsIPv4(na) { + return na.IP.Mask(net.CIDRMask(16, 32)).String() + } + if IsRFC6145(na) || IsRFC6052(na) { + // last four bytes are the ip address + ip := net.IP(na.IP[12:16]) + return ip.Mask(net.CIDRMask(16, 32)).String() + } + + if IsRFC3964(na) { + ip := net.IP(na.IP[2:6]) + return ip.Mask(net.CIDRMask(16, 32)).String() + + } + if IsRFC4380(na) { + // teredo tunnels have the last 4 bytes as the v4 address XOR + // 0xff. + ip := net.IP(make([]byte, 4)) + for i, byte := range na.IP[12:16] { + ip[i] = byte ^ 0xff + } + return ip.Mask(net.CIDRMask(16, 32)).String() + } + if IsOnionCatTor(na) { + // group is keyed off the first 4 bits of the actual onion key. + return fmt.Sprintf("tor:%d", na.IP[6]&((1<<4)-1)) + } + + // OK, so now we know ourselves to be a IPv6 address. + // bitcoind uses /32 for everything, except for Hurricane Electric's + // (he.net) IP range, which it uses /36 for. + bits := 32 + if heNet.Contains(na.IP) { + bits = 36 + } + + return na.IP.Mask(net.CIDRMask(bits, 128)).String() +} diff --git a/vendor/github.com/btcsuite/btcd/addrmgr/network_test.go b/vendor/github.com/btcsuite/btcd/addrmgr/network_test.go new file mode 100644 index 0000000000000000000000000000000000000000..98dc054e402ae1aaa33ebfeeb62c830121bb688c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/addrmgr/network_test.go @@ -0,0 +1,213 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package addrmgr_test + +import ( + "net" + "testing" + "time" + + "github.com/btcsuite/btcd/addrmgr" + "github.com/btcsuite/btcd/wire" +) + +// TestIPTypes ensures the various functions which determine the type of an IP +// address based on RFCs work as intended. +func TestIPTypes(t *testing.T) { + type ipTest struct { + in wire.NetAddress + rfc1918 bool + rfc2544 bool + rfc3849 bool + rfc3927 bool + rfc3964 bool + rfc4193 bool + rfc4380 bool + rfc4843 bool + rfc4862 bool + rfc5737 bool + rfc6052 bool + rfc6145 bool + rfc6598 bool + local bool + valid bool + routable bool + } + + newIPTest := func(ip string, rfc1918, rfc2544, rfc3849, rfc3927, rfc3964, + rfc4193, rfc4380, rfc4843, rfc4862, rfc5737, rfc6052, rfc6145, rfc6598, + local, valid, routable bool) ipTest { + nip := net.ParseIP(ip) + na := wire.NetAddress{ + Timestamp: time.Now(), + Services: wire.SFNodeNetwork, + IP: nip, + Port: 8333, + } + test := ipTest{na, rfc1918, rfc2544, rfc3849, rfc3927, rfc3964, rfc4193, rfc4380, + rfc4843, rfc4862, rfc5737, rfc6052, rfc6145, rfc6598, local, valid, routable} + return test + } + + tests := []ipTest{ + newIPTest("10.255.255.255", true, false, false, false, false, false, + false, false, false, false, false, false, false, false, true, false), + newIPTest("192.168.0.1", true, false, false, false, false, false, + false, false, false, false, false, false, false, false, true, false), + newIPTest("172.31.255.1", true, false, false, false, false, false, + false, false, false, false, false, false, false, false, true, false), + newIPTest("172.32.1.1", false, false, false, false, false, false, false, false, + false, false, false, false, false, false, true, true), + newIPTest("169.254.250.120", false, false, false, true, false, false, + false, false, false, false, false, false, false, false, true, false), + newIPTest("0.0.0.0", false, false, false, false, false, false, false, + false, false, false, false, false, false, true, false, false), + newIPTest("255.255.255.255", false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false), + newIPTest("127.0.0.1", false, false, false, false, false, false, + false, false, false, false, false, false, false, true, true, false), + newIPTest("fd00:dead::1", false, false, false, false, false, true, + false, false, false, false, false, false, false, false, true, false), + newIPTest("2001::1", false, false, false, false, false, false, + true, false, false, false, false, false, false, false, true, true), + newIPTest("2001:10:abcd::1:1", false, false, false, false, false, false, + false, true, false, false, false, false, false, false, true, false), + newIPTest("fe80::1", false, false, false, false, false, false, + false, false, true, false, false, false, false, false, true, false), + newIPTest("fe80:1::1", false, false, false, false, false, false, + false, false, false, false, false, false, false, false, true, true), + newIPTest("64:ff9b::1", false, false, false, false, false, false, + false, false, false, false, true, false, false, false, true, true), + newIPTest("::ffff:abcd:ef12:1", false, false, false, false, false, false, + false, false, false, false, false, false, false, false, true, true), + newIPTest("::1", false, false, false, false, false, false, false, false, + false, false, false, false, false, true, true, false), + newIPTest("198.18.0.1", false, true, false, false, false, false, false, + false, false, false, false, false, false, false, true, false), + newIPTest("100.127.255.1", false, false, false, false, false, false, false, + false, false, false, false, false, true, false, true, false), + newIPTest("203.0.113.1", false, false, false, false, false, false, false, + false, false, false, false, false, false, false, true, false), + } + + t.Logf("Running %d tests", len(tests)) + for _, test := range tests { + if rv := addrmgr.IsRFC1918(&test.in); rv != test.rfc1918 { + t.Errorf("IsRFC1918 %s\n got: %v want: %v", test.in.IP, rv, test.rfc1918) + } + + if rv := addrmgr.IsRFC3849(&test.in); rv != test.rfc3849 { + t.Errorf("IsRFC3849 %s\n got: %v want: %v", test.in.IP, rv, test.rfc3849) + } + + if rv := addrmgr.IsRFC3927(&test.in); rv != test.rfc3927 { + t.Errorf("IsRFC3927 %s\n got: %v want: %v", test.in.IP, rv, test.rfc3927) + } + + if rv := addrmgr.IsRFC3964(&test.in); rv != test.rfc3964 { + t.Errorf("IsRFC3964 %s\n got: %v want: %v", test.in.IP, rv, test.rfc3964) + } + + if rv := addrmgr.IsRFC4193(&test.in); rv != test.rfc4193 { + t.Errorf("IsRFC4193 %s\n got: %v want: %v", test.in.IP, rv, test.rfc4193) + } + + if rv := addrmgr.IsRFC4380(&test.in); rv != test.rfc4380 { + t.Errorf("IsRFC4380 %s\n got: %v want: %v", test.in.IP, rv, test.rfc4380) + } + + if rv := addrmgr.IsRFC4843(&test.in); rv != test.rfc4843 { + t.Errorf("IsRFC4843 %s\n got: %v want: %v", test.in.IP, rv, test.rfc4843) + } + + if rv := addrmgr.IsRFC4862(&test.in); rv != test.rfc4862 { + t.Errorf("IsRFC4862 %s\n got: %v want: %v", test.in.IP, rv, test.rfc4862) + } + + if rv := addrmgr.IsRFC6052(&test.in); rv != test.rfc6052 { + t.Errorf("isRFC6052 %s\n got: %v want: %v", test.in.IP, rv, test.rfc6052) + } + + if rv := addrmgr.IsRFC6145(&test.in); rv != test.rfc6145 { + t.Errorf("IsRFC1918 %s\n got: %v want: %v", test.in.IP, rv, test.rfc6145) + } + + if rv := addrmgr.IsLocal(&test.in); rv != test.local { + t.Errorf("IsLocal %s\n got: %v want: %v", test.in.IP, rv, test.local) + } + + if rv := addrmgr.IsValid(&test.in); rv != test.valid { + t.Errorf("IsValid %s\n got: %v want: %v", test.in.IP, rv, test.valid) + } + + if rv := addrmgr.IsRoutable(&test.in); rv != test.routable { + t.Errorf("IsRoutable %s\n got: %v want: %v", test.in.IP, rv, test.routable) + } + } +} + +// TestGroupKey tests the GroupKey function to ensure it properly groups various +// IP addresses. +func TestGroupKey(t *testing.T) { + tests := []struct { + name string + ip string + expected string + }{ + // Local addresses. + {name: "ipv4 localhost", ip: "127.0.0.1", expected: "local"}, + {name: "ipv6 localhost", ip: "::1", expected: "local"}, + {name: "ipv4 zero", ip: "0.0.0.0", expected: "local"}, + {name: "ipv4 first octet zero", ip: "0.1.2.3", expected: "local"}, + + // Unroutable addresses. + {name: "ipv4 invalid bcast", ip: "255.255.255.255", expected: "unroutable"}, + {name: "ipv4 rfc1918 10/8", ip: "10.1.2.3", expected: "unroutable"}, + {name: "ipv4 rfc1918 172.16/12", ip: "172.16.1.2", expected: "unroutable"}, + {name: "ipv4 rfc1918 192.168/16", ip: "192.168.1.2", expected: "unroutable"}, + {name: "ipv6 rfc3849 2001:db8::/32", ip: "2001:db8::1234", expected: "unroutable"}, + {name: "ipv4 rfc3927 169.254/16", ip: "169.254.1.2", expected: "unroutable"}, + {name: "ipv6 rfc4193 fc00::/7", ip: "fc00::1234", expected: "unroutable"}, + {name: "ipv6 rfc4843 2001:10::/28", ip: "2001:10::1234", expected: "unroutable"}, + {name: "ipv6 rfc4862 fe80::/64", ip: "fe80::1234", expected: "unroutable"}, + + // IPv4 normal. + {name: "ipv4 normal class a", ip: "12.1.2.3", expected: "12.1.0.0"}, + {name: "ipv4 normal class b", ip: "173.1.2.3", expected: "173.1.0.0"}, + {name: "ipv4 normal class c", ip: "196.1.2.3", expected: "196.1.0.0"}, + + // IPv6/IPv4 translations. + {name: "ipv6 rfc3964 with ipv4 encap", ip: "2002:0c01:0203::", expected: "12.1.0.0"}, + {name: "ipv6 rfc4380 toredo ipv4", ip: "2001:0:1234::f3fe:fdfc", expected: "12.1.0.0"}, + {name: "ipv6 rfc6052 well-known prefix with ipv4", ip: "64:ff9b::0c01:0203", expected: "12.1.0.0"}, + {name: "ipv6 rfc6145 translated ipv4", ip: "::ffff:0:0c01:0203", expected: "12.1.0.0"}, + + // Tor. + {name: "ipv6 tor onioncat", ip: "fd87:d87e:eb43:1234::5678", expected: "tor:2"}, + {name: "ipv6 tor onioncat 2", ip: "fd87:d87e:eb43:1245::6789", expected: "tor:2"}, + {name: "ipv6 tor onioncat 3", ip: "fd87:d87e:eb43:1345::6789", expected: "tor:3"}, + + // IPv6 normal. + {name: "ipv6 normal", ip: "2602:100::1", expected: "2602:100::"}, + {name: "ipv6 normal 2", ip: "2602:0100::1234", expected: "2602:100::"}, + {name: "ipv6 hurricane electric", ip: "2001:470:1f10:a1::2", expected: "2001:470:1000::"}, + {name: "ipv6 hurricane electric 2", ip: "2001:0470:1f10:a1::2", expected: "2001:470:1000::"}, + } + + for i, test := range tests { + nip := net.ParseIP(test.ip) + na := wire.NetAddress{ + Timestamp: time.Now(), + Services: wire.SFNodeNetwork, + IP: nip, + Port: 8333, + } + if key := addrmgr.GroupKey(&na); key != test.expected { + t.Errorf("TestGroupKey #%d (%s): unexpected group key "+ + "- got '%s', want '%s'", i, test.name, + key, test.expected) + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/addrmgr/test_coverage.txt b/vendor/github.com/btcsuite/btcd/addrmgr/test_coverage.txt new file mode 100644 index 0000000000000000000000000000000000000000..c67e0f6d0e3ad803b1a6295ec106c07e1c5b0f4e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/addrmgr/test_coverage.txt @@ -0,0 +1,62 @@ + +github.com/conformal/btcd/addrmgr/network.go GroupKey 100.00% (23/23) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.reset 100.00% (6/6) +github.com/conformal/btcd/addrmgr/network.go IsRFC5737 100.00% (4/4) +github.com/conformal/btcd/addrmgr/network.go IsRFC1918 100.00% (4/4) +github.com/conformal/btcd/addrmgr/addrmanager.go New 100.00% (3/3) +github.com/conformal/btcd/addrmgr/addrmanager.go NetAddressKey 100.00% (2/2) +github.com/conformal/btcd/addrmgr/network.go IsRFC4862 100.00% (1/1) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.numAddresses 100.00% (1/1) +github.com/conformal/btcd/addrmgr/log.go init 100.00% (1/1) +github.com/conformal/btcd/addrmgr/log.go DisableLog 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go ipNet 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsIPv4 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsLocal 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsOnionCatTor 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsRFC2544 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsRFC3849 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsRFC3927 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsRFC3964 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsRFC4193 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsRFC4380 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsRFC4843 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsRFC6052 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsRFC6145 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsRFC6598 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsValid 100.00% (1/1) +github.com/conformal/btcd/addrmgr/network.go IsRoutable 100.00% (1/1) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.GetBestLocalAddress 94.74% (18/19) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.AddLocalAddress 90.91% (10/11) +github.com/conformal/btcd/addrmgr/addrmanager.go getReachabilityFrom 51.52% (17/33) +github.com/conformal/btcd/addrmgr/addrmanager.go ipString 50.00% (2/4) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.GetAddress 9.30% (4/43) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.deserializePeers 0.00% (0/50) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.Good 0.00% (0/44) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.savePeers 0.00% (0/39) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.updateAddress 0.00% (0/30) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.expireNew 0.00% (0/22) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.AddressCache 0.00% (0/16) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.HostToNetAddress 0.00% (0/15) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.getNewBucket 0.00% (0/15) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.AddAddressByIP 0.00% (0/14) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.getTriedBucket 0.00% (0/14) +github.com/conformal/btcd/addrmgr/knownaddress.go knownAddress.chance 0.00% (0/13) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.loadPeers 0.00% (0/11) +github.com/conformal/btcd/addrmgr/knownaddress.go knownAddress.isBad 0.00% (0/11) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.Connected 0.00% (0/10) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.addressHandler 0.00% (0/9) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.pickTried 0.00% (0/8) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.DeserializeNetAddress 0.00% (0/7) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.Stop 0.00% (0/7) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.Attempt 0.00% (0/7) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.Start 0.00% (0/6) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.AddAddresses 0.00% (0/4) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.NeedMoreAddresses 0.00% (0/3) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.NumAddresses 0.00% (0/3) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.AddAddress 0.00% (0/3) +github.com/conformal/btcd/addrmgr/knownaddress.go knownAddress.LastAttempt 0.00% (0/1) +github.com/conformal/btcd/addrmgr/knownaddress.go knownAddress.NetAddress 0.00% (0/1) +github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.find 0.00% (0/1) +github.com/conformal/btcd/addrmgr/log.go UseLogger 0.00% (0/1) +github.com/conformal/btcd/addrmgr --------------------------------- 21.04% (113/537) + diff --git a/vendor/github.com/btcsuite/btcd/blockchain/README.md b/vendor/github.com/btcsuite/btcd/blockchain/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ad7a69d4e42ec3cfb827078d6f141faa4e2d7252 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/README.md @@ -0,0 +1,108 @@ +blockchain +========== + +[] +(https://travis-ci.org/btcsuite/btcd) [![ISC License] +(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[] +(http://godoc.org/github.com/btcsuite/btcd/blockchain) + +Package blockchain implements bitcoin block handling and chain selection rules. +The test coverage is currently only around 60%, but will be increasing over +time. See `test_coverage.txt` for the gocov coverage report. Alternatively, if +you are running a POSIX OS, you can run the `cov_report.sh` script for a +real-time report. Package blockchain is licensed under the liberal ISC license. + +There is an associated blog post about the release of this package +[here](https://blog.conformal.com/btcchain-the-bitcoin-chain-package-from-bctd/). + +This package has intentionally been designed so it can be used as a standalone +package for any projects needing to handle processing of blocks into the bitcoin +block chain. + +## Installation and Updating + +```bash +$ go get -u github.com/btcsuite/btcd/blockchain +``` + +## Bitcoin Chain Processing Overview + +Before a block is allowed into the block chain, it must go through an intensive +series of validation rules. The following list serves as a general outline of +those rules to provide some intuition into what is going on under the hood, but +is by no means exhaustive: + + - Reject duplicate blocks + - Perform a series of sanity checks on the block and its transactions such as + verifying proof of work, timestamps, number and character of transactions, + transaction amounts, script complexity, and merkle root calculations + - Compare the block against predetermined checkpoints for expected timestamps + and difficulty based on elapsed time since the checkpoint + - Save the most recent orphan blocks for a limited time in case their parent + blocks become available + - Stop processing if the block is an orphan as the rest of the processing + depends on the block's position within the block chain + - Perform a series of more thorough checks that depend on the block's position + within the block chain such as verifying block difficulties adhere to + difficulty retarget rules, timestamps are after the median of the last + several blocks, all transactions are finalized, checkpoint blocks match, and + block versions are in line with the previous blocks + - Determine how the block fits into the chain and perform different actions + accordingly in order to ensure any side chains which have higher difficulty + than the main chain become the new main chain + - When a block is being connected to the main chain (either through + reorganization of a side chain to the main chain or just extending the + main chain), perform further checks on the block's transactions such as + verifying transaction duplicates, script complexity for the combination of + connected scripts, coinbase maturity, double spends, and connected + transaction values + - Run the transaction scripts to verify the spender is allowed to spend the + coins + - Insert the block into the block database + +## Examples + +* [ProcessBlock Example] + (http://godoc.org/github.com/btcsuite/btcd/blockchain#example-BlockChain-ProcessBlock) + Demonstrates how to create a new chain instance and use ProcessBlock to + attempt to attempt add a block to the chain. This example intentionally + attempts to insert a duplicate genesis block to illustrate how an invalid + block is handled. + +* [CompactToBig Example] + (http://godoc.org/github.com/btcsuite/btcd/blockchain#example-CompactToBig) + Demonstrates how to convert the compact "bits" in a block header which + represent the target difficulty to a big integer and display it using the + typical hex notation. + +* [BigToCompact Example] + (http://godoc.org/github.com/btcsuite/btcd/blockchain#example-BigToCompact) + Demonstrates how to convert how to convert a target difficulty into the + compact "bits" in a block header which represent that target difficulty. + +## GPG Verification Key + +All official release tags are signed by Conformal so users can ensure the code +has not been tampered with and is coming from the btcsuite developers. To +verify the signature perform the following: + +- Download the public key from the Conformal website at + https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt + +- Import the public key into your GPG keyring: + ```bash + gpg --import GIT-GPG-KEY-conformal.txt + ``` + +- Verify the release tag with the following command where `TAG_NAME` is a + placeholder for the specific tag: + ```bash + git tag -v TAG_NAME + ``` + +## License + + +Package blockchain is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/vendor/github.com/btcsuite/btcd/blockchain/accept.go b/vendor/github.com/btcsuite/btcd/blockchain/accept.go new file mode 100644 index 0000000000000000000000000000000000000000..938d40a811feb0a8a094e14f700049394d64ec8c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/accept.go @@ -0,0 +1,85 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import "github.com/btcsuite/btcutil" + +// maybeAcceptBlock potentially accepts a block into the memory block chain. +// It performs several validation checks which depend on its position within +// the block chain before adding it. The block is expected to have already gone +// through ProcessBlock before calling this function with it. +// +// The flags modify the behavior of this function as follows: +// - BFDryRun: The memory chain index will not be pruned and no accept +// notification will be sent since the block is not being accepted. +// +// The flags are also passed to checkBlockContext and connectBestChain. See +// their documentation for how the flags modify their behavior. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) error { + dryRun := flags&BFDryRun == BFDryRun + + // Get a block node for the block previous to this one. Will be nil + // if this is the genesis block. + prevNode, err := b.getPrevNodeFromBlock(block) + if err != nil { + log.Errorf("getPrevNodeFromBlock: %v", err) + return err + } + + // The height of this block is one more than the referenced previous + // block. + blockHeight := int32(0) + if prevNode != nil { + blockHeight = prevNode.height + 1 + } + block.SetHeight(blockHeight) + + // The block must pass all of the validation rules which depend on the + // position of the block within the block chain. + err = b.checkBlockContext(block, prevNode, flags) + if err != nil { + return err + } + + // Prune block nodes which are no longer needed before creating + // a new node. + if !dryRun { + err = b.pruneBlockNodes() + if err != nil { + return err + } + } + + // Create a new block node for the block and add it to the in-memory + // block chain (could be either a side chain or the main chain). + blockHeader := &block.MsgBlock().Header + newNode := newBlockNode(blockHeader, block.Sha(), blockHeight) + if prevNode != nil { + newNode.parent = prevNode + newNode.height = blockHeight + newNode.workSum.Add(prevNode.workSum, newNode.workSum) + } + + // Connect the passed block to the chain while respecting proper chain + // selection according to the chain with the most proof of work. This + // also handles validation of the transaction scripts. + err = b.connectBestChain(newNode, block, flags) + if err != nil { + return err + } + + // Notify the caller that the new block was accepted into the block + // chain. The caller would typically want to react by relaying the + // inventory to other peers. + if !dryRun { + b.chainLock.Unlock() + b.sendNotification(NTBlockAccepted, block) + b.chainLock.Lock() + } + + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/bench_test.go b/vendor/github.com/btcsuite/btcd/blockchain/bench_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a680feca196e425975b735d8a5e572504d7447d0 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/bench_test.go @@ -0,0 +1,32 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain_test + +import ( + "testing" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcutil" +) + +// BenchmarkIsCoinBase performs a simple benchmark against the IsCoinBase +// function. +func BenchmarkIsCoinBase(b *testing.B) { + tx, _ := btcutil.NewBlock(&Block100000).Tx(1) + b.ResetTimer() + for i := 0; i < b.N; i++ { + blockchain.IsCoinBase(tx) + } +} + +// BenchmarkIsCoinBaseTx performs a simple benchmark against the IsCoinBaseTx +// function. +func BenchmarkIsCoinBaseTx(b *testing.B) { + tx := Block100000.Transactions[1] + b.ResetTimer() + for i := 0; i < b.N; i++ { + blockchain.IsCoinBaseTx(tx) + } +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/blocklocator.go b/vendor/github.com/btcsuite/btcd/blockchain/blocklocator.go new file mode 100644 index 0000000000000000000000000000000000000000..181b2b7c378c961d4b942b9d1060c6fcaf548cbc --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/blocklocator.go @@ -0,0 +1,178 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" +) + +// BlockLocator is used to help locate a specific block. The algorithm for +// building the block locator is to add the hashes in reverse order until +// the genesis block is reached. In order to keep the list of locator hashes +// to a reasonable number of entries, first the most recent previous 10 block +// hashes are added, then the step is doubled each loop iteration to +// exponentially decrease the number of hashes as a function of the distance +// from the block being located. +// +// For example, assume you have a block chain with a side chain as depicted +// below: +// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18 +// \-> 16a -> 17a +// +// The block locator for block 17a would be the hashes of blocks: +// [17a 16a 15 14 13 12 11 10 9 8 6 2 genesis] +type BlockLocator []*wire.ShaHash + +// blockLocatorFromHash returns a block locator for the passed block hash. +// See BlockLocator for details on the algotirhm used to create a block locator. +// +// In addition to the general algorithm referenced above, there are a couple of +// special cases which are handled: +// +// - If the genesis hash is passed, there are no previous hashes to add and +// therefore the block locator will only consist of the genesis hash +// - If the passed hash is not currently known, the block locator will only +// consist of the passed hash +// +// This function MUST be called with the chain state lock held (for reads). +func (b *BlockChain) blockLocatorFromHash(hash *wire.ShaHash) BlockLocator { + // The locator contains the requested hash at the very least. + locator := make(BlockLocator, 0, wire.MaxBlockLocatorsPerMsg) + locator = append(locator, hash) + + // Nothing more to do if a locator for the genesis hash was requested. + if hash.IsEqual(b.chainParams.GenesisHash) { + return locator + } + + // Attempt to find the height of the block that corresponds to the + // passed hash, and if it's on a side chain, also find the height at + // which it forks from the main chain. + blockHeight := int32(-1) + forkHeight := int32(-1) + node, exists := b.index[*hash] + if !exists { + // Try to look up the height for passed block hash. Assume an + // error means it doesn't exist and just return the locator for + // the block itself. + var height int32 + err := b.db.View(func(dbTx database.Tx) error { + var err error + height, err = dbFetchHeightByHash(dbTx, hash) + return err + }) + if err != nil { + return locator + } + + blockHeight = height + } else { + blockHeight = node.height + + // Find the height at which this node forks from the main chain + // if the node is on a side chain. + if !node.inMainChain { + for n := node; n.parent != nil; n = n.parent { + if n.inMainChain { + forkHeight = n.height + break + } + } + } + } + + // Generate the block locators according to the algorithm described in + // in the BlockLocator comment and make sure to leave room for the final + // genesis hash. + // + // The error is intentionally ignored here since the only way the code + // could fail is if there is something wrong with the database which + // will be caught in short order anyways and it's also safe to ignore + // block locators. + _ = b.db.View(func(dbTx database.Tx) error { + iterNode := node + increment := int32(1) + for len(locator) < wire.MaxBlockLocatorsPerMsg-1 { + // Once there are 10 locators, exponentially increase + // the distance between each block locator. + if len(locator) > 10 { + increment *= 2 + } + blockHeight -= increment + if blockHeight < 1 { + break + } + + // As long as this is still on the side chain, walk + // backwards along the side chain nodes to each block + // height. + if forkHeight != -1 && blockHeight > forkHeight { + // Intentionally use parent field instead of the + // getPrevNodeFromNode function since we don't + // want to dynamically load nodes when building + // block locators. Side chain blocks should + // always be in memory already, and if they + // aren't for some reason it's ok to skip them. + for iterNode != nil && blockHeight > iterNode.height { + iterNode = iterNode.parent + } + if iterNode != nil && iterNode.height == blockHeight { + locator = append(locator, iterNode.hash) + } + continue + } + + // The desired block height is in the main chain, so + // look it up from the main chain database. + h, err := dbFetchHashByHeight(dbTx, blockHeight) + if err != nil { + // This shouldn't happen and it's ok to ignore + // block locators, so just continue to the next + // one. + log.Warnf("Lookup of known valid height failed %v", + blockHeight) + continue + } + locator = append(locator, h) + } + + return nil + }) + + // Append the appropriate genesis block. + locator = append(locator, b.chainParams.GenesisHash) + return locator +} + +// BlockLocatorFromHash returns a block locator for the passed block hash. +// See BlockLocator for details on the algorithm used to create a block locator. +// +// In addition to the general algorithm referenced above, there are a couple of +// special cases which are handled: +// +// - If the genesis hash is passed, there are no previous hashes to add and +// therefore the block locator will only consist of the genesis hash +// - If the passed hash is not currently known, the block locator will only +// consist of the passed hash +// +// This function is safe for concurrent access. +func (b *BlockChain) BlockLocatorFromHash(hash *wire.ShaHash) BlockLocator { + b.chainLock.RLock() + locator := b.blockLocatorFromHash(hash) + b.chainLock.RUnlock() + return locator +} + +// LatestBlockLocator returns a block locator for the latest known tip of the +// main (best) chain. +// +// This function is safe for concurrent access. +func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) { + b.chainLock.RLock() + locator := b.blockLocatorFromHash(b.bestNode.hash) + b.chainLock.RUnlock() + return locator, nil +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/chain.go b/vendor/github.com/btcsuite/btcd/blockchain/chain.go new file mode 100644 index 0000000000000000000000000000000000000000..b67c737aaab224d41d8431e8ead0f514555955e7 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/chain.go @@ -0,0 +1,1488 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "container/list" + "fmt" + "math/big" + "sort" + "sync" + "time" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +const ( + // maxOrphanBlocks is the maximum number of orphan blocks that can be + // queued. + maxOrphanBlocks = 100 + + // minMemoryNodes is the minimum number of consecutive nodes needed + // in memory in order to perform all necessary validation. It is used + // to determine when it's safe to prune nodes from memory without + // causing constant dynamic reloading. + minMemoryNodes = BlocksPerRetarget +) + +// blockNode represents a block within the block chain and is primarily used to +// aid in selecting the best chain to be the main chain. The main chain is +// stored into the block database. +type blockNode struct { + // parent is the parent block for this node. + parent *blockNode + + // children contains the child nodes for this node. Typically there + // will only be one, but sometimes there can be more than one and that + // is when the best chain selection algorithm is used. + children []*blockNode + + // hash is the double sha 256 of the block. + hash *wire.ShaHash + + // parentHash is the double sha 256 of the parent block. This is kept + // here over simply relying on parent.hash directly since block nodes + // are sparse and the parent node might not be in memory when its hash + // is needed. + parentHash *wire.ShaHash + + // height is the position in the block chain. + height int32 + + // workSum is the total amount of work in the chain up to and including + // this node. + workSum *big.Int + + // inMainChain denotes whether the block node is currently on the + // the main chain or not. This is used to help find the common + // ancestor when switching chains. + inMainChain bool + + // Some fields from block headers to aid in best chain selection. + version int32 + bits uint32 + timestamp time.Time +} + +// newBlockNode returns a new block node for the given block header. It is +// completely disconnected from the chain and the workSum value is just the work +// for the passed block. The work sum is updated accordingly when the node is +// inserted into a chain. +func newBlockNode(blockHeader *wire.BlockHeader, blockSha *wire.ShaHash, height int32) *blockNode { + // Make a copy of the hash so the node doesn't keep a reference to part + // of the full block/block header preventing it from being garbage + // collected. + prevHash := blockHeader.PrevBlock + node := blockNode{ + hash: blockSha, + parentHash: &prevHash, + workSum: CalcWork(blockHeader.Bits), + height: height, + version: blockHeader.Version, + bits: blockHeader.Bits, + timestamp: blockHeader.Timestamp, + } + return &node +} + +// orphanBlock represents a block that we don't yet have the parent for. It +// is a normal block plus an expiration time to prevent caching the orphan +// forever. +type orphanBlock struct { + block *btcutil.Block + expiration time.Time +} + +// removeChildNode deletes node from the provided slice of child block +// nodes. It ensures the final pointer reference is set to nil to prevent +// potential memory leaks. The original slice is returned unmodified if node +// is invalid or not in the slice. +// +// This function MUST be called with the chain state lock held (for writes). +func removeChildNode(children []*blockNode, node *blockNode) []*blockNode { + if node == nil { + return children + } + + // An indexing for loop is intentionally used over a range here as range + // does not reevaluate the slice on each iteration nor does it adjust + // the index for the modified slice. + for i := 0; i < len(children); i++ { + if children[i].hash.IsEqual(node.hash) { + copy(children[i:], children[i+1:]) + children[len(children)-1] = nil + return children[:len(children)-1] + } + } + return children +} + +// BestState houses information about the current best block and other info +// related to the state of the main chain as it exists from the point of view of +// the current best block. +// +// The BestSnapshot method can be used to obtain access to this information +// in a concurrent safe manner and the data will not be changed out from under +// the caller when chain state changes occur as the function name implies. +// However, the returned snapshot must be treated as immutable since it is +// shared by all callers. +type BestState struct { + Hash *wire.ShaHash // The hash of the block. + Height int32 // The height of the block. + Bits uint32 // The difficulty bits of the block. + BlockSize uint64 // The size of the block. + NumTxns uint64 // The number of txns in the block. + TotalTxns uint64 // The total number of txns in the chain. +} + +// newBestState returns a new best stats instance for the given parameters. +func newBestState(node *blockNode, blockSize, numTxns, totalTxns uint64) *BestState { + return &BestState{ + Hash: node.hash, + Height: node.height, + Bits: node.bits, + BlockSize: blockSize, + NumTxns: numTxns, + TotalTxns: totalTxns, + } +} + +// BlockChain provides functions for working with the bitcoin block chain. +// It includes functionality such as rejecting duplicate blocks, ensuring blocks +// follow all rules, orphan handling, checkpoint handling, and best chain +// selection with reorganization. +type BlockChain struct { + // The following fields are set when the instance is created and can't + // be changed afterwards, so there is no need to protect them with a + // separate mutex. + checkpointsByHeight map[int32]*chaincfg.Checkpoint + db database.DB + chainParams *chaincfg.Params + notifications NotificationCallback + sigCache *txscript.SigCache + indexManager IndexManager + + // chainLock protects concurrent access to the vast majority of the + // fields in this struct below this point. + chainLock sync.RWMutex + + // These fields are configuration parameters that can be toggled at + // runtime. They are protected by the chain lock. + noVerify bool + noCheckpoints bool + + // These fields are related to the memory block index. They are + // protected by the chain lock. + root *blockNode + bestNode *blockNode + index map[wire.ShaHash]*blockNode + depNodes map[wire.ShaHash][]*blockNode + + // These fields are related to handling of orphan blocks. They are + // protected by a combination of the chain lock and the orphan lock. + orphanLock sync.RWMutex + orphans map[wire.ShaHash]*orphanBlock + prevOrphans map[wire.ShaHash][]*orphanBlock + oldestOrphan *orphanBlock + blockCache map[wire.ShaHash]*btcutil.Block + + // These fields are related to checkpoint handling. They are protected + // by the chain lock. + nextCheckpoint *chaincfg.Checkpoint + checkpointBlock *btcutil.Block + + // The state is used as a fairly efficient way to cache information + // about the current best chain state that is returned to callers when + // requested. It operates on the principle of MVCC such that any time a + // new block becomes the best block, the state pointer is replaced with + // a new struct and the old state is left untouched. In this way, + // multiple callers can be pointing to different best chain states. + // This is acceptable for most callers because the state is only being + // queried at a specific point in time. + // + // In addition, some of the fields are stored in the database so the + // chain state can be quickly reconstructed on load. + stateLock sync.RWMutex + stateSnapshot *BestState +} + +// DisableVerify provides a mechanism to disable transaction script validation +// which you DO NOT want to do in production as it could allow double spends +// and other undesirable things. It is provided only for debug purposes since +// script validation is extremely intensive and when debugging it is sometimes +// nice to quickly get the chain. +// +// This function is safe for concurrent access. +func (b *BlockChain) DisableVerify(disable bool) { + b.chainLock.Lock() + b.noVerify = disable + b.chainLock.Unlock() +} + +// HaveBlock returns whether or not the chain instance has the block represented +// by the passed hash. This includes checking the various places a block can +// be like part of the main chain, on a side chain, or in the orphan pool. +// +// This function is safe for concurrent access. +func (b *BlockChain) HaveBlock(hash *wire.ShaHash) (bool, error) { + b.chainLock.RLock() + defer b.chainLock.RUnlock() + + exists, err := b.blockExists(hash) + if err != nil { + return false, err + } + return b.IsKnownOrphan(hash) || exists, nil +} + +// IsKnownOrphan returns whether the passed hash is currently a known orphan. +// Keep in mind that only a limited number of orphans are held onto for a +// limited amount of time, so this function must not be used as an absolute +// way to test if a block is an orphan block. A full block (as opposed to just +// its hash) must be passed to ProcessBlock for that purpose. However, calling +// ProcessBlock with an orphan that already exists results in an error, so this +// function provides a mechanism for a caller to intelligently detect *recent* +// duplicate orphans and react accordingly. +// +// This function is safe for concurrent access. +func (b *BlockChain) IsKnownOrphan(hash *wire.ShaHash) bool { + // Protect concurrent access. Using a read lock only so multiple + // readers can query without blocking each other. + b.orphanLock.RLock() + defer b.orphanLock.RUnlock() + + if _, exists := b.orphans[*hash]; exists { + return true + } + + return false +} + +// GetOrphanRoot returns the head of the chain for the provided hash from the +// map of orphan blocks. +// +// This function is safe for concurrent access. +func (b *BlockChain) GetOrphanRoot(hash *wire.ShaHash) *wire.ShaHash { + // Protect concurrent access. Using a read lock only so multiple + // readers can query without blocking each other. + b.orphanLock.RLock() + defer b.orphanLock.RUnlock() + + // Keep looping while the parent of each orphaned block is + // known and is an orphan itself. + orphanRoot := hash + prevHash := hash + for { + orphan, exists := b.orphans[*prevHash] + if !exists { + break + } + orphanRoot = prevHash + prevHash = &orphan.block.MsgBlock().Header.PrevBlock + } + + return orphanRoot +} + +// removeOrphanBlock removes the passed orphan block from the orphan pool and +// previous orphan index. +func (b *BlockChain) removeOrphanBlock(orphan *orphanBlock) { + // Protect concurrent access. + b.orphanLock.Lock() + defer b.orphanLock.Unlock() + + // Remove the orphan block from the orphan pool. + orphanHash := orphan.block.Sha() + delete(b.orphans, *orphanHash) + + // Remove the reference from the previous orphan index too. An indexing + // for loop is intentionally used over a range here as range does not + // reevaluate the slice on each iteration nor does it adjust the index + // for the modified slice. + prevHash := &orphan.block.MsgBlock().Header.PrevBlock + orphans := b.prevOrphans[*prevHash] + for i := 0; i < len(orphans); i++ { + hash := orphans[i].block.Sha() + if hash.IsEqual(orphanHash) { + copy(orphans[i:], orphans[i+1:]) + orphans[len(orphans)-1] = nil + orphans = orphans[:len(orphans)-1] + i-- + } + } + b.prevOrphans[*prevHash] = orphans + + // Remove the map entry altogether if there are no longer any orphans + // which depend on the parent hash. + if len(b.prevOrphans[*prevHash]) == 0 { + delete(b.prevOrphans, *prevHash) + } +} + +// addOrphanBlock adds the passed block (which is already determined to be +// an orphan prior calling this function) to the orphan pool. It lazily cleans +// up any expired blocks so a separate cleanup poller doesn't need to be run. +// It also imposes a maximum limit on the number of outstanding orphan +// blocks and will remove the oldest received orphan block if the limit is +// exceeded. +func (b *BlockChain) addOrphanBlock(block *btcutil.Block) { + // Remove expired orphan blocks. + for _, oBlock := range b.orphans { + if time.Now().After(oBlock.expiration) { + b.removeOrphanBlock(oBlock) + continue + } + + // Update the oldest orphan block pointer so it can be discarded + // in case the orphan pool fills up. + if b.oldestOrphan == nil || oBlock.expiration.Before(b.oldestOrphan.expiration) { + b.oldestOrphan = oBlock + } + } + + // Limit orphan blocks to prevent memory exhaustion. + if len(b.orphans)+1 > maxOrphanBlocks { + // Remove the oldest orphan to make room for the new one. + b.removeOrphanBlock(b.oldestOrphan) + b.oldestOrphan = nil + } + + // Protect concurrent access. This is intentionally done here instead + // of near the top since removeOrphanBlock does its own locking and + // the range iterator is not invalidated by removing map entries. + b.orphanLock.Lock() + defer b.orphanLock.Unlock() + + // Insert the block into the orphan map with an expiration time + // 1 hour from now. + expiration := time.Now().Add(time.Hour) + oBlock := &orphanBlock{ + block: block, + expiration: expiration, + } + b.orphans[*block.Sha()] = oBlock + + // Add to previous hash lookup index for faster dependency lookups. + prevHash := &block.MsgBlock().Header.PrevBlock + b.prevOrphans[*prevHash] = append(b.prevOrphans[*prevHash], oBlock) + + return +} + +// loadBlockNode loads the block identified by hash from the block database, +// creates a block node from it, and updates the memory block chain accordingly. +// It is used mainly to dynamically load previous blocks from the database as +// they are needed to avoid needing to put the entire block chain in memory. +// +// This function MUST be called with the chain state lock held (for writes). +// The database transaction may be read-only. +func (b *BlockChain) loadBlockNode(dbTx database.Tx, hash *wire.ShaHash) (*blockNode, error) { + // Load the block header and height from the db. + blockHeader, err := dbFetchHeaderByHash(dbTx, hash) + if err != nil { + return nil, err + } + blockHeight, err := dbFetchHeightByHash(dbTx, hash) + if err != nil { + return nil, err + } + + // Create the new block node for the block and set the work. + node := newBlockNode(blockHeader, hash, blockHeight) + node.inMainChain = true + + // Add the node to the chain. + // There are a few possibilities here: + // 1) This node is a child of an existing block node + // 2) This node is the parent of one or more nodes + // 3) Neither 1 or 2 is true which implies it's an orphan block and + // therefore is an error to insert into the chain + prevHash := &blockHeader.PrevBlock + if parentNode, ok := b.index[*prevHash]; ok { + // Case 1 -- This node is a child of an existing block node. + // Update the node's work sum with the sum of the parent node's + // work sum and this node's work, append the node as a child of + // the parent node and set this node's parent to the parent + // node. + node.workSum = node.workSum.Add(parentNode.workSum, node.workSum) + parentNode.children = append(parentNode.children, node) + node.parent = parentNode + + } else if childNodes, ok := b.depNodes[*hash]; ok { + // Case 2 -- This node is the parent of one or more nodes. + // Update the node's work sum by subtracting this node's work + // from the sum of its first child, and connect the node to all + // of its children. + node.workSum.Sub(childNodes[0].workSum, node.workSum) + for _, childNode := range childNodes { + childNode.parent = node + node.children = append(node.children, childNode) + b.root = node + } + + } else { + // Case 3 -- The node doesn't have a parent and is not the + // parent of another node. This means an arbitrary orphan block + // is trying to be loaded which is not allowed. + str := "loadBlockNode: attempt to insert orphan block %v" + return nil, AssertError(fmt.Sprintf(str, hash)) + } + + // Add the new node to the indices for faster lookups. + b.index[*hash] = node + b.depNodes[*prevHash] = append(b.depNodes[*prevHash], node) + + return node, nil +} + +// getPrevNodeFromBlock returns a block node for the block previous to the +// passed block (the passed block's parent). When it is already in the memory +// block chain, it simply returns it. Otherwise, it loads the previous block +// header from the block database, creates a new block node from it, and returns +// it. The returned node will be nil if the genesis block is passed. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) getPrevNodeFromBlock(block *btcutil.Block) (*blockNode, error) { + // Genesis block. + prevHash := &block.MsgBlock().Header.PrevBlock + if prevHash.IsEqual(zeroHash) { + return nil, nil + } + + // Return the existing previous block node if it's already there. + if bn, ok := b.index[*prevHash]; ok { + return bn, nil + } + + // Dynamically load the previous block from the block database, create + // a new block node for it, and update the memory chain accordingly. + var prevBlockNode *blockNode + err := b.db.View(func(dbTx database.Tx) error { + var err error + prevBlockNode, err = b.loadBlockNode(dbTx, prevHash) + return err + }) + return prevBlockNode, err +} + +// getPrevNodeFromNode returns a block node for the block previous to the +// passed block node (the passed block node's parent). When the node is already +// connected to a parent, it simply returns it. Otherwise, it loads the +// associated block from the database to obtain the previous hash and uses that +// to dynamically create a new block node and return it. The memory block +// chain is updated accordingly. The returned node will be nil if the genesis +// block is passed. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) getPrevNodeFromNode(node *blockNode) (*blockNode, error) { + // Return the existing previous block node if it's already there. + if node.parent != nil { + return node.parent, nil + } + + // Genesis block. + if node.hash.IsEqual(b.chainParams.GenesisHash) { + return nil, nil + } + + // Dynamically load the previous block from the block database, create + // a new block node for it, and update the memory chain accordingly. + var prevBlockNode *blockNode + err := b.db.View(func(dbTx database.Tx) error { + var err error + prevBlockNode, err = b.loadBlockNode(dbTx, node.parentHash) + return err + }) + return prevBlockNode, err +} + +// removeBlockNode removes the passed block node from the memory chain by +// unlinking all of its children and removing it from the the node and +// dependency indices. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) removeBlockNode(node *blockNode) error { + if node.parent != nil { + return AssertError(fmt.Sprintf("removeBlockNode must be "+ + "called with a node at the front of the chain - node %v", + node.hash)) + } + + // Remove the node from the node index. + delete(b.index, *node.hash) + + // Unlink all of the node's children. + for _, child := range node.children { + child.parent = nil + } + node.children = nil + + // Remove the reference from the dependency index. + prevHash := node.parentHash + if children, ok := b.depNodes[*prevHash]; ok { + // Find the node amongst the children of the + // dependencies for the parent hash and remove it. + b.depNodes[*prevHash] = removeChildNode(children, node) + + // Remove the map entry altogether if there are no + // longer any nodes which depend on the parent hash. + if len(b.depNodes[*prevHash]) == 0 { + delete(b.depNodes, *prevHash) + } + } + + return nil +} + +// pruneBlockNodes removes references to old block nodes which are no longer +// needed so they may be garbage collected. In order to validate block rules +// and choose the best chain, only a portion of the nodes which form the block +// chain are needed in memory. This function walks the chain backwards from the +// current best chain to find any nodes before the first needed block node. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) pruneBlockNodes() error { + // Walk the chain backwards to find what should be the new root node. + // Intentionally use node.parent instead of getPrevNodeFromNode since + // the latter loads the node and the goal is to find nodes still in + // memory that can be pruned. + newRootNode := b.bestNode + for i := int32(0); i < minMemoryNodes-1 && newRootNode != nil; i++ { + newRootNode = newRootNode.parent + } + + // Nothing to do if there are not enough nodes. + if newRootNode == nil || newRootNode.parent == nil { + return nil + } + + // Push the nodes to delete on a list in reverse order since it's easier + // to prune them going forwards than it is backwards. This will + // typically end up being a single node since pruning is currently done + // just before each new node is created. However, that might be tuned + // later to only prune at intervals, so the code needs to account for + // the possibility of multiple nodes. + deleteNodes := list.New() + for node := newRootNode.parent; node != nil; node = node.parent { + deleteNodes.PushFront(node) + } + + // Loop through each node to prune, unlink its children, remove it from + // the dependency index, and remove it from the node index. + for e := deleteNodes.Front(); e != nil; e = e.Next() { + node := e.Value.(*blockNode) + err := b.removeBlockNode(node) + if err != nil { + return err + } + } + + // Set the new root node. + b.root = newRootNode + + return nil +} + +// isMajorityVersion determines if a previous number of blocks in the chain +// starting with startNode are at least the minimum passed version. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) isMajorityVersion(minVer int32, startNode *blockNode, numRequired uint64) bool { + numFound := uint64(0) + iterNode := startNode + for i := uint64(0); i < b.chainParams.BlockUpgradeNumToCheck && + numFound < numRequired && iterNode != nil; i++ { + // This node has a version that is at least the minimum version. + if iterNode.version >= minVer { + numFound++ + } + + // Get the previous block node. This function is used over + // simply accessing iterNode.parent directly as it will + // dynamically create previous block nodes as needed. This + // helps allow only the pieces of the chain that are needed + // to remain in memory. + var err error + iterNode, err = b.getPrevNodeFromNode(iterNode) + if err != nil { + break + } + } + + return numFound >= numRequired +} + +// calcPastMedianTime calculates the median time of the previous few blocks +// prior to, and including, the passed block node. It is primarily used to +// validate new blocks have sane timestamps. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) calcPastMedianTime(startNode *blockNode) (time.Time, error) { + // Genesis block. + if startNode == nil { + return b.chainParams.GenesisBlock.Header.Timestamp, nil + } + + // Create a slice of the previous few block timestamps used to calculate + // the median per the number defined by the constant medianTimeBlocks. + timestamps := make([]time.Time, medianTimeBlocks) + numNodes := 0 + iterNode := startNode + for i := 0; i < medianTimeBlocks && iterNode != nil; i++ { + timestamps[i] = iterNode.timestamp + numNodes++ + + // Get the previous block node. This function is used over + // simply accessing iterNode.parent directly as it will + // dynamically create previous block nodes as needed. This + // helps allow only the pieces of the chain that are needed + // to remain in memory. + var err error + iterNode, err = b.getPrevNodeFromNode(iterNode) + if err != nil { + log.Errorf("getPrevNodeFromNode: %v", err) + return time.Time{}, err + } + } + + // Prune the slice to the actual number of available timestamps which + // will be fewer than desired near the beginning of the block chain + // and sort them. + timestamps = timestamps[:numNodes] + sort.Sort(timeSorter(timestamps)) + + // NOTE: bitcoind incorrectly calculates the median for even numbers of + // blocks. A true median averages the middle two elements for a set + // with an even number of elements in it. Since the constant for the + // previous number of blocks to be used is odd, this is only an issue + // for a few blocks near the beginning of the chain. I suspect this is + // an optimization even though the result is slightly wrong for a few + // of the first blocks since after the first few blocks, there will + // always be an odd number of blocks in the set per the constant. + // + // This code follows suit to ensure the same rules are used as bitcoind + // however, be aware that should the medianTimeBlocks constant ever be + // changed to an even number, this code will be wrong. + medianTimestamp := timestamps[numNodes/2] + return medianTimestamp, nil +} + +// CalcPastMedianTime calculates the median time of the previous few blocks +// prior to, and including, the end of the current best chain. It is primarily +// used to ensure new blocks have sane timestamps. +// +// This function is safe for concurrent access. +func (b *BlockChain) CalcPastMedianTime() (time.Time, error) { + b.chainLock.Lock() + defer b.chainLock.Unlock() + + return b.calcPastMedianTime(b.bestNode) +} + +// getReorganizeNodes finds the fork point between the main chain and the passed +// node and returns a list of block nodes that would need to be detached from +// the main chain and a list of block nodes that would need to be attached to +// the fork point (which will be the end of the main chain after detaching the +// returned list of block nodes) in order to reorganize the chain such that the +// passed node is the new end of the main chain. The lists will be empty if the +// passed node is not on a side chain. +// +// This function MUST be called with the chain state lock held (for reads). +func (b *BlockChain) getReorganizeNodes(node *blockNode) (*list.List, *list.List) { + // Nothing to detach or attach if there is no node. + attachNodes := list.New() + detachNodes := list.New() + if node == nil { + return detachNodes, attachNodes + } + + // Find the fork point (if any) adding each block to the list of nodes + // to attach to the main tree. Push them onto the list in reverse order + // so they are attached in the appropriate order when iterating the list + // later. + ancestor := node + for ; ancestor.parent != nil; ancestor = ancestor.parent { + if ancestor.inMainChain { + break + } + attachNodes.PushFront(ancestor) + } + + // TODO(davec): Use prevNodeFromNode function in case the requested + // node is further back than the what is in memory. This shouldn't + // happen in the normal course of operation, but the ability to fetch + // input transactions of arbitrary blocks will likely to be exposed at + // some point and that could lead to an issue here. + + // Start from the end of the main chain and work backwards until the + // common ancestor adding each block to the list of nodes to detach from + // the main chain. + for n := b.bestNode; n != nil && n.parent != nil; n = n.parent { + if n.hash.IsEqual(ancestor.hash) { + break + } + detachNodes.PushBack(n) + } + + return detachNodes, attachNodes +} + +// dbMaybeStoreBlock stores the provided block in the database if it's not +// already there. +func dbMaybeStoreBlock(dbTx database.Tx, block *btcutil.Block) error { + hasBlock, err := dbTx.HasBlock(block.Sha()) + if err != nil { + return err + } + if hasBlock { + return nil + } + + return dbTx.StoreBlock(block) +} + +// connectBlock handles connecting the passed node/block to the end of the main +// (best) chain. +// +// This passed utxo view must have all referenced txos the block spends marked +// as spent and all of the new txos the block creates added to it. In addition, +// the passed stxos slice must be populated with all of the information for the +// spent txos. This approach is used because the connection validation that +// must happen prior to calling this function requires the same details, so +// it would be inefficient to repeat it. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *UtxoViewpoint, stxos []spentTxOut) error { + // Make sure it's extending the end of the best chain. + prevHash := &block.MsgBlock().Header.PrevBlock + if !prevHash.IsEqual(b.bestNode.hash) { + return AssertError("connectBlock must be called with a block " + + "that extends the main chain") + } + + // Sanity check the correct number of stxos are provided. + if len(stxos) != countSpentOutputs(block) { + return AssertError("connectBlock called with inconsistent " + + "spent transaction out information") + } + + // Generate a new best state snapshot that will be used to update the + // database and later memory if all database updates are successful. + b.stateLock.RLock() + curTotalTxns := b.stateSnapshot.TotalTxns + b.stateLock.RUnlock() + numTxns := uint64(len(block.MsgBlock().Transactions)) + blockSize := uint64(block.MsgBlock().SerializeSize()) + state := newBestState(node, blockSize, numTxns, curTotalTxns+numTxns) + + // Atomically insert info into the database. + err := b.db.Update(func(dbTx database.Tx) error { + // Update best block state. + err := dbPutBestState(dbTx, state, node.workSum) + if err != nil { + return err + } + + // Add the block hash and height to the block index which tracks + // the main chain. + err = dbPutBlockIndex(dbTx, block.Sha(), node.height) + if err != nil { + return err + } + + // Update the utxo set using the state of the utxo view. This + // entails removing all of the utxos spent and adding the new + // ones created by the block. + err = dbPutUtxoView(dbTx, view) + if err != nil { + return err + } + + // Update the transaction spend journal by adding a record for + // the block that contains all txos spent by it. + err = dbPutSpendJournalEntry(dbTx, block.Sha(), stxos) + if err != nil { + return err + } + + // Insert the block into the database if it's not already there. + err = dbMaybeStoreBlock(dbTx, block) + if err != nil { + return err + } + + // Allow the index manager to call each of the currently active + // optional indexes with the block being connected so they can + // update themselves accordingly. + if b.indexManager != nil { + err := b.indexManager.ConnectBlock(dbTx, block, view) + if err != nil { + return err + } + } + + return nil + }) + if err != nil { + return err + } + + // Prune fully spent entries and mark all entries in the view unmodified + // now that the modifications have been committed to the database. + view.commit() + + // Add the new node to the memory main chain indices for faster + // lookups. + node.inMainChain = true + b.index[*node.hash] = node + b.depNodes[*prevHash] = append(b.depNodes[*prevHash], node) + + // This node is now the end of the best chain. + b.bestNode = node + + // Update the state for the best block. Notice how this replaces the + // entire struct instead of updating the existing one. This effectively + // allows the old version to act as a snapshot which callers can use + // freely without needing to hold a lock for the duration. See the + // comments on the state variable for more details. + b.stateLock.Lock() + b.stateSnapshot = state + b.stateLock.Unlock() + + // Notify the caller that the block was connected to the main chain. + // The caller would typically want to react with actions such as + // updating wallets. + b.chainLock.Unlock() + b.sendNotification(NTBlockConnected, block) + b.chainLock.Lock() + + return nil +} + +// disconnectBlock handles disconnecting the passed node/block from the end of +// the main (best) chain. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view *UtxoViewpoint) error { + // Make sure the node being disconnected is the end of the best chain. + if !node.hash.IsEqual(b.bestNode.hash) { + return AssertError("disconnectBlock must be called with the " + + "block at the end of the main chain") + } + + // Get the previous block node. This function is used over simply + // accessing node.parent directly as it will dynamically create previous + // block nodes as needed. This helps allow only the pieces of the chain + // that are needed to remain in memory. + prevNode, err := b.getPrevNodeFromNode(node) + if err != nil { + return err + } + + // Load the previous block since some details for it are needed below. + var prevBlock *btcutil.Block + err = b.db.View(func(dbTx database.Tx) error { + var err error + prevBlock, err = dbFetchBlockByHash(dbTx, prevNode.hash) + return err + }) + if err != nil { + return err + } + + // Generate a new best state snapshot that will be used to update the + // database and later memory if all database updates are successful. + b.stateLock.RLock() + curTotalTxns := b.stateSnapshot.TotalTxns + b.stateLock.RUnlock() + numTxns := uint64(len(prevBlock.MsgBlock().Transactions)) + blockSize := uint64(prevBlock.MsgBlock().SerializeSize()) + newTotalTxns := curTotalTxns - uint64(len(block.MsgBlock().Transactions)) + state := newBestState(prevNode, blockSize, numTxns, newTotalTxns) + + err = b.db.Update(func(dbTx database.Tx) error { + // Update best block state. + err := dbPutBestState(dbTx, state, node.workSum) + if err != nil { + return err + } + + // Remove the block hash and height from the block index which + // tracks the main chain. + err = dbRemoveBlockIndex(dbTx, block.Sha(), node.height) + if err != nil { + return err + } + + // Update the utxo set using the state of the utxo view. This + // entails restoring all of the utxos spent and removing the new + // ones created by the block. + err = dbPutUtxoView(dbTx, view) + if err != nil { + return err + } + + // Update the transaction spend journal by removing the record + // that contains all txos spent by the block . + err = dbRemoveSpendJournalEntry(dbTx, block.Sha()) + if err != nil { + return err + } + + // Allow the index manager to call each of the currently active + // optional indexes with the block being disconnected so they + // can update themselves accordingly. + if b.indexManager != nil { + err := b.indexManager.DisconnectBlock(dbTx, block, view) + if err != nil { + return err + } + } + + return nil + }) + if err != nil { + return err + } + + // Prune fully spent entries and mark all entries in the view unmodified + // now that the modifications have been committed to the database. + view.commit() + + // Put block in the side chain cache. + node.inMainChain = false + b.blockCache[*node.hash] = block + + // This node's parent is now the end of the best chain. + b.bestNode = node.parent + + // Update the state for the best block. Notice how this replaces the + // entire struct instead of updating the existing one. This effectively + // allows the old version to act as a snapshot which callers can use + // freely without needing to hold a lock for the duration. See the + // comments on the state variable for more details. + b.stateLock.Lock() + b.stateSnapshot = state + b.stateLock.Unlock() + + // Notify the caller that the block was disconnected from the main + // chain. The caller would typically want to react with actions such as + // updating wallets. + b.chainLock.Unlock() + b.sendNotification(NTBlockDisconnected, block) + b.chainLock.Lock() + + return nil +} + +// countSpentOutputs returns the number of utxos the passed block spends. +func countSpentOutputs(block *btcutil.Block) int { + // Exclude the coinbase transaction since it can't spend anything. + var numSpent int + for _, tx := range block.Transactions()[1:] { + numSpent += len(tx.MsgTx().TxIn) + } + return numSpent +} + +// reorganizeChain reorganizes the block chain by disconnecting the nodes in the +// detachNodes list and connecting the nodes in the attach list. It expects +// that the lists are already in the correct order and are in sync with the +// end of the current best chain. Specifically, nodes that are being +// disconnected must be in reverse order (think of popping them off the end of +// the chain) and nodes the are being attached must be in forwards order +// (think pushing them onto the end of the chain). +// +// The flags modify the behavior of this function as follows: +// - BFDryRun: Only the checks which ensure the reorganize can be completed +// successfully are performed. The chain is not reorganized. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags BehaviorFlags) error { + // Ensure all of the needed side chain blocks are in the cache. + for e := attachNodes.Front(); e != nil; e = e.Next() { + n := e.Value.(*blockNode) + if _, exists := b.blockCache[*n.hash]; !exists { + return AssertError(fmt.Sprintf("block %v is missing "+ + "from the side chain block cache", n.hash)) + } + } + + // All of the blocks to detach and related spend journal entries needed + // to unspend transaction outputs in the blocks being disconnected must + // be loaded from the database during the reorg check phase below and + // then they are needed again when doing the actual database updates. + // Rather than doing two loads, cache the loaded data into these slices. + detachBlocks := make([]*btcutil.Block, 0, detachNodes.Len()) + detachSpentTxOuts := make([][]spentTxOut, 0, detachNodes.Len()) + + // Disconnect all of the blocks back to the point of the fork. This + // entails loading the blocks and their associated spent txos from the + // database and using that information to unspend all of the spent txos + // and remove the utxos created by the blocks. + view := NewUtxoViewpoint() + view.SetBestHash(b.bestNode.hash) + for e := detachNodes.Front(); e != nil; e = e.Next() { + n := e.Value.(*blockNode) + var block *btcutil.Block + err := b.db.View(func(dbTx database.Tx) error { + var err error + block, err = dbFetchBlockByHash(dbTx, n.hash) + return err + }) + + // Load all of the utxos referenced by the block that aren't + // already in the view. + err = view.fetchInputUtxos(b.db, block) + if err != nil { + return err + } + + // Load all of the spent txos for the block from the spend + // journal. + var stxos []spentTxOut + err = b.db.View(func(dbTx database.Tx) error { + stxos, err = dbFetchSpendJournalEntry(dbTx, block, view) + return err + }) + if err != nil { + return err + } + + // Store the loaded block and spend journal entry for later. + detachBlocks = append(detachBlocks, block) + detachSpentTxOuts = append(detachSpentTxOuts, stxos) + + err = view.disconnectTransactions(block, stxos) + if err != nil { + return err + } + } + + // Perform several checks to verify each block that needs to be attached + // to the main chain can be connected without violating any rules and + // without actually connecting the block. + // + // NOTE: These checks could be done directly when connecting a block, + // however the downside to that approach is that if any of these checks + // fail after disconnecting some blocks or attaching others, all of the + // operations have to be rolled back to get the chain back into the + // state it was before the rule violation (or other failure). There are + // at least a couple of ways accomplish that rollback, but both involve + // tweaking the chain and/or database. This approach catches these + // issues before ever modifying the chain. + for e := attachNodes.Front(); e != nil; e = e.Next() { + n := e.Value.(*blockNode) + block := b.blockCache[*n.hash] + + // Notice the spent txout details are not requested here and + // thus will not be generated. This is done because the state + // is not being immediately written to the database, so it is + // not needed. + err := b.checkConnectBlock(n, block, view, nil) + if err != nil { + return err + } + } + + // Skip disconnecting and connecting the blocks when running with the + // dry run flag set. + if flags&BFDryRun == BFDryRun { + return nil + } + + // Reset the view for the actual connection code below. This is + // required because the view was previously modified when checking if + // the reorg would be successful and the connection code requires the + // view to be valid from the viewpoint of each block being connected or + // disconnected. + view = NewUtxoViewpoint() + view.SetBestHash(b.bestNode.hash) + + // Disconnect blocks from the main chain. + for i, e := 0, detachNodes.Front(); e != nil; i, e = i+1, e.Next() { + n := e.Value.(*blockNode) + block := detachBlocks[i] + + // Load all of the utxos referenced by the block that aren't + // already in the view. + err := view.fetchInputUtxos(b.db, block) + if err != nil { + return err + } + + // Update the view to unspend all of the spent txos and remove + // the utxos created by the block. + err = view.disconnectTransactions(block, detachSpentTxOuts[i]) + if err != nil { + return err + } + + // Update the database and chain state. + err = b.disconnectBlock(n, block, view) + if err != nil { + return err + } + } + + // Connect the new best chain blocks. + for e := attachNodes.Front(); e != nil; e = e.Next() { + n := e.Value.(*blockNode) + block := b.blockCache[*n.hash] + + // Load all of the utxos referenced by the block that aren't + // already in the view. + err := view.fetchInputUtxos(b.db, block) + if err != nil { + return err + } + + // Update the view to mark all utxos referenced by the block + // as spent and add all transactions being created by this block + // to it. Also, provide an stxo slice so the spent txout + // details are generated. + stxos := make([]spentTxOut, 0, countSpentOutputs(block)) + err = view.connectTransactions(block, &stxos) + if err != nil { + return err + } + + // Update the database and chain state. + err = b.connectBlock(n, block, view, stxos) + if err != nil { + return err + } + delete(b.blockCache, *n.hash) + } + + // Log the point where the chain forked. + firstAttachNode := attachNodes.Front().Value.(*blockNode) + forkNode, err := b.getPrevNodeFromNode(firstAttachNode) + if err == nil { + log.Infof("REORGANIZE: Chain forks at %v", forkNode.hash) + } + + // Log the old and new best chain heads. + firstDetachNode := detachNodes.Front().Value.(*blockNode) + lastAttachNode := attachNodes.Back().Value.(*blockNode) + log.Infof("REORGANIZE: Old best chain head was %v", firstDetachNode.hash) + log.Infof("REORGANIZE: New best chain head is %v", lastAttachNode.hash) + + return nil +} + +// connectBestChain handles connecting the passed block to the chain while +// respecting proper chain selection according to the chain with the most +// proof of work. In the typical case, the new block simply extends the main +// chain. However, it may also be extending (or creating) a side chain (fork) +// which may or may not end up becoming the main chain depending on which fork +// cumulatively has the most proof of work. +// +// The flags modify the behavior of this function as follows: +// - BFFastAdd: Avoids several expensive transaction validation operations. +// This is useful when using checkpoints. +// - BFDryRun: Prevents the block from being connected and avoids modifying the +// state of the memory chain index. Also, any log messages related to +// modifying the state are avoided. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, flags BehaviorFlags) error { + fastAdd := flags&BFFastAdd == BFFastAdd + dryRun := flags&BFDryRun == BFDryRun + + // We are extending the main (best) chain with a new block. This is the + // most common case. + if node.parentHash.IsEqual(b.bestNode.hash) { + // Perform several checks to verify the block can be connected + // to the main chain without violating any rules and without + // actually connecting the block. + view := NewUtxoViewpoint() + view.SetBestHash(node.parentHash) + stxos := make([]spentTxOut, 0, countSpentOutputs(block)) + if !fastAdd { + err := b.checkConnectBlock(node, block, view, &stxos) + if err != nil { + return err + } + } + + // Don't connect the block if performing a dry run. + if dryRun { + return nil + } + + // In the fast add case the code to check the block connection + // was skipped, so the utxo view needs to load the referenced + // utxos, spend them, and add the new utxos being created by + // this block. + if fastAdd { + err := view.fetchInputUtxos(b.db, block) + if err != nil { + return err + } + err = view.connectTransactions(block, &stxos) + if err != nil { + return err + } + } + + // Connect the block to the main chain. + err := b.connectBlock(node, block, view, stxos) + if err != nil { + return err + } + + // Connect the parent node to this node. + if node.parent != nil { + node.parent.children = append(node.parent.children, node) + } + + return nil + } + if fastAdd { + log.Warnf("fastAdd set in the side chain case? %v\n", + block.Sha()) + } + + // We're extending (or creating) a side chain which may or may not + // become the main chain, but in either case we need the block stored + // for future processing, so add the block to the side chain holding + // cache. + if !dryRun { + log.Debugf("Adding block %v to side chain cache", node.hash) + } + b.blockCache[*node.hash] = block + b.index[*node.hash] = node + + // Connect the parent node to this node. + node.inMainChain = false + node.parent.children = append(node.parent.children, node) + + // Remove the block from the side chain cache and disconnect it from the + // parent node when the function returns when running in dry run mode. + if dryRun { + defer func() { + children := node.parent.children + children = removeChildNode(children, node) + node.parent.children = children + + delete(b.index, *node.hash) + delete(b.blockCache, *node.hash) + }() + } + + // We're extending (or creating) a side chain, but the cumulative + // work for this new side chain is not enough to make it the new chain. + if node.workSum.Cmp(b.bestNode.workSum) <= 0 { + // Skip Logging info when the dry run flag is set. + if dryRun { + return nil + } + + // Find the fork point. + fork := node + for ; fork.parent != nil; fork = fork.parent { + if fork.inMainChain { + break + } + } + + // Log information about how the block is forking the chain. + if fork.hash.IsEqual(node.parent.hash) { + log.Infof("FORK: Block %v forks the chain at height %d"+ + "/block %v, but does not cause a reorganize", + node.hash, fork.height, fork.hash) + } else { + log.Infof("EXTEND FORK: Block %v extends a side chain "+ + "which forks the chain at height %d/block %v", + node.hash, fork.height, fork.hash) + } + + return nil + } + + // We're extending (or creating) a side chain and the cumulative work + // for this new side chain is more than the old best chain, so this side + // chain needs to become the main chain. In order to accomplish that, + // find the common ancestor of both sides of the fork, disconnect the + // blocks that form the (now) old fork from the main chain, and attach + // the blocks that form the new chain to the main chain starting at the + // common ancenstor (the point where the chain forked). + detachNodes, attachNodes := b.getReorganizeNodes(node) + + // Reorganize the chain. + if !dryRun { + log.Infof("REORGANIZE: Block %v is causing a reorganize.", + node.hash) + } + err := b.reorganizeChain(detachNodes, attachNodes, flags) + if err != nil { + return err + } + + return nil +} + +// IsCurrent returns whether or not the chain believes it is current. Several +// factors are used to guess, but the key factors that allow the chain to +// believe it is current are: +// - Latest block height is after the latest checkpoint (if enabled) +// - Latest block has a timestamp newer than 24 hours ago +// +// This function is safe for concurrent access. +func (b *BlockChain) IsCurrent(timeSource MedianTimeSource) bool { + b.chainLock.RLock() + defer b.chainLock.RUnlock() + + // Not current if the latest main (best) chain height is before the + // latest known good checkpoint (when checkpoints are enabled). + checkpoint := b.latestCheckpoint() + if checkpoint != nil && b.bestNode.height < checkpoint.Height { + return false + } + + // Not current if the latest best block has a timestamp before 24 hours + // ago. + minus24Hours := timeSource.AdjustedTime().Add(-24 * time.Hour) + if b.bestNode.timestamp.Before(minus24Hours) { + return false + } + + // The chain appears to be current if the above checks did not report + // otherwise. + return true +} + +// BestSnapshot returns information about the current best chain block and +// related state as of the current point in time. The returned instance must be +// treated as immutable since it is shared by all callers. +// +// This function is safe for concurrent access. +func (b *BlockChain) BestSnapshot() *BestState { + b.stateLock.RLock() + snapshot := b.stateSnapshot + b.stateLock.RUnlock() + return snapshot +} + +// IndexManager provides a generic interface that the is called when blocks are +// connected and disconnected to and from the tip of the main chain for the +// purpose of supporting optional indexes. +type IndexManager interface { + // Init is invoked during chain initialize in order to allow the index + // manager to initialize itself and any indexes it is managing. + Init(*BlockChain) error + + // ConnectBlock is invoked when a new block has been connected to the + // main chain. + ConnectBlock(database.Tx, *btcutil.Block, *UtxoViewpoint) error + + // DisconnectBlock is invoked when a block has been disconnected from + // the main chain. + DisconnectBlock(database.Tx, *btcutil.Block, *UtxoViewpoint) error +} + +// Config is a descriptor which specifies the blockchain instance configuration. +type Config struct { + // DB defines the database which houses the blocks and will be used to + // store all metadata created by this package such as the utxo set. + // + // This field is required. + DB database.DB + + // ChainParams identifies which chain parameters the chain is associated + // with. + // + // This field is required. + ChainParams *chaincfg.Params + + // Notifications defines a callback to which notifications will be sent + // when various events take place. See the documentation for + // Notification and NotificationType for details on the types and + // contents of notifications. + // + // This field can be nil if the caller is not interested in receiving + // notifications. + Notifications NotificationCallback + + // SigCache defines a signature cache to use when when validating + // signatures. This is typically most useful when individual + // transactions are already being validated prior to their inclusion in + // a block such as what is usually done via a transaction memory pool. + // + // This field can be nil if the caller is not interested in using a + // signature cache. + SigCache *txscript.SigCache + + // IndexManager defines an index manager to use when initializing the + // chain and connecting and disconnecting blocks. + // + // This field can be nil if the caller does not wish to make use of an + // index manager. + IndexManager IndexManager +} + +// New returns a BlockChain instance using the provided configuration details. +func New(config *Config) (*BlockChain, error) { + // Enforce required config fields. + if config.DB == nil { + return nil, AssertError("blockchain.New database is nil") + } + if config.ChainParams == nil { + return nil, AssertError("blockchain.New chain parameters nil") + } + + // Generate a checkpoint by height map from the provided checkpoints. + params := config.ChainParams + var checkpointsByHeight map[int32]*chaincfg.Checkpoint + if len(params.Checkpoints) > 0 { + checkpointsByHeight = make(map[int32]*chaincfg.Checkpoint) + for i := range params.Checkpoints { + checkpoint := ¶ms.Checkpoints[i] + checkpointsByHeight[checkpoint.Height] = checkpoint + } + } + + b := BlockChain{ + checkpointsByHeight: checkpointsByHeight, + db: config.DB, + chainParams: params, + notifications: config.Notifications, + sigCache: config.SigCache, + indexManager: config.IndexManager, + root: nil, + bestNode: nil, + index: make(map[wire.ShaHash]*blockNode), + depNodes: make(map[wire.ShaHash][]*blockNode), + orphans: make(map[wire.ShaHash]*orphanBlock), + prevOrphans: make(map[wire.ShaHash][]*orphanBlock), + blockCache: make(map[wire.ShaHash]*btcutil.Block), + } + + // Initialize the chain state from the passed database. When the db + // does not yet contain any chain state, both it and the chain state + // will be initialized to contain only the genesis block. + if err := b.initChainState(); err != nil { + return nil, err + } + + // Initialize and catch up all of the currently active optional indexes + // as needed. + if config.IndexManager != nil { + if err := config.IndexManager.Init(&b); err != nil { + return nil, err + } + } + + log.Infof("Chain state (height %d, hash %v, totaltx %d, work %v)", + b.bestNode.height, b.bestNode.hash, b.stateSnapshot.TotalTxns, + b.bestNode.workSum) + + return &b, nil +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/chain_test.go b/vendor/github.com/btcsuite/btcd/blockchain/chain_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0cf9090ec88dd777919f77b5a02e5032a89ae438 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/chain_test.go @@ -0,0 +1,114 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain_test + +import ( + "testing" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// TestHaveBlock tests the HaveBlock API to ensure proper functionality. +func TestHaveBlock(t *testing.T) { + // Load up blocks such that there is a side chain. + // (genesis block) -> 1 -> 2 -> 3 -> 4 + // \-> 3a + testFiles := []string{ + "blk_0_to_4.dat.bz2", + "blk_3A.dat.bz2", + } + + var blocks []*btcutil.Block + for _, file := range testFiles { + blockTmp, err := loadBlocks(file) + if err != nil { + t.Errorf("Error loading file: %v\n", err) + return + } + for _, block := range blockTmp { + blocks = append(blocks, block) + } + } + + // Create a new database and chain instance to run tests against. + chain, teardownFunc, err := chainSetup("haveblock") + if err != nil { + t.Errorf("Failed to setup chain instance: %v", err) + return + } + defer teardownFunc() + + // Since we're not dealing with the real block chain, disable + // checkpoints and set the coinbase maturity to 1. + chain.DisableCheckpoints(true) + blockchain.TstSetCoinbaseMaturity(1) + + timeSource := blockchain.NewMedianTime() + for i := 1; i < len(blocks); i++ { + isOrphan, err := chain.ProcessBlock(blocks[i], timeSource, + blockchain.BFNone) + if err != nil { + t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) + return + } + if isOrphan { + t.Errorf("ProcessBlock incorrectly returned block %v "+ + "is an orphan\n", i) + return + } + } + + // Insert an orphan block. + isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000), + timeSource, blockchain.BFNone) + if err != nil { + t.Errorf("Unable to process block: %v", err) + return + } + if !isOrphan { + t.Errorf("ProcessBlock indicated block is an not orphan when " + + "it should be\n") + return + } + + tests := []struct { + hash string + want bool + }{ + // Genesis block should be present (in the main chain). + {hash: chaincfg.MainNetParams.GenesisHash.String(), want: true}, + + // Block 3a should be present (on a side chain). + {hash: "00000000474284d20067a4d33f6a02284e6ef70764a3a26d6a5b9df52ef663dd", want: true}, + + // Block 100000 should be present (as an orphan). + {hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506", want: true}, + + // Random hashes should not be available. + {hash: "123", want: false}, + } + + for i, test := range tests { + hash, err := wire.NewShaHashFromStr(test.hash) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + continue + } + + result, err := chain.HaveBlock(hash) + if err != nil { + t.Errorf("HaveBlock #%d unexpected error: %v", i, err) + return + } + if result != test.want { + t.Errorf("HaveBlock #%d got %v want %v", i, result, + test.want) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/chainio.go b/vendor/github.com/btcsuite/btcd/blockchain/chainio.go new file mode 100644 index 0000000000000000000000000000000000000000..9537f42626f7d5d3b1916e6ced1e34504a7e0a84 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/chainio.go @@ -0,0 +1,1400 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "bytes" + "encoding/binary" + "fmt" + "math/big" + "sort" + + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +var ( + // hashIndexBucketName is the name of the db bucket used to house to the + // block hash -> block height index. + hashIndexBucketName = []byte("hashidx") + + // heightIndexBucketName is the name of the db bucket used to house to + // the block height -> block hash index. + heightIndexBucketName = []byte("heightidx") + + // chainStateKeyName is the name of the db key used to store the best + // chain state. + chainStateKeyName = []byte("chainstate") + + // spendJournalBucketName is the name of the db bucket used to house + // transactions outputs that are spent in each block. + spendJournalBucketName = []byte("spendjournal") + + // utxoSetBucketName is the name of the db bucket used to house the + // unspent transaction output set. + utxoSetBucketName = []byte("utxoset") + + // byteOrder is the preferred byte order used for serializing numeric + // fields for storage in the database. + byteOrder = binary.LittleEndian +) + +// errNotInMainChain signifies that a block hash or height that is not in the +// main chain was requested. +type errNotInMainChain string + +// Error implements the error interface. +func (e errNotInMainChain) Error() string { + return string(e) +} + +// isNotInMainChainErr returns whether or not the passed error is an +// errNotInMainChain error. +func isNotInMainChainErr(err error) bool { + _, ok := err.(errNotInMainChain) + return ok +} + +// errDeserialize signifies that a problem was encountered when deserializing +// data. +type errDeserialize string + +// Error implements the error interface. +func (e errDeserialize) Error() string { + return string(e) +} + +// isDeserializeErr returns whether or not the passed error is an errDeserialize +// error. +func isDeserializeErr(err error) bool { + _, ok := err.(errDeserialize) + return ok +} + +// ----------------------------------------------------------------------------- +// The transaction spend journal consists of an entry for each block connected +// to the main chain which contains the transaction outputs the block spends +// serialized such that the order is the reverse of the order they were spent. +// +// This is required because reorganizing the chain necessarily entails +// disconnecting blocks to get back to the point of the fork which implies +// unspending all of the transaction outputs that each block previously spent. +// Since the utxo set, by definition, only contains unspent transaction outputs, +// the spent transaction outputs must be resurrected from somewhere. There is +// more than one way this could be done, however this is the most straight +// forward method that does not require having a transaction index and unpruned +// blockchain. +// +// NOTE: This format is NOT self describing. The additional details such as +// the number of entries (transaction inputs) are expected to come from the +// block itself and the utxo set. The rationale in doing this is to save a +// significant amount of space. This is also the reason the spent outputs are +// serialized in the reverse order they are spent because later transactions +// are allowed to spend outputs from earlier ones in the same block. +// +// The serialized format is: +// +// [<header code><version><compressed txout>],... +// +// Field Type Size +// header code VLQ variable +// version VLQ variable +// compressed txout +// compressed amount VLQ variable +// compressed script []byte variable +// +// The serialized header code format is: +// bit 0 - containing transaction is a coinbase +// bits 1-x - height of the block that contains the spent txout +// +// NOTE: The header code and version are only encoded when the spent txout was +// the final unspent output of the containing transaction. Otherwise, the +// header code will be 0 and the version is not serialized at all. This is +// done because that information is only needed when the utxo set no longer +// has it. +// +// Example 1: +// From block 170 in main blockchain. +// +// 1301320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c +// <><><------------------------------------------------------------------> +// | | | +// | version compressed txout +// header code +// +// - header code: 0x13 (coinbase, height 9) +// - transaction version: 1 +// - compressed txout 0: +// - 0x32: VLQ-encoded compressed amount for 5000000000 (50 BTC) +// - 0x05: special script type pay-to-pubkey +// - 0x11...5c: x-coordinate of the pubkey +// +// Example 2: +// Adapted from block 100025 in main blockchain. +// +// 0091f20f006edbc6c4d31bae9f1ccc38538a114bf42de65e868b99700186c64700b2fb57eadf61e106a100a7445a8c3f67898841ec +// <><----------------------------------------------><----><><----------------------------------------------> +// | | | | | +// | compressed txout | version compressed txout +// header code header code +// +// - Last spent output: +// - header code: 0x00 (was not the final unspent output for containing tx) +// - transaction version: Nothing since header code is 0 +// - compressed txout: +// - 0x91f20f: VLQ-encoded compressed amount for 34405000000 (344.05 BTC) +// - 0x00: special script type pay-to-pubkey-hash +// - 0x6e...86: pubkey hash +// - Second to last spent output: +// - header code: 0x8b9970 (not coinbase, height 100024) +// - transaction version: 1 +// - compressed txout: +// - 0x86c647: VLQ-encoded compressed amount for 13761000000 (137.61 BTC) +// - 0x00: special script type pay-to-pubkey-hash +// - 0xb2...ec: pubkey hash +// ----------------------------------------------------------------------------- + +// spentTxOut contains a spent transaction output and potentially additional +// contextual information such as whether or not it was contained in a coinbase +// transaction, the version of the transaction it was contained in, and which +// block height the containing transaction was included in. As described in +// the comments above, the additional contextual information will only be valid +// when this spent txout is spending the last unspent output of the containing +// transaction. +type spentTxOut struct { + compressed bool // The amount and public key script are compressed. + version int32 // The version of creating tx. + amount int64 // The amount of the output. + pkScript []byte // The public key script for the output. + + // These fields are only set when this is spending the final output of + // the creating tx. + height int32 // Height of the the block containing the creating tx. + isCoinBase bool // Whether creating tx is a coinbase. +} + +// spentTxOutHeaderCode returns the calculated header code to be used when +// serializing the provided stxo entry. +func spentTxOutHeaderCode(stxo *spentTxOut) uint64 { + // The header code is 0 when there is no height set for the stxo. + if stxo.height == 0 { + return 0 + } + + // As described in the serialization format comments, the header code + // encodes the height shifted over one bit and the coinbase flag in the + // lowest bit. + headerCode := uint64(stxo.height) << 1 + if stxo.isCoinBase { + headerCode |= 0x01 + } + + return headerCode +} + +// spentTxOutSerializeSize returns the number of bytes it would take to +// serialize the passed stxo according to the format described above. +func spentTxOutSerializeSize(stxo *spentTxOut) int { + headerCode := spentTxOutHeaderCode(stxo) + size := serializeSizeVLQ(headerCode) + if headerCode != 0 { + size += serializeSizeVLQ(uint64(stxo.version)) + } + return size + compressedTxOutSize(uint64(stxo.amount), stxo.pkScript, + stxo.version, stxo.compressed) +} + +// putSpentTxOut serializes the passed stxo according to the format described +// above directly into the passed target byte slice. The target byte slice must +// be at least large enough to handle the number of bytes returned by the +// spentTxOutSerializeSize function or it will panic. +func putSpentTxOut(target []byte, stxo *spentTxOut) int { + headerCode := spentTxOutHeaderCode(stxo) + offset := putVLQ(target, headerCode) + if headerCode != 0 { + offset += putVLQ(target[offset:], uint64(stxo.version)) + } + return offset + putCompressedTxOut(target[offset:], uint64(stxo.amount), + stxo.pkScript, stxo.version, stxo.compressed) +} + +// decodeSpentTxOut decodes the passed serialized stxo entry, possibly followed +// by other data, into the passed stxo struct. It returns the number of bytes +// read. +// +// Since the serialized stxo entry does not contain the height, version, or +// coinbase flag of the containing transaction when it still has utxos, the +// caller is responsible for passing in the containing transaction version in +// that case. The provided version is ignore when it is serialized as a part of +// the stxo. +// +// An error will be returned if the version is not serialized as a part of the +// stxo and is also not provided to the function. +func decodeSpentTxOut(serialized []byte, stxo *spentTxOut, txVersion int32) (int, error) { + // Ensure there are bytes to decode. + if len(serialized) == 0 { + return 0, errDeserialize("no serialized bytes") + } + + // Deserialize the header code. + code, offset := deserializeVLQ(serialized) + if offset >= len(serialized) { + return offset, errDeserialize("unexpected end of data after " + + "header code") + } + + // Decode the header code and deserialize the containing transaction + // version if needed. + // + // Bit 0 indicates containing transaction is a coinbase. + // Bits 1-x encode height of containing transaction. + if code != 0 { + version, bytesRead := deserializeVLQ(serialized[offset:]) + offset += bytesRead + if offset >= len(serialized) { + return offset, errDeserialize("unexpected end of data " + + "after version") + } + + stxo.isCoinBase = code&0x01 != 0 + stxo.height = int32(code >> 1) + stxo.version = int32(version) + } else { + // Ensure a tx version was specified if the stxo did not encode + // it. This should never happen unless there is database + // corruption or this function is being called without the + // proper state. + if txVersion == 0 { + return offset, AssertError("decodeSpentTxOut called " + + "without a containing tx version when the " + + "serialized stxo that does not encode the " + + "version") + } + stxo.version = txVersion + } + + // Decode the compressed txout. + compAmount, compScript, bytesRead, err := decodeCompressedTxOut( + serialized[offset:], stxo.version) + offset += bytesRead + if err != nil { + return offset, errDeserialize(fmt.Sprintf("unable to decode "+ + "txout: %v", err)) + } + stxo.amount = int64(compAmount) + stxo.pkScript = compScript + stxo.compressed = true + return offset, nil +} + +// deserializeSpendJournalEntry decodes the passed serialized byte slice into a +// slice of spent txouts according to the format described in detail above. +// +// Since the serialization format is not self describing, as noted in the +// format comments, this function also requires the transactions that spend the +// txouts and a utxo view that contains any remaining existing utxos in the +// transactions referenced by the inputs to the passed transasctions. +func deserializeSpendJournalEntry(serialized []byte, txns []*wire.MsgTx, view *UtxoViewpoint) ([]spentTxOut, error) { + // Calculate the total number of stxos. + var numStxos int + for _, tx := range txns { + numStxos += len(tx.TxIn) + } + + // When a block has no spent txouts there is nothing to serialize. + if len(serialized) == 0 { + // Ensure the block actually has no stxos. This should never + // happen unless there is database corruption or an empty entry + // erroneously made its way into the database. + if numStxos != 0 { + return nil, AssertError(fmt.Sprintf("mismatched spend "+ + "journal serialization - no serialization for "+ + "expected %d stxos", numStxos)) + } + + return nil, nil + } + + // Loop backwards through all transactions so everything is read in + // reverse order to match the serialization order. + stxoIdx := numStxos - 1 + stxoInFlight := make(map[wire.ShaHash]int) + offset := 0 + stxos := make([]spentTxOut, numStxos) + for txIdx := len(txns) - 1; txIdx > -1; txIdx-- { + tx := txns[txIdx] + + // Loop backwards through all of the transaction inputs and read + // the associated stxo. + for txInIdx := len(tx.TxIn) - 1; txInIdx > -1; txInIdx-- { + txIn := tx.TxIn[txInIdx] + stxo := &stxos[stxoIdx] + stxoIdx-- + + // Get the transaction version for the stxo based on + // whether or not it should be serialized as a part of + // the stxo. Recall that it is only serialized when the + // stxo spends the final utxo of a transaction. Since + // they are deserialized in reverse order, this means + // the first time an entry for a given containing tx is + // encountered that is not already in the utxo view it + // must have been the final spend and thus the extra + // data will be serialized with the stxo. Otherwise, + // the version must be pulled from the utxo entry. + // + // Since the view is not actually modified as the stxos + // are read here and it's possible later entries + // reference earlier ones, an inflight map is maintained + // to detect this case and pull the tx version from the + // entry that contains the version information as just + // described. + var txVersion int32 + originHash := &txIn.PreviousOutPoint.Hash + entry := view.LookupEntry(originHash) + if entry != nil { + txVersion = entry.Version() + } else if idx, ok := stxoInFlight[*originHash]; ok { + txVersion = stxos[idx].version + } else { + stxoInFlight[*originHash] = stxoIdx + 1 + } + + n, err := decodeSpentTxOut(serialized[offset:], stxo, + txVersion) + offset += n + if err != nil { + return nil, errDeserialize(fmt.Sprintf("unable "+ + "to decode stxo for %v: %v", + txIn.PreviousOutPoint, err)) + } + } + } + + return stxos, nil +} + +// serializeSpendJournalEntry serializes all of the passed spent txouts into a +// single byte slice according to the format described in detail above. +func serializeSpendJournalEntry(stxos []spentTxOut) []byte { + if len(stxos) == 0 { + return nil + } + + // Calculate the size needed to serialize the entire journal entry. + var size int + for i := range stxos { + size += spentTxOutSerializeSize(&stxos[i]) + } + serialized := make([]byte, size) + + // Serialize each individual stxo directly into the slice in reverse + // order one after the other. + var offset int + for i := len(stxos) - 1; i > -1; i-- { + offset += putSpentTxOut(serialized[offset:], &stxos[i]) + } + + return serialized +} + +// dbFetchSpendJournalEntry fetches the spend journal entry for the passed +// block and deserializes it into a slice of spent txout entries. The provided +// view MUST have the utxos referenced by all of the transactions available for +// the passed block since that information is required to reconstruct the spent +// txouts. +func dbFetchSpendJournalEntry(dbTx database.Tx, block *btcutil.Block, view *UtxoViewpoint) ([]spentTxOut, error) { + // Exclude the coinbase transaction since it can't spend anything. + spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName) + serialized := spendBucket.Get(block.Sha()[:]) + blockTxns := block.MsgBlock().Transactions[1:] + stxos, err := deserializeSpendJournalEntry(serialized, blockTxns, view) + if err != nil { + // Ensure any deserialization errors are returned as database + // corruption errors. + if isDeserializeErr(err) { + return nil, database.Error{ + ErrorCode: database.ErrCorruption, + Description: fmt.Sprintf("corrupt spend "+ + "information for %v: %v", block.Sha(), + err), + } + } + + return nil, err + } + + return stxos, nil +} + +// dbPutSpendJournalEntry uses an existing database transaction to update the +// spend journal entry for the given block hash using the provided slice of +// spent txouts. The spent txouts slice must contain an entry for every txout +// the transactions in the block spend in the order they are spent. +func dbPutSpendJournalEntry(dbTx database.Tx, blockHash *wire.ShaHash, stxos []spentTxOut) error { + spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName) + serialized := serializeSpendJournalEntry(stxos) + return spendBucket.Put(blockHash[:], serialized) +} + +// dbRemoveSpendJournalEntry uses an existing database transaction to remove the +// spend journal entry for the passed block hash. +func dbRemoveSpendJournalEntry(dbTx database.Tx, blockHash *wire.ShaHash) error { + spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName) + return spendBucket.Delete(blockHash[:]) +} + +// ----------------------------------------------------------------------------- +// The unspent transaction output (utxo) set consists of an entry for each +// transaction which contains a utxo serialized using a format that is highly +// optimized to reduce space using domain specific compression algorithms. This +// format is a slightly modified version of the format used in Bitcoin Core. +// +// The serialized format is: +// +// <version><height><header code><unspentness bitmap>[<compressed txouts>,...] +// +// Field Type Size +// version VLQ variable +// block height VLQ variable +// header code VLQ variable +// unspentness bitmap []byte variable +// compressed txouts +// compressed amount VLQ variable +// compressed script []byte variable +// +// The serialized header code format is: +// bit 0 - containing transaction is a coinbase +// bit 1 - output zero is unspent +// bit 2 - output one is unspent +// bits 3-x - number of bytes in unspentness bitmap. When both bits 1 and 2 +// are unset, it encodes N-1 since there must be at least one unspent +// output. +// +// The rationale for the header code scheme is as follows: +// - Transactions which only pay to a single output and a change output are +// extremely common, thus an extra byte for the unspentness bitmap can be +// avoided for them by encoding those two outputs in the low order bits. +// - Given it is encoded as a VLQ which can encode values up to 127 with a +// single byte, that leaves 4 bits to represent the number of bytes in the +// unspentness bitmap while still only consuming a single byte for the +// header code. In other words, an unspentness bitmap with up to 120 +// transaction outputs can be encoded with a single-byte header code. +// This covers the vast majority of transactions. +// - Encoding N-1 bytes when both bits 1 and 2 are unset allows an additional +// 8 outpoints to be encoded before causing the header code to require an +// additional byte. +// +// Example 1: +// From tx in main blockchain: +// Blk 1, 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098 +// +// 010103320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52 +// <><><><------------------------------------------------------------------> +// | | \--------\ | +// | height | compressed txout 0 +// version header code +// +// - version: 1 +// - height: 1 +// - header code: 0x03 (coinbase, output zero unspent, 0 bytes of unspentness) +// - unspentness: Nothing since it is zero bytes +// - compressed txout 0: +// - 0x32: VLQ-encoded compressed amount for 5000000000 (50 BTC) +// - 0x04: special script type pay-to-pubkey +// - 0x96...52: x-coordinate of the pubkey +// +// Example 2: +// From tx in main blockchain: +// Blk 113931, 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f +// +// 0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58 +// <><----><><><------------------------------------------><--------------------------------------------> +// | | | \-------------------\ | | +// version | \--------\ unspentness | compressed txout 2 +// height header code compressed txout 0 +// +// - version: 1 +// - height: 113931 +// - header code: 0x0a (output zero unspent, 1 byte in unspentness bitmap) +// - unspentness: [0x01] (bit 0 is set, so output 0+2 = 2 is unspent) +// NOTE: It's +2 since the first two outputs are encoded in the header code +// - compressed txout 0: +// - 0x12: VLQ-encoded compressed amount for 20000000 (0.2 BTC) +// - 0x00: special script type pay-to-pubkey-hash +// - 0xe2...8a: pubkey hash +// - compressed txout 2: +// - 0x8009: VLQ-encoded compressed amount for 15000000 (0.15 BTC) +// - 0x00: special script type pay-to-pubkey-hash +// - 0xb8...58: pubkey hash +// +// Example 3: +// From tx in main blockchain: +// Blk 338156, 1b02d1c8cfef60a189017b9a420c682cf4a0028175f2f563209e4ff61c8c3620 +// +// 0193d06c100000108ba5b9e763011dd46a006572d820e448e12d2bbb38640bc718e6 +// <><----><><----><--------------------------------------------------> +// | | | \-----------------\ | +// version | \--------\ unspentness | +// height header code compressed txout 22 +// +// - version: 1 +// - height: 338156 +// - header code: 0x10 (2+1 = 3 bytes in unspentness bitmap) +// NOTE: It's +1 since neither bit 1 nor 2 are set, so N-1 is encoded. +// - unspentness: [0x00 0x00 0x10] (bit 20 is set, so output 20+2 = 22 is unspent) +// NOTE: It's +2 since the first two outputs are encoded in the header code +// - compressed txout 22: +// - 0x8ba5b9e763: VLQ-encoded compressed amount for 366875659 (3.66875659 BTC) +// - 0x01: special script type pay-to-script-hash +// - 0x1d...e6: script hash +// ----------------------------------------------------------------------------- + +// utxoEntryHeaderCode returns the calculated header code to be used when +// serializing the provided utxo entry and the number of bytes needed to encode +// the unspentness bitmap. +func utxoEntryHeaderCode(entry *UtxoEntry, highestOutputIndex uint32) (uint64, int, error) { + // The first two outputs are encoded separately, so offset the index + // accordingly to calculate the correct number of bytes needed to encode + // up to the highest unspent output index. + numBitmapBytes := int((highestOutputIndex + 6) / 8) + + // As previously described, one less than the number of bytes is encoded + // when both output 0 and 1 are spent because there must be at least one + // unspent output. Adjust the number of bytes to encode accordingly and + // encode the value by shifting it over 3 bits. + output0Unspent := !entry.IsOutputSpent(0) + output1Unspent := !entry.IsOutputSpent(1) + var numBitmapBytesAdjustment int + if !output0Unspent && !output1Unspent { + if numBitmapBytes == 0 { + return 0, 0, AssertError("attempt to serialize utxo " + + "header for fully spent transaction") + } + numBitmapBytesAdjustment = 1 + } + headerCode := uint64(numBitmapBytes-numBitmapBytesAdjustment) << 3 + + // Set the coinbase, output 0, and output 1 bits in the header code + // accordingly. + if entry.isCoinBase { + headerCode |= 0x01 // bit 0 + } + if output0Unspent { + headerCode |= 0x02 // bit 1 + } + if output1Unspent { + headerCode |= 0x04 // bit 2 + } + + return headerCode, numBitmapBytes, nil +} + +// serializeUtxoEntry returns the entry serialized to a format that is suitable +// for long-term storage. The format is described in detail above. +func serializeUtxoEntry(entry *UtxoEntry) ([]byte, error) { + // Fully spent entries have no serialization. + if entry.IsFullySpent() { + return nil, nil + } + + // Determine the output order by sorting the sparse output index keys. + outputOrder := make([]int, 0, len(entry.sparseOutputs)) + for outputIndex := range entry.sparseOutputs { + outputOrder = append(outputOrder, int(outputIndex)) + } + sort.Ints(outputOrder) + + // Encode the header code and determine the number of bytes the + // unspentness bitmap needs. + highIndex := uint32(outputOrder[len(outputOrder)-1]) + headerCode, numBitmapBytes, err := utxoEntryHeaderCode(entry, highIndex) + if err != nil { + return nil, err + } + + // Calculate the size needed to serialize the entry. + size := serializeSizeVLQ(uint64(entry.version)) + + serializeSizeVLQ(uint64(entry.blockHeight)) + + serializeSizeVLQ(headerCode) + numBitmapBytes + for _, outputIndex := range outputOrder { + out := entry.sparseOutputs[uint32(outputIndex)] + if out.spent { + continue + } + size += compressedTxOutSize(uint64(out.amount), out.pkScript, + entry.version, out.compressed) + } + + // Serialize the version, block height of the containing transaction, + // and header code. + serialized := make([]byte, size) + offset := putVLQ(serialized, uint64(entry.version)) + offset += putVLQ(serialized[offset:], uint64(entry.blockHeight)) + offset += putVLQ(serialized[offset:], headerCode) + + // Serialize the unspentness bitmap. + for i := uint32(0); i < uint32(numBitmapBytes); i++ { + unspentBits := byte(0) + for j := uint32(0); j < 8; j++ { + // The first 2 outputs are encoded via the header code, + // so adjust the output index accordingly. + if !entry.IsOutputSpent(2 + i*8 + j) { + unspentBits |= 1 << uint8(j) + } + } + serialized[offset] = unspentBits + offset++ + } + + // Serialize the compressed unspent transaction outputs. Outputs that + // are already compressed are serialized without modifications. + for _, outputIndex := range outputOrder { + out := entry.sparseOutputs[uint32(outputIndex)] + if out.spent { + continue + } + + offset += putCompressedTxOut(serialized[offset:], + uint64(out.amount), out.pkScript, entry.version, + out.compressed) + } + + return serialized, nil +} + +// deserializeUtxoEntry decodes a utxo entry from the passed serialized byte +// slice into a new UtxoEntry using a format that is suitable for long-term +// storage. The format is described in detail above. +func deserializeUtxoEntry(serialized []byte) (*UtxoEntry, error) { + // Deserialize the version. + version, bytesRead := deserializeVLQ(serialized) + offset := bytesRead + if offset >= len(serialized) { + return nil, errDeserialize("unexpected end of data after version") + } + + // Deserialize the block height. + blockHeight, bytesRead := deserializeVLQ(serialized[offset:]) + offset += bytesRead + if offset >= len(serialized) { + return nil, errDeserialize("unexpected end of data after height") + } + + // Deserialize the header code. + code, bytesRead := deserializeVLQ(serialized[offset:]) + offset += bytesRead + if offset >= len(serialized) { + return nil, errDeserialize("unexpected end of data after header") + } + + // Decode the header code. + // + // Bit 0 indicates whether the containing transaction is a coinbase. + // Bit 1 indicates output 0 is unspent. + // Bit 2 indicates output 1 is unspent. + // Bits 3-x encodes the number of non-zero unspentness bitmap bytes that + // follow. When both output 0 and 1 are spent, it encodes N-1. + isCoinBase := code&0x01 != 0 + output0Unspent := code&0x02 != 0 + output1Unspent := code&0x04 != 0 + numBitmapBytes := code >> 3 + if !output0Unspent && !output1Unspent { + numBitmapBytes++ + } + + // Ensure there are enough bytes left to deserialize the unspentness + // bitmap. + if uint64(len(serialized[offset:])) < numBitmapBytes { + return nil, errDeserialize("unexpected end of data for " + + "unspentness bitmap") + } + + // Create a new utxo entry with the details deserialized above to house + // all of the utxos. + entry := newUtxoEntry(int32(version), isCoinBase, int32(blockHeight)) + + // Add sparse output for unspent outputs 0 and 1 as needed based on the + // details provided by the header code. + var outputIndexes []uint32 + if output0Unspent { + outputIndexes = append(outputIndexes, 0) + } + if output1Unspent { + outputIndexes = append(outputIndexes, 1) + } + + // Decode the unspentness bitmap adding a sparse output for each unspent + // output. + for i := uint32(0); i < uint32(numBitmapBytes); i++ { + unspentBits := serialized[offset] + for j := uint32(0); j < 8; j++ { + if unspentBits&0x01 != 0 { + // The first 2 outputs are encoded via the + // header code, so adjust the output number + // accordingly. + outputNum := 2 + i*8 + j + outputIndexes = append(outputIndexes, outputNum) + } + unspentBits >>= 1 + } + offset++ + } + + // Decode and add all of the utxos. + for i, outputIndex := range outputIndexes { + // Decode the next utxo. The script and amount fields of the + // utxo output are left compressed so decompression can be + // avoided on those that are not accessed. This is done since + // it is quite common for a redeeming transaction to only + // reference a single utxo from a referenced transaction. + compAmount, compScript, bytesRead, err := decodeCompressedTxOut( + serialized[offset:], int32(version)) + if err != nil { + return nil, errDeserialize(fmt.Sprintf("unable to "+ + "decode utxo at index %d: %v", i, err)) + } + offset += bytesRead + + entry.sparseOutputs[outputIndex] = &utxoOutput{ + spent: false, + compressed: true, + pkScript: compScript, + amount: int64(compAmount), + } + } + + return entry, nil +} + +// dbFetchUtxoEntry uses an existing database transaction to fetch all unspent +// outputs for the provided Bitcoin transaction hash from the utxo set. +// +// When there is no entry for the provided hash, nil will be returned for the +// both the entry and the error. +func dbFetchUtxoEntry(dbTx database.Tx, hash *wire.ShaHash) (*UtxoEntry, error) { + // Fetch the unspent transaction output information for the passed + // transaction hash. Return now when there is no entry. + utxoBucket := dbTx.Metadata().Bucket(utxoSetBucketName) + serializedUtxo := utxoBucket.Get(hash[:]) + if serializedUtxo == nil { + return nil, nil + } + + // A non-nil zero-length entry means there is an entry in the database + // for a fully spent transaction which should never be the case. + if len(serializedUtxo) == 0 { + return nil, AssertError(fmt.Sprintf("database contains entry "+ + "for fully spent tx %v", hash)) + } + + // Deserialize the utxo entry and return it. + entry, err := deserializeUtxoEntry(serializedUtxo) + if err != nil { + // Ensure any deserialization errors are returned as database + // corruption errors. + if isDeserializeErr(err) { + return nil, database.Error{ + ErrorCode: database.ErrCorruption, + Description: fmt.Sprintf("corrupt utxo entry "+ + "for %v: %v", hash, err), + } + } + + return nil, err + } + + return entry, nil +} + +// dbPutUtxoView uses an existing database transaction to update the utxo set +// in the database based on the provided utxo view contents and state. In +// particular, only the entries that have been marked as modified are written +// to the database. +func dbPutUtxoView(dbTx database.Tx, view *UtxoViewpoint) error { + utxoBucket := dbTx.Metadata().Bucket(utxoSetBucketName) + for txHashIter, entry := range view.entries { + // No need to update the database if the entry was not modified. + if entry == nil || !entry.modified { + continue + } + + // Serialize the utxo entry without any entries that have been + // spent. + serialized, err := serializeUtxoEntry(entry) + if err != nil { + return err + } + + // Make a copy of the hash because the iterator changes on each + // loop iteration and thus slicing it directly would cause the + // data to change out from under the put/delete funcs below. + txHash := txHashIter + + // Remove the utxo entry if it is now fully spent. + if serialized == nil { + if err := utxoBucket.Delete(txHash[:]); err != nil { + return err + } + + continue + } + + // At this point the utxo entry is not fully spent, so store its + // serialization in the database. + err = utxoBucket.Put(txHash[:], serialized) + if err != nil { + return err + } + } + + return nil +} + +// ----------------------------------------------------------------------------- +// The block index consists of two buckets with an entry for every block in the +// main chain. One bucket is for the hash to height mapping and the other is +// for the height to hash mapping. +// +// The serialized format for values in the hash to height bucket is: +// <height> +// +// Field Type Size +// height uint32 4 bytes +// +// The serialized format for values in the height to hash bucket is: +// <hash> +// +// Field Type Size +// hash wire.ShaHash wire.HashSize +// ----------------------------------------------------------------------------- + +// dbPutBlockIndex uses an existing database transaction to update or add the +// block index entries for the hash to height and height to hash mappings for +// the provided values. +func dbPutBlockIndex(dbTx database.Tx, hash *wire.ShaHash, height int32) error { + // Serialize the height for use in the index entries. + var serializedHeight [4]byte + byteOrder.PutUint32(serializedHeight[:], uint32(height)) + + // Add the block hash to height mapping to the index. + meta := dbTx.Metadata() + hashIndex := meta.Bucket(hashIndexBucketName) + if err := hashIndex.Put(hash[:], serializedHeight[:]); err != nil { + return err + } + + // Add the block height to hash mapping to the index. + heightIndex := meta.Bucket(heightIndexBucketName) + return heightIndex.Put(serializedHeight[:], hash[:]) +} + +// dbRemoveBlockIndex uses an existing database transaction remove block index +// entries from the hash to height and height to hash mappings for the provided +// values. +func dbRemoveBlockIndex(dbTx database.Tx, hash *wire.ShaHash, height int32) error { + // Remove the block hash to height mapping. + meta := dbTx.Metadata() + hashIndex := meta.Bucket(hashIndexBucketName) + if err := hashIndex.Delete(hash[:]); err != nil { + return err + } + + // Remove the block height to hash mapping. + var serializedHeight [4]byte + byteOrder.PutUint32(serializedHeight[:], uint32(height)) + heightIndex := meta.Bucket(heightIndexBucketName) + return heightIndex.Delete(serializedHeight[:]) +} + +// dbFetchHeightByHash uses an existing database transaction to retrieve the +// height for the provided hash from the index. +func dbFetchHeightByHash(dbTx database.Tx, hash *wire.ShaHash) (int32, error) { + meta := dbTx.Metadata() + hashIndex := meta.Bucket(hashIndexBucketName) + serializedHeight := hashIndex.Get(hash[:]) + if serializedHeight == nil { + str := fmt.Sprintf("block %s is not in the main chain", hash) + return 0, errNotInMainChain(str) + } + + return int32(byteOrder.Uint32(serializedHeight)), nil +} + +// dbFetchHashByHeight uses an existing database transaction to retrieve the +// hash for the provided height from the index. +func dbFetchHashByHeight(dbTx database.Tx, height int32) (*wire.ShaHash, error) { + var serializedHeight [4]byte + byteOrder.PutUint32(serializedHeight[:], uint32(height)) + + meta := dbTx.Metadata() + heightIndex := meta.Bucket(heightIndexBucketName) + hashBytes := heightIndex.Get(serializedHeight[:]) + if hashBytes == nil { + str := fmt.Sprintf("no block at height %d exists", height) + return nil, errNotInMainChain(str) + } + + var hash wire.ShaHash + copy(hash[:], hashBytes) + return &hash, nil +} + +// ----------------------------------------------------------------------------- +// The best chain state consists of the best block hash and height, the total +// number of transactions up to and including those in the best block, and the +// accumulated work sum up to and including the best block. +// +// The serialized format is: +// +// <block hash><block height><total txns><work sum length><work sum> +// +// Field Type Size +// block hash wire.ShaHash wire.HashSize +// block height uint32 4 bytes +// total txns uint64 8 bytes +// work sum length uint32 4 bytes +// work sum big.Int work sum length +// ----------------------------------------------------------------------------- + +// bestChainState represents the data to be stored the database for the current +// best chain state. +type bestChainState struct { + hash wire.ShaHash + height uint32 + totalTxns uint64 + workSum *big.Int +} + +// serializeBestChainState returns the serialization of the passed block best +// chain state. This is data to be stored in the chain state bucket. +func serializeBestChainState(state bestChainState) []byte { + // Calculate the full size needed to serialize the chain state. + workSumBytes := state.workSum.Bytes() + workSumBytesLen := uint32(len(workSumBytes)) + serializedLen := wire.HashSize + 4 + 8 + 4 + workSumBytesLen + + // Serialize the chain state. + serializedData := make([]byte, serializedLen) + copy(serializedData[0:wire.HashSize], state.hash[:]) + offset := uint32(wire.HashSize) + byteOrder.PutUint32(serializedData[offset:], state.height) + offset += 4 + byteOrder.PutUint64(serializedData[offset:], state.totalTxns) + offset += 8 + byteOrder.PutUint32(serializedData[offset:], workSumBytesLen) + offset += 4 + copy(serializedData[offset:], workSumBytes) + return serializedData[:] +} + +// deserializeBestChainState deserializes the passed serialized best chain +// state. This is data stored in the chain state bucket and is updated after +// every block is connected or disconnected form the main chain. +// block. +func deserializeBestChainState(serializedData []byte) (bestChainState, error) { + // Ensure the serialized data has enough bytes to properly deserialize + // the hash, height, total transactions, and work sum length. + if len(serializedData) < wire.HashSize+16 { + return bestChainState{}, database.Error{ + ErrorCode: database.ErrCorruption, + Description: "corrupt best chain state", + } + } + + state := bestChainState{} + copy(state.hash[:], serializedData[0:wire.HashSize]) + offset := uint32(wire.HashSize) + state.height = byteOrder.Uint32(serializedData[offset : offset+4]) + offset += 4 + state.totalTxns = byteOrder.Uint64(serializedData[offset : offset+8]) + offset += 8 + workSumBytesLen := byteOrder.Uint32(serializedData[offset : offset+4]) + offset += 4 + + // Ensure the serialized data has enough bytes to deserialize the work + // sum. + if uint32(len(serializedData[offset:])) < workSumBytesLen { + return bestChainState{}, database.Error{ + ErrorCode: database.ErrCorruption, + Description: "corrupt best chain state", + } + } + workSumBytes := serializedData[offset : offset+workSumBytesLen] + state.workSum = new(big.Int).SetBytes(workSumBytes) + + return state, nil +} + +// dbPutBestState uses an existing database transaction to update the best chain +// state with the given parameters. +func dbPutBestState(dbTx database.Tx, snapshot *BestState, workSum *big.Int) error { + // Serialize the current best chain state. + serializedData := serializeBestChainState(bestChainState{ + hash: *snapshot.Hash, + height: uint32(snapshot.Height), + totalTxns: snapshot.TotalTxns, + workSum: workSum, + }) + + // Store the current best chain state into the database. + return dbTx.Metadata().Put(chainStateKeyName, serializedData) +} + +// createChainState initializes both the database and the chain state to the +// genesis block. This includes creating the necessary buckets and inserting +// the genesis block, so it must only be called on an uninitialized database. +func (b *BlockChain) createChainState() error { + // Create a new node from the genesis block and set it as both the root + // node and the best node. + genesisBlock := btcutil.NewBlock(b.chainParams.GenesisBlock) + header := &genesisBlock.MsgBlock().Header + node := newBlockNode(header, genesisBlock.Sha(), 0) + node.inMainChain = true + b.bestNode = node + b.root = node + + // Add the new node to the index which is used for faster lookups. + b.index[*node.hash] = node + + // Initialize the state related to the best block. + numTxns := uint64(len(genesisBlock.MsgBlock().Transactions)) + blockSize := uint64(genesisBlock.MsgBlock().SerializeSize()) + b.stateSnapshot = newBestState(b.bestNode, blockSize, numTxns, numTxns) + + // Create the initial the database chain state including creating the + // necessary index buckets and inserting the genesis block. + err := b.db.Update(func(dbTx database.Tx) error { + // Create the bucket that houses the chain block hash to height + // index. + meta := dbTx.Metadata() + _, err := meta.CreateBucket(hashIndexBucketName) + if err != nil { + return err + } + + // Create the bucket that houses the chain block height to hash + // index. + _, err = meta.CreateBucket(heightIndexBucketName) + if err != nil { + return err + } + + // Create the bucket that houses the spend journal data. + _, err = meta.CreateBucket(spendJournalBucketName) + if err != nil { + return err + } + + // Create the bucket that houses the utxo set. Note that the + // genesis block coinbase transaction is intentionally not + // inserted here since it is not spendable by consensus rules. + _, err = meta.CreateBucket(utxoSetBucketName) + if err != nil { + return err + } + + // Add the genesis block hash to height and height to hash + // mappings to the index. + err = dbPutBlockIndex(dbTx, b.bestNode.hash, b.bestNode.height) + if err != nil { + return err + } + + // Store the current best chain state into the database. + err = dbPutBestState(dbTx, b.stateSnapshot, b.bestNode.workSum) + if err != nil { + return err + } + + // Store the genesis block into the database. + return dbTx.StoreBlock(genesisBlock) + }) + return err +} + +// initChainState attempts to load and initialize the chain state from the +// database. When the db does not yet contain any chain state, both it and the +// chain state are initialized to the genesis block. +func (b *BlockChain) initChainState() error { + // Attempt to load the chain state from the database. + var isStateInitialized bool + err := b.db.View(func(dbTx database.Tx) error { + // Fetch the stored chain state from the database metadata. + // When it doesn't exist, it means the database hasn't been + // initialized for use with chain yet, so break out now to allow + // that to happen under a writable database transaction. + serializedData := dbTx.Metadata().Get(chainStateKeyName) + if serializedData == nil { + return nil + } + log.Tracef("Serialized chain state: %x", serializedData) + state, err := deserializeBestChainState(serializedData) + if err != nil { + return err + } + + // Load the raw block bytes for the best block. + blockBytes, err := dbTx.FetchBlock(&state.hash) + if err != nil { + return err + } + var block wire.MsgBlock + err = block.Deserialize(bytes.NewReader(blockBytes)) + if err != nil { + return err + } + + // Create a new node and set it as both the root node and the + // best node. The preceding nodes will be loaded on demand as + // needed. + header := &block.Header + node := newBlockNode(header, &state.hash, int32(state.height)) + node.inMainChain = true + node.workSum = state.workSum + b.bestNode = node + b.root = node + + // Add the new node to the indices for faster lookups. + prevHash := node.parentHash + b.index[*node.hash] = node + b.depNodes[*prevHash] = append(b.depNodes[*prevHash], node) + + // Initialize the state related to the best block. + blockSize := uint64(len(blockBytes)) + numTxns := uint64(len(block.Transactions)) + b.stateSnapshot = newBestState(b.bestNode, blockSize, numTxns, + state.totalTxns) + + isStateInitialized = true + return nil + }) + if err != nil { + return err + } + + // There is nothing more to do if the chain state was initialized. + if isStateInitialized { + return nil + } + + // At this point the database has not already been initialized, so + // initialize both it and the chain state to the genesis block. + return b.createChainState() +} + +// dbFetchHeaderByHash uses an existing database transaction to retrieve the +// block header for the provided hash. +func dbFetchHeaderByHash(dbTx database.Tx, hash *wire.ShaHash) (*wire.BlockHeader, error) { + headerBytes, err := dbTx.FetchBlockHeader(hash) + if err != nil { + return nil, err + } + + var header wire.BlockHeader + err = header.Deserialize(bytes.NewReader(headerBytes)) + if err != nil { + return nil, err + } + + return &header, nil +} + +// dbFetchHeaderByHeight uses an existing database transaction to retrieve the +// block header for the provided height. +func dbFetchHeaderByHeight(dbTx database.Tx, height int32) (*wire.BlockHeader, error) { + hash, err := dbFetchHashByHeight(dbTx, height) + if err != nil { + return nil, err + } + + return dbFetchHeaderByHash(dbTx, hash) +} + +// dbFetchBlockByHash uses an existing database transaction to retrieve the raw +// block for the provided hash, deserialize it, retrieve the appropriate height +// from the index, and return a btcutil.Block with the height set. +func dbFetchBlockByHash(dbTx database.Tx, hash *wire.ShaHash) (*btcutil.Block, error) { + // First find the height associated with the provided hash in the index. + blockHeight, err := dbFetchHeightByHash(dbTx, hash) + if err != nil { + return nil, err + } + + // Load the raw block bytes from the database. + blockBytes, err := dbTx.FetchBlock(hash) + if err != nil { + return nil, err + } + + // Create the encapsulated block and set the height appropriately. + block, err := btcutil.NewBlockFromBytes(blockBytes) + if err != nil { + return nil, err + } + block.SetHeight(blockHeight) + + return block, nil +} + +// dbFetchBlockByHeight uses an existing database transaction to retrieve the +// raw block for the provided height, deserialize it, and return a btcutil.Block +// with the height set. +func dbFetchBlockByHeight(dbTx database.Tx, height int32) (*btcutil.Block, error) { + // First find the hash associated with the provided height in the index. + hash, err := dbFetchHashByHeight(dbTx, height) + if err != nil { + return nil, err + } + + // Load the raw block bytes from the database. + blockBytes, err := dbTx.FetchBlock(hash) + if err != nil { + return nil, err + } + + // Create the encapsulated block and set the height appropriately. + block, err := btcutil.NewBlockFromBytes(blockBytes) + if err != nil { + return nil, err + } + block.SetHeight(height) + + return block, nil +} + +// dbMainChainHasBlock uses an existing database transaction to return whether +// or not the main chain contains the block identified by the provided hash. +func dbMainChainHasBlock(dbTx database.Tx, hash *wire.ShaHash) bool { + hashIndex := dbTx.Metadata().Bucket(hashIndexBucketName) + return hashIndex.Get(hash[:]) != nil +} + +// MainChainHasBlock returns whether or not the block with the given hash is in +// the main chain. +// +// This function is safe for concurrent access. +func (b *BlockChain) MainChainHasBlock(hash *wire.ShaHash) (bool, error) { + var exists bool + err := b.db.View(func(dbTx database.Tx) error { + exists = dbMainChainHasBlock(dbTx, hash) + return nil + }) + return exists, err +} + +// BlockHeightByHash returns the height of the block with the given hash in the +// main chain. +// +// This function is safe for concurrent access. +func (b *BlockChain) BlockHeightByHash(hash *wire.ShaHash) (int32, error) { + var height int32 + err := b.db.View(func(dbTx database.Tx) error { + var err error + height, err = dbFetchHeightByHash(dbTx, hash) + return err + }) + return height, err +} + +// BlockHashByHeight returns the hash of the block at the given height in the +// main chain. +// +// This function is safe for concurrent access. +func (b *BlockChain) BlockHashByHeight(blockHeight int32) (*wire.ShaHash, error) { + var hash *wire.ShaHash + err := b.db.View(func(dbTx database.Tx) error { + var err error + hash, err = dbFetchHashByHeight(dbTx, blockHeight) + return err + }) + return hash, err +} + +// BlockByHeight returns the block at the given height in the main chain. +// +// This function is safe for concurrent access. +func (b *BlockChain) BlockByHeight(blockHeight int32) (*btcutil.Block, error) { + var block *btcutil.Block + err := b.db.View(func(dbTx database.Tx) error { + var err error + block, err = dbFetchBlockByHeight(dbTx, blockHeight) + return err + }) + return block, err +} + +// BlockByHash returns the block from the main chain with the given hash with +// the appropriate chain height set. +// +// This function is safe for concurrent access. +func (b *BlockChain) BlockByHash(hash *wire.ShaHash) (*btcutil.Block, error) { + var block *btcutil.Block + err := b.db.View(func(dbTx database.Tx) error { + var err error + block, err = dbFetchBlockByHash(dbTx, hash) + return err + }) + return block, err +} + +// HeightRange returns a range of block hashes for the given start and end +// heights. It is inclusive of the start height and exclusive of the end +// height. The end height will be limited to the current main chain height. +// +// This function is safe for concurrent access. +func (b *BlockChain) HeightRange(startHeight, endHeight int32) ([]wire.ShaHash, error) { + // Ensure requested heights are sane. + if startHeight < 0 { + return nil, fmt.Errorf("start height of fetch range must not "+ + "be less than zero - got %d", startHeight) + } + if endHeight < startHeight { + return nil, fmt.Errorf("end height of fetch range must not "+ + "be less than the start height - got start %d, end %d", + startHeight, endHeight) + } + + // There is nothing to do when the start and end heights are the same, + // so return now to avoid the chain lock and a database transaction. + if startHeight == endHeight { + return nil, nil + } + + // Grab a lock on the chain to prevent it from changing due to a reorg + // while building the hashes. + b.chainLock.RLock() + defer b.chainLock.RUnlock() + + // When the requested start height is after the most recent best chain + // height, there is nothing to do. + latestHeight := b.bestNode.height + if startHeight > latestHeight { + return nil, nil + } + + // Limit the ending height to the latest height of the chain. + if endHeight > latestHeight+1 { + endHeight = latestHeight + 1 + } + + // Fetch as many as are available within the specified range. + var hashList []wire.ShaHash + err := b.db.View(func(dbTx database.Tx) error { + hashes := make([]wire.ShaHash, 0, endHeight-startHeight) + for i := startHeight; i < endHeight; i++ { + hash, err := dbFetchHashByHeight(dbTx, i) + if err != nil { + return err + } + hashes = append(hashes, *hash) + } + + // Set the list to be returned to the constructed list. + hashList = hashes + return nil + }) + return hashList, err +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/chainio_test.go b/vendor/github.com/btcsuite/btcd/blockchain/chainio_test.go new file mode 100644 index 0000000000000000000000000000000000000000..743996474aa3c62684da7457a15b451f7a84ba6b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/chainio_test.go @@ -0,0 +1,1030 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "bytes" + "errors" + "math/big" + "reflect" + "testing" + + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" +) + +// TestErrNotInMainChain ensures the functions related to errNotInMainChain work +// as expected. +func TestErrNotInMainChain(t *testing.T) { + errStr := "no block at height 1 exists" + err := error(errNotInMainChain(errStr)) + + // Ensure the stringized output for the error is as expected. + if err.Error() != errStr { + t.Fatalf("errNotInMainChain retuned unexpected error string - "+ + "got %q, want %q", err.Error(), errStr) + } + + // Ensure error is detected as the correct type. + if !isNotInMainChainErr(err) { + t.Fatalf("isNotInMainChainErr did not detect as expected type") + } + err = errors.New("something else") + if isNotInMainChainErr(err) { + t.Fatalf("isNotInMainChainErr detected incorrect type") + } +} + +// maybeDecompress decompresses the amount and public key script fields of the +// stxo and marks it decompressed if needed. +func (o *spentTxOut) maybeDecompress(version int32) { + // Nothing to do if it's not compressed. + if !o.compressed { + return + } + + o.amount = int64(decompressTxOutAmount(uint64(o.amount))) + o.pkScript = decompressScript(o.pkScript, version) + o.compressed = false +} + +// TestStxoSerialization ensures serializing and deserializing spent transaction +// output entries works as expected. +func TestStxoSerialization(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + stxo spentTxOut + txVersion int32 // When the txout is not fully spent. + serialized []byte + }{ + // From block 170 in main blockchain. + { + name: "Spends last output of coinbase", + stxo: spentTxOut{ + amount: 5000000000, + pkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"), + isCoinBase: true, + height: 9, + version: 1, + }, + serialized: hexToBytes("1301320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"), + }, + // Adapted from block 100025 in main blockchain. + { + name: "Spends last output of non coinbase", + stxo: spentTxOut{ + amount: 13761000000, + pkScript: hexToBytes("76a914b2fb57eadf61e106a100a7445a8c3f67898841ec88ac"), + isCoinBase: false, + height: 100024, + version: 1, + }, + serialized: hexToBytes("8b99700186c64700b2fb57eadf61e106a100a7445a8c3f67898841ec"), + }, + // Adapted from block 100025 in main blockchain. + { + name: "Does not spend last output", + stxo: spentTxOut{ + amount: 34405000000, + pkScript: hexToBytes("76a9146edbc6c4d31bae9f1ccc38538a114bf42de65e8688ac"), + version: 1, + }, + txVersion: 1, + serialized: hexToBytes("0091f20f006edbc6c4d31bae9f1ccc38538a114bf42de65e86"), + }, + } + + for _, test := range tests { + // Ensure the function to calculate the serialized size without + // actually serializing it is calculated properly. + gotSize := spentTxOutSerializeSize(&test.stxo) + if gotSize != len(test.serialized) { + t.Errorf("spentTxOutSerializeSize (%s): did not get "+ + "expected size - got %d, want %d", test.name, + gotSize, len(test.serialized)) + continue + } + + // Ensure the stxo serializes to the expected value. + gotSerialized := make([]byte, gotSize) + gotBytesWritten := putSpentTxOut(gotSerialized, &test.stxo) + if !bytes.Equal(gotSerialized, test.serialized) { + t.Errorf("putSpentTxOut (%s): did not get expected "+ + "bytes - got %x, want %x", test.name, + gotSerialized, test.serialized) + continue + } + if gotBytesWritten != len(test.serialized) { + t.Errorf("putSpentTxOut (%s): did not get expected "+ + "number of bytes written - got %d, want %d", + test.name, gotBytesWritten, + len(test.serialized)) + continue + } + + // Ensure the serialized bytes are decoded back to the expected + // stxo. + var gotStxo spentTxOut + gotBytesRead, err := decodeSpentTxOut(test.serialized, &gotStxo, + test.txVersion) + if err != nil { + t.Errorf("decodeSpentTxOut (%s): unexpected error: %v", + test.name, err) + continue + } + gotStxo.maybeDecompress(test.stxo.version) + if !reflect.DeepEqual(gotStxo, test.stxo) { + t.Errorf("decodeSpentTxOut (%s) mismatched entries - "+ + "got %v, want %v", test.name, gotStxo, test.stxo) + continue + } + if gotBytesRead != len(test.serialized) { + t.Errorf("decodeSpentTxOut (%s): did not get expected "+ + "number of bytes read - got %d, want %d", + test.name, gotBytesRead, len(test.serialized)) + continue + } + } +} + +// TestStxoDecodeErrors performs negative tests against decoding spent +// transaction outputs to ensure error paths work as expected. +func TestStxoDecodeErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + stxo spentTxOut + txVersion int32 // When the txout is not fully spent. + serialized []byte + bytesRead int // Expected number of bytes read. + errType error + }{ + { + name: "nothing serialized", + stxo: spentTxOut{}, + serialized: hexToBytes(""), + errType: errDeserialize(""), + bytesRead: 0, + }, + { + name: "no data after header code w/o version", + stxo: spentTxOut{}, + serialized: hexToBytes("00"), + errType: errDeserialize(""), + bytesRead: 1, + }, + { + name: "no data after header code with version", + stxo: spentTxOut{}, + serialized: hexToBytes("13"), + errType: errDeserialize(""), + bytesRead: 1, + }, + { + name: "no data after version", + stxo: spentTxOut{}, + serialized: hexToBytes("1301"), + errType: errDeserialize(""), + bytesRead: 2, + }, + { + name: "no serialized tx version and passed 0", + stxo: spentTxOut{}, + serialized: hexToBytes("003205"), + errType: AssertError(""), + bytesRead: 1, + }, + { + name: "incomplete compressed txout", + stxo: spentTxOut{}, + txVersion: 1, + serialized: hexToBytes("0032"), + errType: errDeserialize(""), + bytesRead: 2, + }, + } + + for _, test := range tests { + // Ensure the expected error type is returned. + gotBytesRead, err := decodeSpentTxOut(test.serialized, + &test.stxo, test.txVersion) + if reflect.TypeOf(err) != reflect.TypeOf(test.errType) { + t.Errorf("decodeSpentTxOut (%s): expected error type "+ + "does not match - got %T, want %T", test.name, + err, test.errType) + continue + } + + // Ensure the expected number of bytes read is returned. + if gotBytesRead != test.bytesRead { + t.Errorf("decodeSpentTxOut (%s): unexpected number of "+ + "bytes read - got %d, want %d", test.name, + gotBytesRead, test.bytesRead) + continue + } + } +} + +// TestSpendJournalSerialization ensures serializing and deserializing spend +// journal entries works as expected. +func TestSpendJournalSerialization(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + entry []spentTxOut + blockTxns []*wire.MsgTx + utxoView *UtxoViewpoint + serialized []byte + }{ + // From block 2 in main blockchain. + { + name: "No spends", + entry: nil, + blockTxns: nil, + utxoView: NewUtxoViewpoint(), + serialized: nil, + }, + // From block 170 in main blockchain. + { + name: "One tx with one input spends last output of coinbase", + entry: []spentTxOut{{ + amount: 5000000000, + pkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"), + isCoinBase: true, + height: 9, + version: 1, + }}, + blockTxns: []*wire.MsgTx{{ // Coinbase omitted. + Version: 1, + TxIn: []*wire.TxIn{{ + PreviousOutPoint: wire.OutPoint{ + Hash: *newShaHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"), + Index: 0, + }, + SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"), + Sequence: 0xffffffff, + }}, + TxOut: []*wire.TxOut{{ + Value: 1000000000, + PkScript: hexToBytes("4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac"), + }, { + Value: 4000000000, + PkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"), + }}, + LockTime: 0, + }}, + utxoView: NewUtxoViewpoint(), + serialized: hexToBytes("1301320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"), + }, + // Adapted from block 100025 in main blockchain. + { + name: "Two txns when one spends last output, one doesn't", + entry: []spentTxOut{{ + amount: 34405000000, + pkScript: hexToBytes("76a9146edbc6c4d31bae9f1ccc38538a114bf42de65e8688ac"), + version: 1, + }, { + amount: 13761000000, + pkScript: hexToBytes("76a914b2fb57eadf61e106a100a7445a8c3f67898841ec88ac"), + isCoinBase: false, + height: 100024, + version: 1, + }}, + blockTxns: []*wire.MsgTx{{ // Coinbase omitted. + Version: 1, + TxIn: []*wire.TxIn{{ + PreviousOutPoint: wire.OutPoint{ + Hash: *newShaHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"), + Index: 1, + }, + SignatureScript: hexToBytes("493046022100c167eead9840da4a033c9a56470d7794a9bb1605b377ebe5688499b39f94be59022100fb6345cab4324f9ea0b9ee9169337534834638d818129778370f7d378ee4a325014104d962cac5390f12ddb7539507065d0def320d68c040f2e73337c3a1aaaab7195cb5c4d02e0959624d534f3c10c3cf3d73ca5065ebd62ae986b04c6d090d32627c"), + Sequence: 0xffffffff, + }}, + TxOut: []*wire.TxOut{{ + Value: 5000000, + PkScript: hexToBytes("76a914f419b8db4ba65f3b6fcc233acb762ca6f51c23d488ac"), + }, { + Value: 34400000000, + PkScript: hexToBytes("76a914cadf4fc336ab3c6a4610b75f31ba0676b7f663d288ac"), + }}, + LockTime: 0, + }, { + Version: 1, + TxIn: []*wire.TxIn{{ + PreviousOutPoint: wire.OutPoint{ + Hash: *newShaHashFromStr("92fbe1d4be82f765dfabc9559d4620864b05cc897c4db0e29adac92d294e52b7"), + Index: 0, + }, + SignatureScript: hexToBytes("483045022100e256743154c097465cf13e89955e1c9ff2e55c46051b627751dee0144183157e02201d8d4f02cde8496aae66768f94d35ce54465bd4ae8836004992d3216a93a13f00141049d23ce8686fe9b802a7a938e8952174d35dd2c2089d4112001ed8089023ab4f93a3c9fcd5bfeaa9727858bf640dc1b1c05ec3b434bb59837f8640e8810e87742"), + Sequence: 0xffffffff, + }}, + TxOut: []*wire.TxOut{{ + Value: 5000000, + PkScript: hexToBytes("76a914a983ad7c92c38fc0e2025212e9f972204c6e687088ac"), + }, { + Value: 13756000000, + PkScript: hexToBytes("76a914a6ebd69952ab486a7a300bfffdcb395dc7d47c2388ac"), + }}, + LockTime: 0, + }}, + utxoView: &UtxoViewpoint{entries: map[wire.ShaHash]*UtxoEntry{ + *newShaHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"): { + version: 1, + isCoinBase: false, + blockHeight: 100024, + sparseOutputs: map[uint32]*utxoOutput{ + 1: { + amount: 34405000000, + pkScript: hexToBytes("76a9142084541c3931677527a7eafe56fd90207c344eb088ac"), + }, + }, + }, + }}, + serialized: hexToBytes("8b99700186c64700b2fb57eadf61e106a100a7445a8c3f67898841ec0091f20f006edbc6c4d31bae9f1ccc38538a114bf42de65e86"), + }, + // Hand crafted. + { + name: "One tx, two inputs from same tx, neither spend last output", + entry: []spentTxOut{{ + amount: 165125632, + pkScript: hexToBytes("51"), + version: 1, + }, { + amount: 154370000, + pkScript: hexToBytes("51"), + version: 1, + }}, + blockTxns: []*wire.MsgTx{{ // Coinbase omitted. + Version: 1, + TxIn: []*wire.TxIn{{ + PreviousOutPoint: wire.OutPoint{ + Hash: *newShaHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"), + Index: 1, + }, + SignatureScript: hexToBytes(""), + Sequence: 0xffffffff, + }, { + PreviousOutPoint: wire.OutPoint{ + Hash: *newShaHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"), + Index: 2, + }, + SignatureScript: hexToBytes(""), + Sequence: 0xffffffff, + }}, + TxOut: []*wire.TxOut{{ + Value: 165125632, + PkScript: hexToBytes("51"), + }, { + Value: 154370000, + PkScript: hexToBytes("51"), + }}, + LockTime: 0, + }}, + utxoView: &UtxoViewpoint{entries: map[wire.ShaHash]*UtxoEntry{ + *newShaHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"): { + version: 1, + isCoinBase: false, + blockHeight: 100000, + sparseOutputs: map[uint32]*utxoOutput{ + 0: { + amount: 165712179, + pkScript: hexToBytes("51"), + }, + }, + }, + }}, + serialized: hexToBytes("0087bc3707510084c3d19a790751"), + }, + } + + for i, test := range tests { + // Ensure the journal entry serializes to the expected value. + gotBytes := serializeSpendJournalEntry(test.entry) + if !bytes.Equal(gotBytes, test.serialized) { + t.Errorf("serializeSpendJournalEntry #%d (%s): "+ + "mismatched bytes - got %x, want %x", i, + test.name, gotBytes, test.serialized) + continue + } + + // Deserialize to a spend journal entry. + gotEntry, err := deserializeSpendJournalEntry(test.serialized, + test.blockTxns, test.utxoView) + if err != nil { + t.Errorf("deserializeSpendJournalEntry #%d (%s) "+ + "unexpected error: %v", i, test.name, err) + continue + } + for stxoIdx := range gotEntry { + stxo := &gotEntry[stxoIdx] + stxo.maybeDecompress(test.entry[stxoIdx].version) + } + + // Ensure that the deserialized spend journal entry has the + // correct properties. + if !reflect.DeepEqual(gotEntry, test.entry) { + t.Errorf("deserializeSpendJournalEntry #%d (%s) "+ + "mismatched entries - got %v, want %v", + i, test.name, gotEntry, test.entry) + continue + } + } +} + +// TestSpendJournalErrors performs negative tests against deserializing spend +// journal entries to ensure error paths work as expected. +func TestSpendJournalErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + blockTxns []*wire.MsgTx + utxoView *UtxoViewpoint + serialized []byte + errType error + }{ + // Adapted from block 170 in main blockchain. + { + name: "Force assertion due to missing stxos", + blockTxns: []*wire.MsgTx{{ // Coinbase omitted. + Version: 1, + TxIn: []*wire.TxIn{{ + PreviousOutPoint: wire.OutPoint{ + Hash: *newShaHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"), + Index: 0, + }, + SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"), + Sequence: 0xffffffff, + }}, + LockTime: 0, + }}, + utxoView: NewUtxoViewpoint(), + serialized: hexToBytes(""), + errType: AssertError(""), + }, + { + name: "Force deserialization error in stxos", + blockTxns: []*wire.MsgTx{{ // Coinbase omitted. + Version: 1, + TxIn: []*wire.TxIn{{ + PreviousOutPoint: wire.OutPoint{ + Hash: *newShaHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"), + Index: 0, + }, + SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"), + Sequence: 0xffffffff, + }}, + LockTime: 0, + }}, + utxoView: NewUtxoViewpoint(), + serialized: hexToBytes("1301320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a"), + errType: errDeserialize(""), + }, + } + + for _, test := range tests { + // Ensure the expected error type is returned and the returned + // slice is nil. + stxos, err := deserializeSpendJournalEntry(test.serialized, + test.blockTxns, test.utxoView) + if reflect.TypeOf(err) != reflect.TypeOf(test.errType) { + t.Errorf("deserializeSpendJournalEntry (%s): expected "+ + "error type does not match - got %T, want %T", + test.name, err, test.errType) + continue + } + if stxos != nil { + t.Errorf("deserializeSpendJournalEntry (%s): returned "+ + "slice of spent transaction outputs is not nil", + test.name) + continue + } + } +} + +// TestUtxoSerialization ensures serializing and deserializing unspent +// trasaction output entries works as expected. +func TestUtxoSerialization(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + entry *UtxoEntry + serialized []byte + }{ + // From tx in main blockchain: + // 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098 + { + name: "Only output 0, coinbase", + entry: &UtxoEntry{ + version: 1, + isCoinBase: true, + blockHeight: 1, + sparseOutputs: map[uint32]*utxoOutput{ + 0: { + amount: 5000000000, + pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"), + }, + }, + }, + serialized: hexToBytes("010103320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"), + }, + // From tx in main blockchain: + // 8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb + { + name: "Only output 1, not coinbase", + entry: &UtxoEntry{ + version: 1, + isCoinBase: false, + blockHeight: 100001, + sparseOutputs: map[uint32]*utxoOutput{ + 1: { + amount: 1000000, + pkScript: hexToBytes("76a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac"), + }, + }, + }, + serialized: hexToBytes("01858c21040700ee8bd501094a7d5ca318da2506de35e1cb025ddc"), + }, + // Adapted from tx in main blockchain: + // df3f3f442d9699857f7f49de4ff0b5d0f3448bec31cdc7b5bf6d25f2abd637d5 + { + name: "Only output 2, coinbase", + entry: &UtxoEntry{ + version: 1, + isCoinBase: true, + blockHeight: 99004, + sparseOutputs: map[uint32]*utxoOutput{ + 2: { + amount: 100937281, + pkScript: hexToBytes("76a914da33f77cee27c2a975ed5124d7e4f7f97513510188ac"), + }, + }, + }, + serialized: hexToBytes("0185843c010182b095bf4100da33f77cee27c2a975ed5124d7e4f7f975135101"), + }, + // Adapted from tx in main blockchain: + // 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f + { + name: "outputs 0 and 2 not coinbase", + entry: &UtxoEntry{ + version: 1, + isCoinBase: false, + blockHeight: 113931, + sparseOutputs: map[uint32]*utxoOutput{ + 0: { + amount: 20000000, + pkScript: hexToBytes("76a914e2ccd6ec7c6e2e581349c77e067385fa8236bf8a88ac"), + }, + 2: { + amount: 15000000, + pkScript: hexToBytes("76a914b8025be1b3efc63b0ad48e7f9f10e87544528d5888ac"), + }, + }, + }, + serialized: hexToBytes("0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58"), + }, + // Adapted from tx in main blockchain: + // 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f + { + name: "outputs 0 and 2, not coinbase, 1 marked spent", + entry: &UtxoEntry{ + version: 1, + isCoinBase: false, + blockHeight: 113931, + sparseOutputs: map[uint32]*utxoOutput{ + 0: { + amount: 20000000, + pkScript: hexToBytes("76a914e2ccd6ec7c6e2e581349c77e067385fa8236bf8a88ac"), + }, + 1: { // This won't be serialized. + spent: true, + amount: 1000000, + pkScript: hexToBytes("76a914e43031c3e46f20bf1ccee9553ce815de5a48467588ac"), + }, + 2: { + amount: 15000000, + pkScript: hexToBytes("76a914b8025be1b3efc63b0ad48e7f9f10e87544528d5888ac"), + }, + }, + }, + serialized: hexToBytes("0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58"), + }, + // Adapted from tx in main blockchain: + // 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f + { + name: "outputs 0 and 2, not coinbase, output 2 compressed", + entry: &UtxoEntry{ + version: 1, + isCoinBase: false, + blockHeight: 113931, + sparseOutputs: map[uint32]*utxoOutput{ + 0: { + amount: 20000000, + pkScript: hexToBytes("76a914e2ccd6ec7c6e2e581349c77e067385fa8236bf8a88ac"), + }, + 2: { + // Uncompressed Amount: 15000000 + // Uncompressed PkScript: 76a914b8025be1b3efc63b0ad48e7f9f10e87544528d5888ac + compressed: true, + amount: 137, + pkScript: hexToBytes("00b8025be1b3efc63b0ad48e7f9f10e87544528d58"), + }, + }, + }, + serialized: hexToBytes("0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58"), + }, + // Adapted from tx in main blockchain: + // 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f + { + name: "outputs 0 and 2, not coinbase, output 2 compressed, packed indexes reversed", + entry: &UtxoEntry{ + version: 1, + isCoinBase: false, + blockHeight: 113931, + sparseOutputs: map[uint32]*utxoOutput{ + 0: { + amount: 20000000, + pkScript: hexToBytes("76a914e2ccd6ec7c6e2e581349c77e067385fa8236bf8a88ac"), + }, + 2: { + // Uncompressed Amount: 15000000 + // Uncompressed PkScript: 76a914b8025be1b3efc63b0ad48e7f9f10e87544528d5888ac + compressed: true, + amount: 137, + pkScript: hexToBytes("00b8025be1b3efc63b0ad48e7f9f10e87544528d58"), + }, + }, + }, + serialized: hexToBytes("0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58"), + }, + // From tx in main blockchain: + // 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098 + { + name: "Only output 0, coinbase, fully spent", + entry: &UtxoEntry{ + version: 1, + isCoinBase: true, + blockHeight: 1, + sparseOutputs: map[uint32]*utxoOutput{ + 0: { + spent: true, + amount: 5000000000, + pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"), + }, + }, + }, + serialized: nil, + }, + // Adapted from tx in main blockchain: + // 1b02d1c8cfef60a189017b9a420c682cf4a0028175f2f563209e4ff61c8c3620 + { + name: "Only output 22, not coinbase", + entry: &UtxoEntry{ + version: 1, + isCoinBase: false, + blockHeight: 338156, + sparseOutputs: map[uint32]*utxoOutput{ + 22: { + spent: false, + amount: 366875659, + pkScript: hexToBytes("a9141dd46a006572d820e448e12d2bbb38640bc718e687"), + }, + }, + }, + serialized: hexToBytes("0193d06c100000108ba5b9e763011dd46a006572d820e448e12d2bbb38640bc718e6"), + }, + } + + for i, test := range tests { + // Ensure the utxo entry serializes to the expected value. + gotBytes, err := serializeUtxoEntry(test.entry) + if err != nil { + t.Errorf("serializeUtxoEntry #%d (%s) unexpected "+ + "error: %v", i, test.name, err) + continue + } + if !bytes.Equal(gotBytes, test.serialized) { + t.Errorf("serializeUtxoEntry #%d (%s): mismatched "+ + "bytes - got %x, want %x", i, test.name, + gotBytes, test.serialized) + continue + } + + // Don't try to deserialize if the test entry was fully spent + // since it will have a nil serialization. + if test.entry.IsFullySpent() { + continue + } + + // Deserialize to a utxo entry. + utxoEntry, err := deserializeUtxoEntry(test.serialized) + if err != nil { + t.Errorf("deserializeUtxoEntry #%d (%s) unexpected "+ + "error: %v", i, test.name, err) + continue + } + + // Ensure that the deserialized utxo entry has the same + // properties for the containing transaction and block height. + if utxoEntry.Version() != test.entry.Version() { + t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+ + "version: got %d, want %d", i, test.name, + utxoEntry.Version(), test.entry.Version()) + continue + } + if utxoEntry.IsCoinBase() != test.entry.IsCoinBase() { + t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+ + "coinbase flag: got %v, want %v", i, test.name, + utxoEntry.IsCoinBase(), test.entry.IsCoinBase()) + continue + } + if utxoEntry.BlockHeight() != test.entry.BlockHeight() { + t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+ + "block height: got %d, want %d", i, test.name, + utxoEntry.BlockHeight(), + test.entry.BlockHeight()) + continue + } + if utxoEntry.IsFullySpent() != test.entry.IsFullySpent() { + t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+ + "fully spent: got %v, want %v", i, test.name, + utxoEntry.IsFullySpent(), + test.entry.IsFullySpent()) + continue + } + + // Ensure all of the outputs in the test entry match the + // spentness of the output in the deserialized entry and the + // deserialized entry does not contain any additional utxos. + var numUnspent int + for outputIndex := range test.entry.sparseOutputs { + gotSpent := utxoEntry.IsOutputSpent(outputIndex) + wantSpent := test.entry.IsOutputSpent(outputIndex) + if !wantSpent { + numUnspent++ + } + if gotSpent != wantSpent { + t.Errorf("deserializeUtxoEntry #%d (%s) output "+ + "#%d: mismatched spent: got %v, want "+ + "%v", i, test.name, outputIndex, + gotSpent, wantSpent) + continue + + } + } + if len(utxoEntry.sparseOutputs) != numUnspent { + t.Errorf("deserializeUtxoEntry #%d (%s): mismatched "+ + "number of unspent outputs: got %d, want %d", i, + test.name, len(utxoEntry.sparseOutputs), + numUnspent) + continue + } + + // Ensure all of the amounts and scripts of the utxos in the + // deserialized entry match the ones in the test entry. + for outputIndex := range utxoEntry.sparseOutputs { + gotAmount := utxoEntry.AmountByIndex(outputIndex) + wantAmount := test.entry.AmountByIndex(outputIndex) + if gotAmount != wantAmount { + t.Errorf("deserializeUtxoEntry #%d (%s) "+ + "output #%d: mismatched amounts: got "+ + "%d, want %d", i, test.name, + outputIndex, gotAmount, wantAmount) + continue + } + + gotPkScript := utxoEntry.PkScriptByIndex(outputIndex) + wantPkScript := test.entry.PkScriptByIndex(outputIndex) + if !bytes.Equal(gotPkScript, wantPkScript) { + t.Errorf("deserializeUtxoEntry #%d (%s) "+ + "output #%d mismatched scripts: got "+ + "%x, want %x", i, test.name, + outputIndex, gotPkScript, wantPkScript) + continue + } + } + } +} + +// TestUtxoEntryHeaderCodeErrors performs negative tests against unspent +// transaction output header codes to ensure error paths work as expected. +func TestUtxoEntryHeaderCodeErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + entry *UtxoEntry + code uint64 + bytesRead int // Expected number of bytes read. + errType error + }{ + { + name: "Force assertion due to fully spent tx", + entry: &UtxoEntry{}, + errType: AssertError(""), + bytesRead: 0, + }, + } + + for _, test := range tests { + // Ensure the expected error type is returned and the code is 0. + code, gotBytesRead, err := utxoEntryHeaderCode(test.entry, 0) + if reflect.TypeOf(err) != reflect.TypeOf(test.errType) { + t.Errorf("utxoEntryHeaderCode (%s): expected error "+ + "type does not match - got %T, want %T", + test.name, err, test.errType) + continue + } + if code != 0 { + t.Errorf("utxoEntryHeaderCode (%s): unexpected code "+ + "on error - got %d, want 0", test.name, code) + continue + } + + // Ensure the expected number of bytes read is returned. + if gotBytesRead != test.bytesRead { + t.Errorf("utxoEntryHeaderCode (%s): unexpected number "+ + "of bytes read - got %d, want %d", test.name, + gotBytesRead, test.bytesRead) + continue + } + } +} + +// TestUtxoEntryDeserializeErrors performs negative tests against deserializing +// unspent transaction outputs to ensure error paths work as expected. +func TestUtxoEntryDeserializeErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + serialized []byte + errType error + }{ + { + name: "no data after version", + serialized: hexToBytes("01"), + errType: errDeserialize(""), + }, + { + name: "no data after block height", + serialized: hexToBytes("0101"), + errType: errDeserialize(""), + }, + { + name: "no data after header code", + serialized: hexToBytes("010102"), + errType: errDeserialize(""), + }, + { + name: "not enough bytes for unspentness bitmap", + serialized: hexToBytes("01017800"), + errType: errDeserialize(""), + }, + { + name: "incomplete compressed txout", + serialized: hexToBytes("01010232"), + errType: errDeserialize(""), + }, + } + + for _, test := range tests { + // Ensure the expected error type is returned and the returned + // entry is nil. + entry, err := deserializeUtxoEntry(test.serialized) + if reflect.TypeOf(err) != reflect.TypeOf(test.errType) { + t.Errorf("deserializeUtxoEntry (%s): expected error "+ + "type does not match - got %T, want %T", + test.name, err, test.errType) + continue + } + if entry != nil { + t.Errorf("deserializeUtxoEntry (%s): returned entry "+ + "is not nil", test.name) + continue + } + } +} + +// TestBestChainStateSerialization ensures serializing and deserializing the +// best chain state works as expected. +func TestBestChainStateSerialization(t *testing.T) { + t.Parallel() + + workSum := new(big.Int) + tests := []struct { + name string + state bestChainState + serialized []byte + }{ + { + name: "genesis", + state: bestChainState{ + hash: *newShaHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"), + height: 0, + totalTxns: 1, + workSum: func() *big.Int { + workSum.Add(workSum, CalcWork(486604799)) + return new(big.Int).Set(workSum) + }(), // 0x0100010001 + }, + serialized: hexToBytes("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000000000000100000000000000050000000100010001"), + }, + { + name: "block 1", + state: bestChainState{ + hash: *newShaHashFromStr("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"), + height: 1, + totalTxns: 2, + workSum: func() *big.Int { + workSum.Add(workSum, CalcWork(486604799)) + return new(big.Int).Set(workSum) + }(), // 0x0200020002 + }, + serialized: hexToBytes("4860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000010000000200000000000000050000000200020002"), + }, + } + + for i, test := range tests { + // Ensure the state serializes to the expected value. + gotBytes := serializeBestChainState(test.state) + if !bytes.Equal(gotBytes, test.serialized) { + t.Errorf("serializeBestChainState #%d (%s): mismatched "+ + "bytes - got %x, want %x", i, test.name, + gotBytes, test.serialized) + continue + } + + // Ensure the serialized bytes are decoded back to the expected + // state. + state, err := deserializeBestChainState(test.serialized) + if err != nil { + t.Errorf("deserializeBestChainState #%d (%s) "+ + "unexpected error: %v", i, test.name, err) + continue + } + if !reflect.DeepEqual(state, test.state) { + t.Errorf("deserializeBestChainState #%d (%s) "+ + "mismatched state - got %v, want %v", i, + test.name, state, test.state) + continue + + } + } +} + +// TestBestChainStateDeserializeErrors performs negative tests against +// deserializing the chain state to ensure error paths work as expected. +func TestBestChainStateDeserializeErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + serialized []byte + errType error + }{ + { + name: "nothing serialized", + serialized: hexToBytes(""), + errType: database.Error{ErrorCode: database.ErrCorruption}, + }, + { + name: "short data in hash", + serialized: hexToBytes("0000"), + errType: database.Error{ErrorCode: database.ErrCorruption}, + }, + { + name: "short data in work sum", + serialized: hexToBytes("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000001000000000000000500000001000100"), + errType: database.Error{ErrorCode: database.ErrCorruption}, + }, + } + + for _, test := range tests { + // Ensure the expected error type and code is returned. + _, err := deserializeBestChainState(test.serialized) + if reflect.TypeOf(err) != reflect.TypeOf(test.errType) { + t.Errorf("deserializeBestChainState (%s): expected "+ + "error type does not match - got %T, want %T", + test.name, err, test.errType) + continue + } + if derr, ok := err.(database.Error); ok { + tderr := test.errType.(database.Error) + if derr.ErrorCode != tderr.ErrorCode { + t.Errorf("deserializeBestChainState (%s): "+ + "wrong error code got: %v, want: %v", + test.name, derr.ErrorCode, + tderr.ErrorCode) + continue + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/checkpoints.go b/vendor/github.com/btcsuite/btcd/blockchain/checkpoints.go new file mode 100644 index 0000000000000000000000000000000000000000..66b3ebb2cd392f1bf93e402211cfaa57b3985aa7 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/checkpoints.go @@ -0,0 +1,334 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "fmt" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// CheckpointConfirmations is the number of blocks before the end of the current +// best block chain that a good checkpoint candidate must be. +const CheckpointConfirmations = 2016 + +// newShaHashFromStr converts the passed big-endian hex string into a +// wire.ShaHash. It only differs from the one available in wire in that +// it ignores the error since it will only (and must only) be called with +// hard-coded, and therefore known good, hashes. +func newShaHashFromStr(hexStr string) *wire.ShaHash { + sha, _ := wire.NewShaHashFromStr(hexStr) + return sha +} + +// DisableCheckpoints provides a mechanism to disable validation against +// checkpoints which you DO NOT want to do in production. It is provided only +// for debug purposes. +// +// This function is safe for concurrent access. +func (b *BlockChain) DisableCheckpoints(disable bool) { + b.chainLock.Lock() + b.noCheckpoints = disable + b.chainLock.Unlock() +} + +// Checkpoints returns a slice of checkpoints (regardless of whether they are +// already known). When checkpoints are disabled or there are no checkpoints +// for the active network, it will return nil. +// +// This function is safe for concurrent access. +func (b *BlockChain) Checkpoints() []chaincfg.Checkpoint { + b.chainLock.RLock() + defer b.chainLock.RUnlock() + + if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 { + return nil + } + + return b.chainParams.Checkpoints +} + +// latestCheckpoint returns the most recent checkpoint (regardless of whether it +// is already known). When checkpoints are disabled or there are no checkpoints +// for the active network, it will return nil. +// +// This function MUST be called with the chain state lock held (for reads). +func (b *BlockChain) latestCheckpoint() *chaincfg.Checkpoint { + if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 { + return nil + } + + checkpoints := b.chainParams.Checkpoints + return &checkpoints[len(checkpoints)-1] +} + +// LatestCheckpoint returns the most recent checkpoint (regardless of whether it +// is already known). When checkpoints are disabled or there are no checkpoints +// for the active network, it will return nil. +// +// This function is safe for concurrent access. +func (b *BlockChain) LatestCheckpoint() *chaincfg.Checkpoint { + b.chainLock.RLock() + checkpoint := b.latestCheckpoint() + b.chainLock.RUnlock() + return checkpoint +} + +// verifyCheckpoint returns whether the passed block height and hash combination +// match the hard-coded checkpoint data. It also returns true if there is no +// checkpoint data for the passed block height. +// +// This function MUST be called with the chain lock held (for reads). +func (b *BlockChain) verifyCheckpoint(height int32, hash *wire.ShaHash) bool { + if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 { + return true + } + + // Nothing to check if there is no checkpoint data for the block height. + checkpoint, exists := b.checkpointsByHeight[height] + if !exists { + return true + } + + if !checkpoint.Hash.IsEqual(hash) { + return false + } + + log.Infof("Verified checkpoint at height %d/block %s", checkpoint.Height, + checkpoint.Hash) + return true +} + +// findPreviousCheckpoint finds the most recent checkpoint that is already +// available in the downloaded portion of the block chain and returns the +// associated block. It returns nil if a checkpoint can't be found (this should +// really only happen for blocks before the first checkpoint). +// +// This function MUST be called with the chain lock held (for reads). +func (b *BlockChain) findPreviousCheckpoint() (*btcutil.Block, error) { + if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 { + return nil, nil + } + + // No checkpoints. + checkpoints := b.chainParams.Checkpoints + numCheckpoints := len(checkpoints) + if numCheckpoints == 0 { + return nil, nil + } + + // Perform the initial search to find and cache the latest known + // checkpoint if the best chain is not known yet or we haven't already + // previously searched. + if b.checkpointBlock == nil && b.nextCheckpoint == nil { + // Loop backwards through the available checkpoints to find one + // that is already available. + checkpointIndex := -1 + err := b.db.View(func(dbTx database.Tx) error { + for i := numCheckpoints - 1; i >= 0; i-- { + if dbMainChainHasBlock(dbTx, checkpoints[i].Hash) { + checkpointIndex = i + break + } + } + return nil + }) + if err != nil { + return nil, err + } + + // No known latest checkpoint. This will only happen on blocks + // before the first known checkpoint. So, set the next expected + // checkpoint to the first checkpoint and return the fact there + // is no latest known checkpoint block. + if checkpointIndex == -1 { + b.nextCheckpoint = &checkpoints[0] + return nil, nil + } + + // Cache the latest known checkpoint block for future lookups. + checkpoint := checkpoints[checkpointIndex] + err = b.db.View(func(dbTx database.Tx) error { + block, err := dbFetchBlockByHash(dbTx, checkpoint.Hash) + if err != nil { + return err + } + b.checkpointBlock = block + + // Set the next expected checkpoint block accordingly. + b.nextCheckpoint = nil + if checkpointIndex < numCheckpoints-1 { + b.nextCheckpoint = &checkpoints[checkpointIndex+1] + } + + return nil + }) + if err != nil { + return nil, err + } + + return b.checkpointBlock, nil + } + + // At this point we've already searched for the latest known checkpoint, + // so when there is no next checkpoint, the current checkpoint lockin + // will always be the latest known checkpoint. + if b.nextCheckpoint == nil { + return b.checkpointBlock, nil + } + + // When there is a next checkpoint and the height of the current best + // chain does not exceed it, the current checkpoint lockin is still + // the latest known checkpoint. + if b.bestNode.height < b.nextCheckpoint.Height { + return b.checkpointBlock, nil + } + + // We've reached or exceeded the next checkpoint height. Note that + // once a checkpoint lockin has been reached, forks are prevented from + // any blocks before the checkpoint, so we don't have to worry about the + // checkpoint going away out from under us due to a chain reorganize. + + // Cache the latest known checkpoint block for future lookups. Note + // that if this lookup fails something is very wrong since the chain + // has already passed the checkpoint which was verified as accurate + // before inserting it. + err := b.db.View(func(tx database.Tx) error { + block, err := dbFetchBlockByHash(tx, b.nextCheckpoint.Hash) + if err != nil { + return err + } + b.checkpointBlock = block + return nil + }) + if err != nil { + return nil, err + } + + // Set the next expected checkpoint. + checkpointIndex := -1 + for i := numCheckpoints - 1; i >= 0; i-- { + if checkpoints[i].Hash.IsEqual(b.nextCheckpoint.Hash) { + checkpointIndex = i + break + } + } + b.nextCheckpoint = nil + if checkpointIndex != -1 && checkpointIndex < numCheckpoints-1 { + b.nextCheckpoint = &checkpoints[checkpointIndex+1] + } + + return b.checkpointBlock, nil +} + +// isNonstandardTransaction determines whether a transaction contains any +// scripts which are not one of the standard types. +func isNonstandardTransaction(tx *btcutil.Tx) bool { + // Check all of the output public key scripts for non-standard scripts. + for _, txOut := range tx.MsgTx().TxOut { + scriptClass := txscript.GetScriptClass(txOut.PkScript) + if scriptClass == txscript.NonStandardTy { + return true + } + } + return false +} + +// IsCheckpointCandidate returns whether or not the passed block is a good +// checkpoint candidate. +// +// The factors used to determine a good checkpoint are: +// - The block must be in the main chain +// - The block must be at least 'CheckpointConfirmations' blocks prior to the +// current end of the main chain +// - The timestamps for the blocks before and after the checkpoint must have +// timestamps which are also before and after the checkpoint, respectively +// (due to the median time allowance this is not always the case) +// - The block must not contain any strange transaction such as those with +// nonstandard scripts +// +// The intent is that candidates are reviewed by a developer to make the final +// decision and then manually added to the list of checkpoints for a network. +// +// This function is safe for concurrent access. +func (b *BlockChain) IsCheckpointCandidate(block *btcutil.Block) (bool, error) { + b.chainLock.RLock() + defer b.chainLock.RUnlock() + + // Checkpoints must be enabled. + if b.noCheckpoints { + return false, fmt.Errorf("checkpoints are disabled") + } + + var isCandidate bool + err := b.db.View(func(dbTx database.Tx) error { + // A checkpoint must be in the main chain. + blockHeight, err := dbFetchHeightByHash(dbTx, block.Sha()) + if err != nil { + // Only return an error if it's not due to the block not + // being in the main chain. + if !isNotInMainChainErr(err) { + return err + } + return nil + } + + // Ensure the height of the passed block and the entry for the + // block in the main chain match. This should always be the + // case unless the caller provided an invalid block. + if blockHeight != block.Height() { + return fmt.Errorf("passed block height of %d does not "+ + "match the main chain height of %d", + block.Height(), blockHeight) + } + + // A checkpoint must be at least CheckpointConfirmations blocks + // before the end of the main chain. + mainChainHeight := b.bestNode.height + if blockHeight > (mainChainHeight - CheckpointConfirmations) { + return nil + } + + // Get the previous block header. + prevHash := &block.MsgBlock().Header.PrevBlock + prevHeader, err := dbFetchHeaderByHash(dbTx, prevHash) + if err != nil { + return err + } + + // Get the next block header. + nextHeader, err := dbFetchHeaderByHeight(dbTx, blockHeight+1) + if err != nil { + return err + } + + // A checkpoint must have timestamps for the block and the + // blocks on either side of it in order (due to the median time + // allowance this is not always the case). + prevTime := prevHeader.Timestamp + curTime := block.MsgBlock().Header.Timestamp + nextTime := nextHeader.Timestamp + if prevTime.After(curTime) || nextTime.Before(curTime) { + return nil + } + + // A checkpoint must have transactions that only contain + // standard scripts. + for _, tx := range block.Transactions() { + if isNonstandardTransaction(tx) { + return nil + } + } + + // All of the checks passed, so the block is a candidate. + isCandidate = true + return nil + }) + return isCandidate, err +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/common_test.go b/vendor/github.com/btcsuite/btcd/blockchain/common_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9021b048715f760a0b743ce53418153f1ff88e7e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/common_test.go @@ -0,0 +1,181 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain_test + +import ( + "compress/bzip2" + "encoding/binary" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + _ "github.com/btcsuite/btcd/database/ffldb" + "github.com/btcsuite/btcd/wire" +) + +const ( + // testDbType is the database backend type to use for the tests. + testDbType = "ffldb" + + // testDbRoot is the root directory used to create all test databases. + testDbRoot = "testdbs" + + // blockDataNet is the expected network in the test block data. + blockDataNet = wire.MainNet +) + +// filesExists returns whether or not the named file or directory exists. +func fileExists(name string) bool { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +} + +// isSupportedDbType returns whether or not the passed database type is +// currently supported. +func isSupportedDbType(dbType string) bool { + supportedDrivers := database.SupportedDrivers() + for _, driver := range supportedDrivers { + if dbType == driver { + return true + } + } + + return false +} + +// chainSetup is used to create a new db and chain instance with the genesis +// block already inserted. In addition to the new chain instnce, it returns +// a teardown function the caller should invoke when done testing to clean up. +func chainSetup(dbName string) (*blockchain.BlockChain, func(), error) { + if !isSupportedDbType(testDbType) { + return nil, nil, fmt.Errorf("unsupported db type %v", testDbType) + } + + // Handle memory database specially since it doesn't need the disk + // specific handling. + var db database.DB + var teardown func() + if testDbType == "memdb" { + ndb, err := database.Create(testDbType) + if err != nil { + return nil, nil, fmt.Errorf("error creating db: %v", err) + } + db = ndb + + // Setup a teardown function for cleaning up. This function is + // returned to the caller to be invoked when it is done testing. + teardown = func() { + db.Close() + } + } else { + // Create the root directory for test databases. + if !fileExists(testDbRoot) { + if err := os.MkdirAll(testDbRoot, 0700); err != nil { + err := fmt.Errorf("unable to create test db "+ + "root: %v", err) + return nil, nil, err + } + } + + // Create a new database to store the accepted blocks into. + dbPath := filepath.Join(testDbRoot, dbName) + _ = os.RemoveAll(dbPath) + ndb, err := database.Create(testDbType, dbPath, blockDataNet) + if err != nil { + return nil, nil, fmt.Errorf("error creating db: %v", err) + } + db = ndb + + // Setup a teardown function for cleaning up. This function is + // returned to the caller to be invoked when it is done testing. + teardown = func() { + db.Close() + os.RemoveAll(dbPath) + os.RemoveAll(testDbRoot) + } + } + + // Create the main chain instance. + chain, err := blockchain.New(&blockchain.Config{ + DB: db, + ChainParams: &chaincfg.MainNetParams, + }) + if err != nil { + teardown() + err := fmt.Errorf("failed to create chain instance: %v", err) + return nil, nil, err + } + return chain, teardown, nil +} + +// loadUtxoView returns a utxo view loaded from a file. +func loadUtxoView(filename string) (*blockchain.UtxoViewpoint, error) { + // The utxostore file format is: + // <tx hash><serialized utxo len><serialized utxo> + // + // The serialized utxo len is a little endian uint32 and the serialized + // utxo uses the format described in chainio.go. + + filename = filepath.Join("testdata", filename) + fi, err := os.Open(filename) + if err != nil { + return nil, err + } + + // Choose read based on whether the file is compressed or not. + var r io.Reader + if strings.HasSuffix(filename, ".bz2") { + r = bzip2.NewReader(fi) + } else { + r = fi + } + defer fi.Close() + + view := blockchain.NewUtxoViewpoint() + for { + // Hash of the utxo entry. + var hash wire.ShaHash + _, err := io.ReadAtLeast(r, hash[:], len(hash[:])) + if err != nil { + // Expected EOF at the right offset. + if err == io.EOF { + break + } + return nil, err + } + + // Num of serialize utxo entry bytes. + var numBytes uint32 + err = binary.Read(r, binary.LittleEndian, &numBytes) + if err != nil { + return nil, err + } + + // Serialized utxo entry. + serialized := make([]byte, numBytes) + _, err = io.ReadAtLeast(r, serialized, int(numBytes)) + if err != nil { + return nil, err + } + + // Deserialize it and add it to the view. + utxoEntry, err := blockchain.TstDeserializeUtxoEntry(serialized) + if err != nil { + return nil, err + } + view.Entries()[hash] = utxoEntry + } + + return view, nil +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/compress.go b/vendor/github.com/btcsuite/btcd/blockchain/compress.go new file mode 100644 index 0000000000000000000000000000000000000000..87328d0f31b4cd8e57a9adb8dc8aef07c0b7b0b8 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/compress.go @@ -0,0 +1,603 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/txscript" +) + +// ----------------------------------------------------------------------------- +// A variable length quantity (VLQ) is an encoding that uses an arbitrary number +// of binary octets to represent an arbitrarily large integer. The scheme +// employs a most significant byte (MSB) base-128 encoding where the high bit in +// each byte indicates whether or not the byte is the final one. In addition, +// to ensure there are no redundant encodings, an offset is subtracted every +// time a group of 7 bits is shifted out. Therefore each integer can be +// represented in exactly one way, and each representation stands for exactly +// one integer. +// +// Another nice property of this encoding is that it provides a compact +// representation of values that are typically used to indicate sizes. For +// example, the values 0 - 127 are represented with a single byte, 128 - 16511 +// with two bytes, and 16512 - 2113663 with three bytes. +// +// While the encoding allows arbitrarily large integers, it is artificially +// limited in this code to an unsigned 64-bit integer for efficiency purposes. +// +// Example encodings: +// 0 -> [0x00] +// 127 -> [0x7f] * Max 1-byte value +// 128 -> [0x80 0x00] +// 129 -> [0x80 0x01] +// 255 -> [0x80 0x7f] +// 256 -> [0x81 0x00] +// 16511 -> [0xff 0x7f] * Max 2-byte value +// 16512 -> [0x80 0x80 0x00] +// 32895 -> [0x80 0xff 0x7f] +// 2113663 -> [0xff 0xff 0x7f] * Max 3-byte value +// 270549119 -> [0xff 0xff 0xff 0x7f] * Max 4-byte value +// 2^64-1 -> [0x80 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0x7f] +// +// References: +// https://en.wikipedia.org/wiki/Variable-length_quantity +// http://www.codecodex.com/wiki/Variable-Length_Integers +// ----------------------------------------------------------------------------- + +// serializeSizeVLQ returns the number of bytes it would take to serialize the +// passed number as a variable-length quantity according to the format described +// above. +func serializeSizeVLQ(n uint64) int { + size := 1 + for ; n > 0x7f; n = (n >> 7) - 1 { + size++ + } + + return size +} + +// putVLQ serializes the provided number to a variable-length quantity according +// to the format described above and returns the number of bytes of the encoded +// value. The result is placed directly into the passed byte slice which must +// be at least large enough to handle the number of bytes returned by the +// serializeSizeVLQ function or it will panic. +func putVLQ(target []byte, n uint64) int { + offset := 0 + for ; ; offset++ { + // The high bit is set when another byte follows. + highBitMask := byte(0x80) + if offset == 0 { + highBitMask = 0x00 + } + + target[offset] = byte(n&0x7f) | highBitMask + if n <= 0x7f { + break + } + n = (n >> 7) - 1 + } + + // Reverse the bytes so it is MSB-encoded. + for i, j := 0, offset; i < j; i, j = i+1, j-1 { + target[i], target[j] = target[j], target[i] + } + + return offset + 1 +} + +// deserializeVLQ deserializes the provided variable-length quantity according +// to the format described above. It also returns the number of bytes +// deserialized. +func deserializeVLQ(serialized []byte) (uint64, int) { + var n uint64 + var size int + for _, val := range serialized { + size++ + n = (n << 7) | uint64(val&0x7f) + if val&0x80 != 0x80 { + break + } + n++ + } + + return n, size +} + +// ----------------------------------------------------------------------------- +// In order to reduce the size of stored scripts, a domain specific compression +// algorithm is used which recognizes standard scripts and stores them using +// less bytes than the original script. The compression algorithm used here was +// obtained from Bitcoin Core, so all credits for the algorithm go to it. +// +// The general serialized format is: +// +// <script size or type><script data> +// +// Field Type Size +// script size or type VLQ variable +// script data []byte variable +// +// The specific serialized format for each recognized standard script is: +// +// - Pay-to-pubkey-hash: (21 bytes) - <0><20-byte pubkey hash> +// - Pay-to-script-hash: (21 bytes) - <1><20-byte script hash> +// - Pay-to-pubkey**: (33 bytes) - <2, 3, 4, or 5><32-byte pubkey X value> +// 2, 3 = compressed pubkey with bit 0 specifying the y coordinate to use +// 4, 5 = uncompressed pubkey with bit 0 specifying the y coordinate to use +// ** Only valid public keys starting with 0x02, 0x03, and 0x04 are supported. +// +// Any scripts which are not recognized as one of the aforementioned standard +// scripts are encoded using the general serialized format and encode the script +// size as the sum of the actual size of the script and the number of special +// cases. +// ----------------------------------------------------------------------------- + +// The following constants specify the special constants used to identify a +// special script type in the domain-specific compressed script encoding. +// +// NOTE: This section specifically does not use iota since these values are +// serialized and must be stable for long-term storage. +const ( + // cstPayToPubKeyHash identifies a compressed pay-to-pubkey-hash script. + cstPayToPubKeyHash = 0 + + // cstPayToScriptHash identifies a compressed pay-to-script-hash script. + cstPayToScriptHash = 1 + + // cstPayToPubKeyComp2 identifies a compressed pay-to-pubkey script to + // a compressed pubkey. Bit 0 specifies which y-coordinate to use + // to reconstruct the full uncompressed pubkey. + cstPayToPubKeyComp2 = 2 + + // cstPayToPubKeyComp3 identifies a compressed pay-to-pubkey script to + // a compressed pubkey. Bit 0 specifies which y-coordinate to use + // to reconstruct the full uncompressed pubkey. + cstPayToPubKeyComp3 = 3 + + // cstPayToPubKeyUncomp4 identifies a compressed pay-to-pubkey script to + // an uncompressed pubkey. Bit 0 specifies which y-coordinate to use + // to reconstruct the full uncompressed pubkey. + cstPayToPubKeyUncomp4 = 4 + + // cstPayToPubKeyUncomp5 identifies a compressed pay-to-pubkey script to + // an uncompressed pubkey. Bit 0 specifies which y-coordinate to use + // to reconstruct the full uncompressed pubkey. + cstPayToPubKeyUncomp5 = 5 + + // numSpecialScripts is the number of special scripts recognized by the + // domain-specific script compression algorithm. + numSpecialScripts = 6 +) + +// isPubKeyHash returns whether or not the passed public key script is a +// standard pay-to-pubkey-hash script along with the pubkey hash it is paying to +// if it is. +func isPubKeyHash(script []byte) (bool, []byte) { + if len(script) == 25 && script[0] == txscript.OP_DUP && + script[1] == txscript.OP_HASH160 && + script[2] == txscript.OP_DATA_20 && + script[23] == txscript.OP_EQUALVERIFY && + script[24] == txscript.OP_CHECKSIG { + + return true, script[3:23] + } + + return false, nil +} + +// isScriptHash returns whether or not the passed public key script is a +// standard pay-to-script-hash script along with the script hash it is paying to +// if it is. +func isScriptHash(script []byte) (bool, []byte) { + if len(script) == 23 && script[0] == txscript.OP_HASH160 && + script[1] == txscript.OP_DATA_20 && + script[22] == txscript.OP_EQUAL { + + return true, script[2:22] + } + + return false, nil +} + +// isPubKey returns whether or not the passed public key script is a standard +// pay-to-pubkey script that pays to a valid compressed or uncompressed public +// key along with the serialized pubkey it is paying to if it is. +// +// NOTE: This function ensures the public key is actually valid since the +// compression algorithm requires valid pubkeys. It does not support hybrid +// pubkeys. This means that even if the script has the correct form for a +// pay-to-pubkey script, this function will only return true when it is paying +// to a valid compressed or uncompressed pubkey. +func isPubKey(script []byte) (bool, []byte) { + // Pay-to-compressed-pubkey script. + if len(script) == 35 && script[0] == txscript.OP_DATA_33 && + script[34] == txscript.OP_CHECKSIG && (script[1] == 0x02 || + script[1] == 0x03) { + + // Ensure the public key is valid. + serializedPubKey := script[1:34] + _, err := btcec.ParsePubKey(serializedPubKey, btcec.S256()) + if err == nil { + return true, serializedPubKey + } + } + + // Pay-to-uncompressed-pubkey script. + if len(script) == 67 && script[0] == txscript.OP_DATA_65 && + script[66] == txscript.OP_CHECKSIG && script[1] == 0x04 { + + // Ensure the public key is valid. + serializedPubKey := script[1:66] + _, err := btcec.ParsePubKey(serializedPubKey, btcec.S256()) + if err == nil { + return true, serializedPubKey + } + } + + return false, nil +} + +// compressedScriptSize returns the number of bytes the passed script would take +// when encoded with the domain specific compression algorithm described above. +func compressedScriptSize(pkScript []byte, version int32) int { + // Pay-to-pubkey-hash script. + if valid, _ := isPubKeyHash(pkScript); valid { + return 21 + } + + // Pay-to-script-hash script. + if valid, _ := isScriptHash(pkScript); valid { + return 21 + } + + // Pay-to-pubkey (compressed or uncompressed) script. + if valid, _ := isPubKey(pkScript); valid { + return 33 + } + + // When none of the above special cases apply, encode the script as is + // preceded by the sum of its size and the number of special cases + // encoded as a variable length quantity. + return serializeSizeVLQ(uint64(len(pkScript)+numSpecialScripts)) + + len(pkScript) +} + +// decodeCompressedScriptSize treats the passed serialized bytes as a compressed +// script, possibly followed by other data, and returns the number of bytes it +// occupies taking into account the special encoding of the script size by the +// domain specific compression algorithm described above. +func decodeCompressedScriptSize(serialized []byte, version int32) int { + scriptSize, bytesRead := deserializeVLQ(serialized) + if bytesRead == 0 { + return 0 + } + + switch scriptSize { + case cstPayToPubKeyHash: + return 21 + + case cstPayToScriptHash: + return 21 + + case cstPayToPubKeyComp2, cstPayToPubKeyComp3, cstPayToPubKeyUncomp4, + cstPayToPubKeyUncomp5: + return 33 + } + + scriptSize -= numSpecialScripts + scriptSize += uint64(bytesRead) + return int(scriptSize) +} + +// putCompressedScript compresses the passed script according to the domain +// specific compression algorithm described above directly into the passed +// target byte slice. The target byte slice must be at least large enough to +// handle the number of bytes returned by the compressedScriptSize function or +// it will panic. +func putCompressedScript(target, pkScript []byte, version int32) int { + // Pay-to-pubkey-hash script. + if valid, hash := isPubKeyHash(pkScript); valid { + target[0] = cstPayToPubKeyHash + copy(target[1:21], hash) + return 21 + } + + // Pay-to-script-hash script. + if valid, hash := isScriptHash(pkScript); valid { + target[0] = cstPayToScriptHash + copy(target[1:21], hash) + return 21 + } + + // Pay-to-pubkey (compressed or uncompressed) script. + if valid, serializedPubKey := isPubKey(pkScript); valid { + pubKeyFormat := serializedPubKey[0] + switch pubKeyFormat { + case 0x02, 0x03: + target[0] = pubKeyFormat + copy(target[1:33], serializedPubKey[1:33]) + return 33 + case 0x04: + // Encode the oddness of the serialized pubkey into the + // compressed script type. + target[0] = pubKeyFormat | (serializedPubKey[64] & 0x01) + copy(target[1:33], serializedPubKey[1:33]) + return 33 + } + } + + // When none of the above special cases apply, encode the unmodified + // script preceded by the sum of its size and the number of special + // cases encoded as a variable length quantity. + encodedSize := uint64(len(pkScript) + numSpecialScripts) + vlqSizeLen := putVLQ(target, encodedSize) + copy(target[vlqSizeLen:], pkScript) + return vlqSizeLen + len(pkScript) +} + +// decompressScript returns the original script obtained by decompressing the +// passed compressed script according to the domain specific compression +// algorithm described above. +// +// NOTE: The script parameter must already have been proven to be long enough +// to contain the number of bytes returned by decodeCompressedScriptSize or it +// will panic. This is acceptable since it is only an internal function. +func decompressScript(compressedPkScript []byte, version int32) []byte { + // In practice this function will not be called with a zero-length or + // nil script since the nil script encoding includes the length, however + // the code below assumes the length exists, so just return nil now if + // the function ever ends up being called with a nil script in the + // future. + if len(compressedPkScript) == 0 { + return nil + } + + // Decode the script size and examine it for the special cases. + encodedScriptSize, bytesRead := deserializeVLQ(compressedPkScript) + switch encodedScriptSize { + // Pay-to-pubkey-hash script. The resulting script is: + // <OP_DUP><OP_HASH160><20 byte hash><OP_EQUALVERIFY><OP_CHECKSIG> + case cstPayToPubKeyHash: + pkScript := make([]byte, 25) + pkScript[0] = txscript.OP_DUP + pkScript[1] = txscript.OP_HASH160 + pkScript[2] = txscript.OP_DATA_20 + copy(pkScript[3:], compressedPkScript[bytesRead:bytesRead+20]) + pkScript[23] = txscript.OP_EQUALVERIFY + pkScript[24] = txscript.OP_CHECKSIG + return pkScript + + // Pay-to-script-hash script. The resulting script is: + // <OP_HASH160><20 byte script hash><OP_EQUAL> + case cstPayToScriptHash: + pkScript := make([]byte, 23) + pkScript[0] = txscript.OP_HASH160 + pkScript[1] = txscript.OP_DATA_20 + copy(pkScript[2:], compressedPkScript[bytesRead:bytesRead+20]) + pkScript[22] = txscript.OP_EQUAL + return pkScript + + // Pay-to-compressed-pubkey script. The resulting script is: + // <OP_DATA_33><33 byte compressed pubkey><OP_CHECKSIG> + case cstPayToPubKeyComp2, cstPayToPubKeyComp3: + pkScript := make([]byte, 35) + pkScript[0] = txscript.OP_DATA_33 + pkScript[1] = byte(encodedScriptSize) + copy(pkScript[2:], compressedPkScript[bytesRead:bytesRead+32]) + pkScript[34] = txscript.OP_CHECKSIG + return pkScript + + // Pay-to-uncompressed-pubkey script. The resulting script is: + // <OP_DATA_65><65 byte uncompressed pubkey><OP_CHECKSIG> + case cstPayToPubKeyUncomp4, cstPayToPubKeyUncomp5: + // Change the leading byte to the appropriate compressed pubkey + // identifier (0x02 or 0x03) so it can be decoded as a + // compressed pubkey. This really should never fail since the + // encoding ensures it is valid before compressing to this type. + compressedKey := make([]byte, 33) + compressedKey[0] = byte(encodedScriptSize - 2) + copy(compressedKey[1:], compressedPkScript[1:]) + key, err := btcec.ParsePubKey(compressedKey, btcec.S256()) + if err != nil { + return nil + } + + pkScript := make([]byte, 67) + pkScript[0] = txscript.OP_DATA_65 + copy(pkScript[1:], key.SerializeUncompressed()) + pkScript[66] = txscript.OP_CHECKSIG + return pkScript + } + + // When none of the special cases apply, the script was encoded using + // the general format, so reduce the script size by the number of + // special cases and return the unmodified script. + scriptSize := int(encodedScriptSize - numSpecialScripts) + pkScript := make([]byte, scriptSize) + copy(pkScript, compressedPkScript[bytesRead:bytesRead+scriptSize]) + return pkScript +} + +// ----------------------------------------------------------------------------- +// In order to reduce the size of stored amounts, a domain specific compression +// algorithm is used which relies on there typically being a lot of zeroes at +// end of the amounts. The compression algorithm used here was obtained from +// Bitcoin Core, so all credits for the algorithm go to it. +// +// While this is simply exchanging one uint64 for another, the resulting value +// for typical amounts has a much smaller magnitude which results in fewer bytes +// when encoded as variable length quantity. For example, consider the amount +// of 0.1 BTC which is 10000000 satoshi. Encoding 10000000 as a VLQ would take +// 4 bytes while encoding the compressed value of 8 as a VLQ only takes 1 byte. +// +// Essentially the compression is achieved by splitting the value into an +// exponent in the range [0-9] and a digit in the range [1-9], when possible, +// and encoding them in a way that can be decoded. More specifically, the +// encoding is as follows: +// - 0 is 0 +// - Find the exponent, e, as the largest power of 10 that evenly divides the +// value up to a maximum of 9 +// - When e < 9, the final digit can't be 0 so store it as d and remove it by +// dividing the value by 10 (call the result n). The encoded value is thus: +// 1 + 10*(9*n + d-1) + e +// - When e==9, the only thing known is the amount is not 0. The encoded value +// is thus: +// 1 + 10*(n-1) + e == 10 + 10*(n-1) +// +// Example encodings: +// (The numbers in parenthesis are the number of bytes when serialized as a VLQ) +// 0 (1) -> 0 (1) * 0.00000000 BTC +// 1000 (2) -> 4 (1) * 0.00001000 BTC +// 10000 (2) -> 5 (1) * 0.00010000 BTC +// 12345678 (4) -> 111111101(4) * 0.12345678 BTC +// 50000000 (4) -> 47 (1) * 0.50000000 BTC +// 100000000 (4) -> 9 (1) * 1.00000000 BTC +// 500000000 (5) -> 49 (1) * 5.00000000 BTC +// 1000000000 (5) -> 10 (1) * 10.00000000 BTC +// ----------------------------------------------------------------------------- + +// compressTxOutAmount compresses the passed amount according to the domain +// specific compression algorithm described above. +func compressTxOutAmount(amount uint64) uint64 { + // No need to do any work if it's zero. + if amount == 0 { + return 0 + } + + // Find the largest power of 10 (max of 9) that evenly divides the + // value. + exponent := uint64(0) + for amount%10 == 0 && exponent < 9 { + amount /= 10 + exponent++ + } + + // The compressed result for exponents less than 9 is: + // 1 + 10*(9*n + d-1) + e + if exponent < 9 { + lastDigit := amount % 10 + amount /= 10 + return 1 + 10*(9*amount+lastDigit-1) + exponent + } + + // The compressed result for an exponent of 9 is: + // 1 + 10*(n-1) + e == 10 + 10*(n-1) + return 10 + 10*(amount-1) +} + +// decompressTxOutAmount returns the original amount the passed compressed +// amount represents according to the domain specific compression algorithm +// described above. +func decompressTxOutAmount(amount uint64) uint64 { + // No need to do any work if it's zero. + if amount == 0 { + return 0 + } + + // The decompressed amount is either of the following two equations: + // x = 1 + 10*(9*n + d - 1) + e + // x = 1 + 10*(n - 1) + 9 + amount-- + + // The decompressed amount is now one of the following two equations: + // x = 10*(9*n + d - 1) + e + // x = 10*(n - 1) + 9 + exponent := amount % 10 + amount /= 10 + + // The decompressed amount is now one of the following two equations: + // x = 9*n + d - 1 | where e < 9 + // x = n - 1 | where e = 9 + n := uint64(0) + if exponent < 9 { + lastDigit := amount%9 + 1 + amount /= 9 + n = amount*10 + lastDigit + } else { + n = amount + 1 + } + + // Apply the exponent. + for ; exponent > 0; exponent-- { + n *= 10 + } + + return n +} + +// ----------------------------------------------------------------------------- +// Compressed transaction outputs consist of an amount and a public key script +// both compressed using the domain specific compression algorithms previously +// described. +// +// The serialized format is: +// +// <compressed amount><compressed script> +// +// Field Type Size +// compressed amount VLQ variable +// compressed script []byte variable +// ----------------------------------------------------------------------------- + +// compressedTxOutSize returns the number of bytes the passed transaction output +// fields would take when encoded with the format described above. The +// preCompressed flag indicates the provided amount and script are already +// compressed. This is useful since loaded utxo entries are not decompressed +// until the output is accessed. +func compressedTxOutSize(amount uint64, pkScript []byte, version int32, preCompressed bool) int { + if preCompressed { + return serializeSizeVLQ(amount) + len(pkScript) + } + + return serializeSizeVLQ(compressTxOutAmount(amount)) + + compressedScriptSize(pkScript, version) +} + +// putCompressedTxOut potentially compresses the passed amount and script +// according to their domain specific compression algorithms and encodes them +// directly into the passed target byte slice with the format described above. +// The preCompressed flag indicates the provided amount and script are already +// compressed in which case the values are not modified. This is useful since +// loaded utxo entries are not decompressed until the output is accessed. The +// target byte slice must be at least large enough to handle the number of bytes +// returned by the compressedTxOutSize function or it will panic. +func putCompressedTxOut(target []byte, amount uint64, pkScript []byte, version int32, preCompressed bool) int { + if preCompressed { + offset := putVLQ(target, amount) + copy(target[offset:], pkScript) + return offset + len(pkScript) + } + + offset := putVLQ(target, compressTxOutAmount(amount)) + offset += putCompressedScript(target[offset:], pkScript, version) + return offset +} + +// decodeCompressedTxOut decodes the passed compressed txout, possibly followed +// by other data, into its compressed amount and compressed script and returns +// them along with the number of bytes they occupied. +func decodeCompressedTxOut(serialized []byte, version int32) (uint64, []byte, int, error) { + // Deserialize the compressed amount and ensure there are bytes + // remaining for the compressed script. + compressedAmount, bytesRead := deserializeVLQ(serialized) + if bytesRead >= len(serialized) { + return 0, nil, bytesRead, errDeserialize("unexpected end of " + + "data after compressed amount") + } + + // Decode the compressed script size and ensure there are enough bytes + // left in the slice for it. + scriptSize := decodeCompressedScriptSize(serialized[bytesRead:], version) + if len(serialized[bytesRead:]) < scriptSize { + return 0, nil, bytesRead, errDeserialize("unexpected end of " + + "data after script size") + } + + // Make a copy of the compressed script so the original serialized data + // can be released as soon as possible. + compressedScript := make([]byte, scriptSize) + copy(compressedScript, serialized[bytesRead:bytesRead+scriptSize]) + return compressedAmount, compressedScript, bytesRead + scriptSize, nil +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/compress_test.go b/vendor/github.com/btcsuite/btcd/blockchain/compress_test.go new file mode 100644 index 0000000000000000000000000000000000000000..10a7747b9f97a6810627ecf739baba84d8da76b3 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/compress_test.go @@ -0,0 +1,491 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "bytes" + "encoding/hex" + "testing" +) + +// hexToBytes converts the passed hex string into bytes and will panic if there +// is an error. This is only provided for the hard-coded constants so errors in +// the source code can be detected. It will only (and must only) be called with +// hard-coded values. +func hexToBytes(s string) []byte { + b, err := hex.DecodeString(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + return b +} + +// TestVLQ ensures the variable length quantity serialization, deserialization, +// and size calculation works as expected. +func TestVLQ(t *testing.T) { + t.Parallel() + + tests := []struct { + val uint64 + serialized []byte + }{ + {0, hexToBytes("00")}, + {1, hexToBytes("01")}, + {127, hexToBytes("7f")}, + {128, hexToBytes("8000")}, + {129, hexToBytes("8001")}, + {255, hexToBytes("807f")}, + {256, hexToBytes("8100")}, + {16383, hexToBytes("fe7f")}, + {16384, hexToBytes("ff00")}, + {16511, hexToBytes("ff7f")}, // Max 2-byte value + {16512, hexToBytes("808000")}, + {16513, hexToBytes("808001")}, + {16639, hexToBytes("80807f")}, + {32895, hexToBytes("80ff7f")}, + {2113663, hexToBytes("ffff7f")}, // Max 3-byte value + {2113664, hexToBytes("80808000")}, + {270549119, hexToBytes("ffffff7f")}, // Max 4-byte value + {270549120, hexToBytes("8080808000")}, + {2147483647, hexToBytes("86fefefe7f")}, + {2147483648, hexToBytes("86fefeff00")}, + {4294967295, hexToBytes("8efefefe7f")}, // Max uint32, 5 bytes + // Max uint64, 10 bytes + {18446744073709551615, hexToBytes("80fefefefefefefefe7f")}, + } + + for _, test := range tests { + // Ensure the function to calculate the serialized size without + // actually serializing the value is calculated properly. + gotSize := serializeSizeVLQ(test.val) + if gotSize != len(test.serialized) { + t.Errorf("serializeSizeVLQ: did not get expected size "+ + "for %d - got %d, want %d", test.val, gotSize, + len(test.serialized)) + continue + } + + // Ensure the value serializes to the expected bytes. + gotBytes := make([]byte, gotSize) + gotBytesWritten := putVLQ(gotBytes, test.val) + if !bytes.Equal(gotBytes, test.serialized) { + t.Errorf("putVLQUnchecked: did not get expected bytes "+ + "for %d - got %x, want %x", test.val, gotBytes, + test.serialized) + continue + } + if gotBytesWritten != len(test.serialized) { + t.Errorf("putVLQUnchecked: did not get expected number "+ + "of bytes written for %d - got %d, want %d", + test.val, gotBytesWritten, len(test.serialized)) + continue + } + + // Ensure the serialized bytes deserialize to the expected + // value. + gotVal, gotBytesRead := deserializeVLQ(test.serialized) + if gotVal != test.val { + t.Errorf("deserializeVLQ: did not get expected value "+ + "for %x - got %d, want %d", test.serialized, + gotVal, test.val) + continue + } + if gotBytesRead != len(test.serialized) { + t.Errorf("deserializeVLQ: did not get expected number "+ + "of bytes read for %d - got %d, want %d", + test.serialized, gotBytesRead, + len(test.serialized)) + continue + } + } +} + +// TestScriptCompression ensures the domain-specific script compression and +// decompression works as expected. +func TestScriptCompression(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + version int32 + uncompressed []byte + compressed []byte + }{ + { + name: "nil", + version: 1, + uncompressed: nil, + compressed: hexToBytes("06"), + }, + { + name: "pay-to-pubkey-hash 1", + version: 1, + uncompressed: hexToBytes("76a9141018853670f9f3b0582c5b9ee8ce93764ac32b9388ac"), + compressed: hexToBytes("001018853670f9f3b0582c5b9ee8ce93764ac32b93"), + }, + { + name: "pay-to-pubkey-hash 2", + version: 1, + uncompressed: hexToBytes("76a914e34cce70c86373273efcc54ce7d2a491bb4a0e8488ac"), + compressed: hexToBytes("00e34cce70c86373273efcc54ce7d2a491bb4a0e84"), + }, + { + name: "pay-to-script-hash 1", + version: 1, + uncompressed: hexToBytes("a914da1745e9b549bd0bfa1a569971c77eba30cd5a4b87"), + compressed: hexToBytes("01da1745e9b549bd0bfa1a569971c77eba30cd5a4b"), + }, + { + name: "pay-to-script-hash 2", + version: 1, + uncompressed: hexToBytes("a914f815b036d9bbbce5e9f2a00abd1bf3dc91e9551087"), + compressed: hexToBytes("01f815b036d9bbbce5e9f2a00abd1bf3dc91e95510"), + }, + { + name: "pay-to-pubkey compressed 0x02", + version: 1, + uncompressed: hexToBytes("2102192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4ac"), + compressed: hexToBytes("02192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"), + }, + { + name: "pay-to-pubkey compressed 0x03", + version: 1, + uncompressed: hexToBytes("2103b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65ac"), + compressed: hexToBytes("03b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65"), + }, + { + name: "pay-to-pubkey uncompressed 0x04 even", + version: 1, + uncompressed: hexToBytes("4104192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b40d45264838c0bd96852662ce6a847b197376830160c6d2eb5e6a4c44d33f453eac"), + compressed: hexToBytes("04192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"), + }, + { + name: "pay-to-pubkey uncompressed 0x04 odd", + version: 1, + uncompressed: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"), + compressed: hexToBytes("0511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"), + }, + { + name: "pay-to-pubkey invalid pubkey", + version: 1, + uncompressed: hexToBytes("3302aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac"), + compressed: hexToBytes("293302aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac"), + }, + { + name: "null data", + version: 1, + uncompressed: hexToBytes("6a200102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"), + compressed: hexToBytes("286a200102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"), + }, + { + name: "requires 2 size bytes - data push 200 bytes", + version: 1, + uncompressed: append(hexToBytes("4cc8"), bytes.Repeat([]byte{0x00}, 200)...), + // [0x80, 0x50] = 208 as a variable length quantity + // [0x4c, 0xc8] = OP_PUSHDATA1 200 + compressed: append(hexToBytes("80504cc8"), bytes.Repeat([]byte{0x00}, 200)...), + }, + } + + for _, test := range tests { + // Ensure the function to calculate the serialized size without + // actually serializing the value is calculated properly. + gotSize := compressedScriptSize(test.uncompressed, test.version) + if gotSize != len(test.compressed) { + t.Errorf("compressedScriptSize (%s): did not get "+ + "expected size - got %d, want %d", test.name, + gotSize, len(test.compressed)) + continue + } + + // Ensure the script compresses to the expected bytes. + gotCompressed := make([]byte, gotSize) + gotBytesWritten := putCompressedScript(gotCompressed, + test.uncompressed, test.version) + if !bytes.Equal(gotCompressed, test.compressed) { + t.Errorf("putCompressedScript (%s): did not get "+ + "expected bytes - got %x, want %x", test.name, + gotCompressed, test.compressed) + continue + } + if gotBytesWritten != len(test.compressed) { + t.Errorf("putCompressedScript (%s): did not get "+ + "expected number of bytes written - got %d, "+ + "want %d", test.name, gotBytesWritten, + len(test.compressed)) + continue + } + + // Ensure the compressed script size is properly decoded from + // the compressed script. + gotDecodedSize := decodeCompressedScriptSize(test.compressed, + test.version) + if gotDecodedSize != len(test.compressed) { + t.Errorf("decodeCompressedScriptSize (%s): did not get "+ + "expected size - got %d, want %d", test.name, + gotDecodedSize, len(test.compressed)) + continue + } + + // Ensure the script decompresses to the expected bytes. + gotDecompressed := decompressScript(test.compressed, test.version) + if !bytes.Equal(gotDecompressed, test.uncompressed) { + t.Errorf("decompressScript (%s): did not get expected "+ + "bytes - got %x, want %x", test.name, + gotDecompressed, test.uncompressed) + continue + } + } +} + +// TestScriptCompressionErrors ensures calling various functions related to +// script compression with incorrect data returns the expected results. +func TestScriptCompressionErrors(t *testing.T) { + t.Parallel() + + // A nil script must result in a decoded size of 0. + if gotSize := decodeCompressedScriptSize(nil, 1); gotSize != 0 { + t.Fatalf("decodeCompressedScriptSize with nil script did not "+ + "return 0 - got %d", gotSize) + } + + // A nil script must result in a nil decompressed script. + if gotScript := decompressScript(nil, 1); gotScript != nil { + t.Fatalf("decompressScript with nil script did not return nil "+ + "decompressed script - got %x", gotScript) + } + + // A compressed script for a pay-to-pubkey (uncompressed) that results + // in an invalid pubkey must result in a nil decompressed script. + compressedScript := hexToBytes("04012d74d0cb94344c9569c2e77901573d8d" + + "7903c3ebec3a957724895dca52c6b4") + if gotScript := decompressScript(compressedScript, 1); gotScript != nil { + t.Fatalf("decompressScript with compressed pay-to-"+ + "uncompressed-pubkey that is invalid did not return "+ + "nil decompressed script - got %x", gotScript) + } +} + +// TestAmountCompression ensures the domain-specific transaction output amount +// compression and decompression works as expected. +func TestAmountCompression(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + uncompressed uint64 + compressed uint64 + }{ + { + name: "0 BTC (sometimes used in nulldata)", + uncompressed: 0, + compressed: 0, + }, + { + name: "546 Satoshi (current network dust value)", + uncompressed: 546, + compressed: 4911, + }, + { + name: "0.00001 BTC (typical transaction fee)", + uncompressed: 1000, + compressed: 4, + }, + { + name: "0.0001 BTC (typical transaction fee)", + uncompressed: 10000, + compressed: 5, + }, + { + name: "0.12345678 BTC", + uncompressed: 12345678, + compressed: 111111101, + }, + { + name: "0.5 BTC", + uncompressed: 50000000, + compressed: 48, + }, + { + name: "1 BTC", + uncompressed: 100000000, + compressed: 9, + }, + { + name: "5 BTC", + uncompressed: 500000000, + compressed: 49, + }, + { + name: "21000000 BTC (max minted coins)", + uncompressed: 2100000000000000, + compressed: 21000000, + }, + } + + for _, test := range tests { + // Ensure the amount compresses to the expected value. + gotCompressed := compressTxOutAmount(test.uncompressed) + if gotCompressed != test.compressed { + t.Errorf("compressTxOutAmount (%s): did not get "+ + "expected value - got %d, want %d", test.name, + gotCompressed, test.compressed) + continue + } + + // Ensure the value decompresses to the expected value. + gotDecompressed := decompressTxOutAmount(test.compressed) + if gotDecompressed != test.uncompressed { + t.Errorf("decompressTxOutAmount (%s): did not get "+ + "expected value - got %d, want %d", test.name, + gotDecompressed, test.uncompressed) + continue + } + } +} + +// TestCompressedTxOut ensures the transaction output serialization and +// deserialization works as expected. +func TestCompressedTxOut(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + amount uint64 + compAmount uint64 + pkScript []byte + compPkScript []byte + version int32 + compressed []byte + }{ + { + name: "nulldata with 0 BTC", + amount: 0, + compAmount: 0, + pkScript: hexToBytes("6a200102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"), + compPkScript: hexToBytes("286a200102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"), + version: 1, + compressed: hexToBytes("00286a200102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"), + }, + { + name: "pay-to-pubkey-hash dust", + amount: 546, + compAmount: 4911, + pkScript: hexToBytes("76a9141018853670f9f3b0582c5b9ee8ce93764ac32b9388ac"), + compPkScript: hexToBytes("001018853670f9f3b0582c5b9ee8ce93764ac32b93"), + version: 1, + compressed: hexToBytes("a52f001018853670f9f3b0582c5b9ee8ce93764ac32b93"), + }, + { + name: "pay-to-pubkey uncompressed 1 BTC", + amount: 100000000, + compAmount: 9, + pkScript: hexToBytes("4104192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b40d45264838c0bd96852662ce6a847b197376830160c6d2eb5e6a4c44d33f453eac"), + compPkScript: hexToBytes("04192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"), + version: 1, + compressed: hexToBytes("0904192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"), + }, + } + + for _, test := range tests { + // Ensure the function to calculate the serialized size without + // actually serializing the txout is calculated properly. + gotSize := compressedTxOutSize(test.amount, test.pkScript, + test.version, false) + if gotSize != len(test.compressed) { + t.Errorf("compressedTxOutSize (%s): did not get "+ + "expected size - got %d, want %d", test.name, + gotSize, len(test.compressed)) + continue + } + + // Ensure the txout compresses to the expected value. + gotCompressed := make([]byte, gotSize) + gotBytesWritten := putCompressedTxOut(gotCompressed, + test.amount, test.pkScript, test.version, false) + if !bytes.Equal(gotCompressed, test.compressed) { + t.Errorf("compressTxOut (%s): did not get expected "+ + "bytes - got %x, want %x", test.name, + gotCompressed, test.compressed) + continue + } + if gotBytesWritten != len(test.compressed) { + t.Errorf("compressTxOut (%s): did not get expected "+ + "number of bytes written - got %d, want %d", + test.name, gotBytesWritten, + len(test.compressed)) + continue + } + + // Ensure the serialized bytes are decoded back to the expected + // compressed values. + gotAmount, gotScript, gotBytesRead, err := decodeCompressedTxOut( + test.compressed, test.version) + if err != nil { + t.Errorf("decodeCompressedTxOut (%s): unexpected "+ + "error: %v", test.name, err) + continue + } + if gotAmount != test.compAmount { + t.Errorf("decodeCompressedTxOut (%s): did not get "+ + "expected amount - got %d, want %d", + test.name, gotAmount, test.compAmount) + continue + } + if !bytes.Equal(gotScript, test.compPkScript) { + t.Errorf("decodeCompressedTxOut (%s): did not get "+ + "expected script - got %x, want %x", + test.name, gotScript, test.compPkScript) + continue + } + if gotBytesRead != len(test.compressed) { + t.Errorf("decodeCompressedTxOut (%s): did not get "+ + "expected number of bytes read - got %d, want %d", + test.name, gotBytesRead, len(test.compressed)) + continue + } + + // Ensure the compressed values decompress to the expected + // txout. + gotAmount = decompressTxOutAmount(gotAmount) + if gotAmount != test.amount { + t.Errorf("decompressTxOut (%s): did not get expected "+ + "value - got %d, want %d", test.name, gotAmount, + test.amount) + continue + } + gotScript = decompressScript(gotScript, test.version) + if !bytes.Equal(gotScript, test.pkScript) { + t.Errorf("decompressTxOut (%s): did not get expected "+ + "script - got %x, want %x", test.name, + gotScript, test.pkScript) + continue + } + } +} + +// TestTxOutCompressionErrors ensures calling various functions related to +// txout compression with incorrect data returns the expected results. +func TestTxOutCompressionErrors(t *testing.T) { + t.Parallel() + + // A compressed txout with missing compressed script must error. + compressedTxOut := hexToBytes("00") + _, _, _, err := decodeCompressedTxOut(compressedTxOut, 1) + if !isDeserializeErr(err) { + t.Fatalf("decodeCompressedTxOut with missing compressed script "+ + "did not return expected error type - got %T, want "+ + "errDeserialize", err) + } + + // A compressed txout with short compressed script must error. + compressedTxOut = hexToBytes("0010") + _, _, _, err = decodeCompressedTxOut(compressedTxOut, 1) + if !isDeserializeErr(err) { + t.Fatalf("decodeCompressedTxOut with short compressed script "+ + "did not return expected error type - got %T, want "+ + "errDeserialize", err) + } +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/difficulty.go b/vendor/github.com/btcsuite/btcd/blockchain/difficulty.go new file mode 100644 index 0000000000000000000000000000000000000000..23cf6c961a0373432ca11abd4b52518cb173d361 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/difficulty.go @@ -0,0 +1,367 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "math/big" + "time" + + "github.com/btcsuite/btcd/wire" +) + +const ( + // targetTimespan is the desired amount of time that should elapse + // before block difficulty requirement is examined to determine how + // it should be changed in order to maintain the desired block + // generation rate. + targetTimespan = time.Hour * 24 * 14 + + // targetSpacing is the desired amount of time to generate each block. + targetSpacing = time.Minute * 10 + + // BlocksPerRetarget is the number of blocks between each difficulty + // retarget. It is calculated based on the desired block generation + // rate. + BlocksPerRetarget = int32(targetTimespan / targetSpacing) + + // retargetAdjustmentFactor is the adjustment factor used to limit + // the minimum and maximum amount of adjustment that can occur between + // difficulty retargets. + retargetAdjustmentFactor = 4 + + // minRetargetTimespan is the minimum amount of adjustment that can + // occur between difficulty retargets. It equates to 25% of the + // previous difficulty. + minRetargetTimespan = int64(targetTimespan / retargetAdjustmentFactor) + + // maxRetargetTimespan is the maximum amount of adjustment that can + // occur between difficulty retargets. It equates to 400% of the + // previous difficulty. + maxRetargetTimespan = int64(targetTimespan * retargetAdjustmentFactor) +) + +var ( + // bigOne is 1 represented as a big.Int. It is defined here to avoid + // the overhead of creating it multiple times. + bigOne = big.NewInt(1) + + // oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid + // the overhead of creating it multiple times. + oneLsh256 = new(big.Int).Lsh(bigOne, 256) +) + +// ShaHashToBig converts a wire.ShaHash into a big.Int that can be used to +// perform math comparisons. +func ShaHashToBig(hash *wire.ShaHash) *big.Int { + // A ShaHash is in little-endian, but the big package wants the bytes + // in big-endian, so reverse them. + buf := *hash + blen := len(buf) + for i := 0; i < blen/2; i++ { + buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i] + } + + return new(big.Int).SetBytes(buf[:]) +} + +// CompactToBig converts a compact representation of a whole number N to an +// unsigned 32-bit number. The representation is similar to IEEE754 floating +// point numbers. +// +// Like IEEE754 floating point, there are three basic components: the sign, +// the exponent, and the mantissa. They are broken out as follows: +// +// * the most significant 8 bits represent the unsigned base 256 exponent +// * bit 23 (the 24th bit) represents the sign bit +// * the least significant 23 bits represent the mantissa +// +// ------------------------------------------------- +// | Exponent | Sign | Mantissa | +// ------------------------------------------------- +// | 8 bits [31-24] | 1 bit [23] | 23 bits [22-00] | +// ------------------------------------------------- +// +// The formula to calculate N is: +// N = (-1^sign) * mantissa * 256^(exponent-3) +// +// This compact form is only used in bitcoin to encode unsigned 256-bit numbers +// which represent difficulty targets, thus there really is not a need for a +// sign bit, but it is implemented here to stay consistent with bitcoind. +func CompactToBig(compact uint32) *big.Int { + // Extract the mantissa, sign bit, and exponent. + mantissa := compact & 0x007fffff + isNegative := compact&0x00800000 != 0 + exponent := uint(compact >> 24) + + // Since the base for the exponent is 256, the exponent can be treated + // as the number of bytes to represent the full 256-bit number. So, + // treat the exponent as the number of bytes and shift the mantissa + // right or left accordingly. This is equivalent to: + // N = mantissa * 256^(exponent-3) + var bn *big.Int + if exponent <= 3 { + mantissa >>= 8 * (3 - exponent) + bn = big.NewInt(int64(mantissa)) + } else { + bn = big.NewInt(int64(mantissa)) + bn.Lsh(bn, 8*(exponent-3)) + } + + // Make it negative if the sign bit is set. + if isNegative { + bn = bn.Neg(bn) + } + + return bn +} + +// BigToCompact converts a whole number N to a compact representation using +// an unsigned 32-bit number. The compact representation only provides 23 bits +// of precision, so values larger than (2^23 - 1) only encode the most +// significant digits of the number. See CompactToBig for details. +func BigToCompact(n *big.Int) uint32 { + // No need to do any work if it's zero. + if n.Sign() == 0 { + return 0 + } + + // Since the base for the exponent is 256, the exponent can be treated + // as the number of bytes. So, shift the number right or left + // accordingly. This is equivalent to: + // mantissa = mantissa / 256^(exponent-3) + var mantissa uint32 + exponent := uint(len(n.Bytes())) + if exponent <= 3 { + mantissa = uint32(n.Bits()[0]) + mantissa <<= 8 * (3 - exponent) + } else { + // Use a copy to avoid modifying the caller's original number. + tn := new(big.Int).Set(n) + mantissa = uint32(tn.Rsh(tn, 8*(exponent-3)).Bits()[0]) + } + + // When the mantissa already has the sign bit set, the number is too + // large to fit into the available 23-bits, so divide the number by 256 + // and increment the exponent accordingly. + if mantissa&0x00800000 != 0 { + mantissa >>= 8 + exponent++ + } + + // Pack the exponent, sign bit, and mantissa into an unsigned 32-bit + // int and return it. + compact := uint32(exponent<<24) | mantissa + if n.Sign() < 0 { + compact |= 0x00800000 + } + return compact +} + +// CalcWork calculates a work value from difficulty bits. Bitcoin increases +// the difficulty for generating a block by decreasing the value which the +// generated hash must be less than. This difficulty target is stored in each +// block header using a compact representation as described in the documentation +// for CompactToBig. The main chain is selected by choosing the chain that has +// the most proof of work (highest difficulty). Since a lower target difficulty +// value equates to higher actual difficulty, the work value which will be +// accumulated must be the inverse of the difficulty. Also, in order to avoid +// potential division by zero and really small floating point numbers, the +// result adds 1 to the denominator and multiplies the numerator by 2^256. +func CalcWork(bits uint32) *big.Int { + // Return a work value of zero if the passed difficulty bits represent + // a negative number. Note this should not happen in practice with valid + // blocks, but an invalid block could trigger it. + difficultyNum := CompactToBig(bits) + if difficultyNum.Sign() <= 0 { + return big.NewInt(0) + } + + // (1 << 256) / (difficultyNum + 1) + denominator := new(big.Int).Add(difficultyNum, bigOne) + return new(big.Int).Div(oneLsh256, denominator) +} + +// calcEasiestDifficulty calculates the easiest possible difficulty that a block +// can have given starting difficulty bits and a duration. It is mainly used to +// verify that claimed proof of work by a block is sane as compared to a +// known good checkpoint. +func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration) uint32 { + // Convert types used in the calculations below. + durationVal := int64(duration) + adjustmentFactor := big.NewInt(retargetAdjustmentFactor) + + // The test network rules allow minimum difficulty blocks after more + // than twice the desired amount of time needed to generate a block has + // elapsed. + if b.chainParams.ResetMinDifficulty { + if durationVal > int64(targetSpacing)*2 { + return b.chainParams.PowLimitBits + } + } + + // Since easier difficulty equates to higher numbers, the easiest + // difficulty for a given duration is the largest value possible given + // the number of retargets for the duration and starting difficulty + // multiplied by the max adjustment factor. + newTarget := CompactToBig(bits) + for durationVal > 0 && newTarget.Cmp(b.chainParams.PowLimit) < 0 { + newTarget.Mul(newTarget, adjustmentFactor) + durationVal -= maxRetargetTimespan + } + + // Limit new value to the proof of work limit. + if newTarget.Cmp(b.chainParams.PowLimit) > 0 { + newTarget.Set(b.chainParams.PowLimit) + } + + return BigToCompact(newTarget) +} + +// findPrevTestNetDifficulty returns the difficulty of the previous block which +// did not have the special testnet minimum difficulty rule applied. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) (uint32, error) { + // Search backwards through the chain for the last block without + // the special rule applied. + iterNode := startNode + for iterNode != nil && iterNode.height%BlocksPerRetarget != 0 && + iterNode.bits == b.chainParams.PowLimitBits { + + // Get the previous block node. This function is used over + // simply accessing iterNode.parent directly as it will + // dynamically create previous block nodes as needed. This + // helps allow only the pieces of the chain that are needed + // to remain in memory. + var err error + iterNode, err = b.getPrevNodeFromNode(iterNode) + if err != nil { + log.Errorf("getPrevNodeFromNode: %v", err) + return 0, err + } + } + + // Return the found difficulty or the minimum difficulty if no + // appropriate block was found. + lastBits := b.chainParams.PowLimitBits + if iterNode != nil { + lastBits = iterNode.bits + } + return lastBits, nil +} + +// calcNextRequiredDifficulty calculates the required difficulty for the block +// after the passed previous block node based on the difficulty retarget rules. +// This function differs from the exported CalcNextRequiredDifficulty in that +// the exported version uses the current best chain as the previous block node +// while this function accepts any block node. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTime time.Time) (uint32, error) { + // Genesis block. + if lastNode == nil { + return b.chainParams.PowLimitBits, nil + } + + // Return the previous block's difficulty requirements if this block + // is not at a difficulty retarget interval. + if (lastNode.height+1)%BlocksPerRetarget != 0 { + // The test network rules allow minimum difficulty blocks after + // more than twice the desired amount of time needed to generate + // a block has elapsed. + if b.chainParams.ResetMinDifficulty { + // Return minimum difficulty when more than twice the + // desired amount of time needed to generate a block has + // elapsed. + allowMinTime := lastNode.timestamp.Add(targetSpacing * 2) + if newBlockTime.After(allowMinTime) { + return b.chainParams.PowLimitBits, nil + } + + // The block was mined within the desired timeframe, so + // return the difficulty for the last block which did + // not have the special minimum difficulty rule applied. + prevBits, err := b.findPrevTestNetDifficulty(lastNode) + if err != nil { + return 0, err + } + return prevBits, nil + } + + // For the main network (or any unrecognized networks), simply + // return the previous block's difficulty requirements. + return lastNode.bits, nil + } + + // Get the block node at the previous retarget (targetTimespan days + // worth of blocks). + firstNode := lastNode + for i := int32(0); i < BlocksPerRetarget-1 && firstNode != nil; i++ { + // Get the previous block node. This function is used over + // simply accessing firstNode.parent directly as it will + // dynamically create previous block nodes as needed. This + // helps allow only the pieces of the chain that are needed + // to remain in memory. + var err error + firstNode, err = b.getPrevNodeFromNode(firstNode) + if err != nil { + return 0, err + } + } + + if firstNode == nil { + return 0, AssertError("unable to obtain previous retarget block") + } + + // Limit the amount of adjustment that can occur to the previous + // difficulty. + actualTimespan := lastNode.timestamp.UnixNano() - firstNode.timestamp.UnixNano() + adjustedTimespan := actualTimespan + if actualTimespan < minRetargetTimespan { + adjustedTimespan = minRetargetTimespan + } else if actualTimespan > maxRetargetTimespan { + adjustedTimespan = maxRetargetTimespan + } + + // Calculate new target difficulty as: + // currentDifficulty * (adjustedTimespan / targetTimespan) + // The result uses integer division which means it will be slightly + // rounded down. Bitcoind also uses integer division to calculate this + // result. + oldTarget := CompactToBig(lastNode.bits) + newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan)) + newTarget.Div(newTarget, big.NewInt(int64(targetTimespan))) + + // Limit new value to the proof of work limit. + if newTarget.Cmp(b.chainParams.PowLimit) > 0 { + newTarget.Set(b.chainParams.PowLimit) + } + + // Log new target difficulty and return it. The new target logging is + // intentionally converting the bits back to a number instead of using + // newTarget since conversion to the compact representation loses + // precision. + newTargetBits := BigToCompact(newTarget) + log.Debugf("Difficulty retarget at block height %d", lastNode.height+1) + log.Debugf("Old target %08x (%064x)", lastNode.bits, oldTarget) + log.Debugf("New target %08x (%064x)", newTargetBits, CompactToBig(newTargetBits)) + log.Debugf("Actual timespan %v, adjusted timespan %v, target timespan %v", + time.Duration(actualTimespan), time.Duration(adjustedTimespan), + targetTimespan) + + return newTargetBits, nil +} + +// CalcNextRequiredDifficulty calculates the required difficulty for the block +// after the end of the current best chain based on the difficulty retarget +// rules. +// +// This function is safe for concurrent access. +func (b *BlockChain) CalcNextRequiredDifficulty(timestamp time.Time) (uint32, error) { + b.chainLock.Lock() + difficulty, err := b.calcNextRequiredDifficulty(b.bestNode, timestamp) + b.chainLock.Unlock() + return difficulty, err +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/difficulty_test.go b/vendor/github.com/btcsuite/btcd/blockchain/difficulty_test.go new file mode 100644 index 0000000000000000000000000000000000000000..58fa7f855aea4bd3ef155e3499bcbb2c7d1e928b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/difficulty_test.go @@ -0,0 +1,71 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain_test + +import ( + "math/big" + "testing" + + "github.com/btcsuite/btcd/blockchain" +) + +func TestBigToCompact(t *testing.T) { + tests := []struct { + in int64 + out uint32 + }{ + {0, 0}, + {-1, 25231360}, + } + + for x, test := range tests { + n := big.NewInt(test.in) + r := blockchain.BigToCompact(n) + if r != test.out { + t.Errorf("TestBigToCompact test #%d failed: got %d want %d\n", + x, r, test.out) + return + } + } +} + +func TestCompactToBig(t *testing.T) { + tests := []struct { + in uint32 + out int64 + }{ + {10000000, 0}, + } + + for x, test := range tests { + n := blockchain.CompactToBig(test.in) + want := big.NewInt(test.out) + if n.Cmp(want) != 0 { + t.Errorf("TestCompactToBig test #%d failed: got %d want %d\n", + x, n.Int64(), want.Int64()) + return + } + } +} + +func TestCalcWork(t *testing.T) { + tests := []struct { + in uint32 + out int64 + }{ + {10000000, 0}, + } + + for x, test := range tests { + bits := uint32(test.in) + + r := blockchain.CalcWork(bits) + if r.Int64() != test.out { + t.Errorf("TestCalcWork test #%d failed: got %v want %d\n", + x, r.Int64(), test.out) + return + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/doc.go b/vendor/github.com/btcsuite/btcd/blockchain/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..244175414a20ca140997d22a74012d106bc23120 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/doc.go @@ -0,0 +1,81 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package blockchain implements bitcoin block handling and chain selection rules. + +The bitcoin block handling and chain selection rules are an integral, and quite +likely the most important, part of bitcoin. Unfortunately, at the time of +this writing, these rules are also largely undocumented and had to be +ascertained from the bitcoind source code. At its core, bitcoin is a +distributed consensus of which blocks are valid and which ones will comprise the +main block chain (public ledger) that ultimately determines accepted +transactions, so it is extremely important that fully validating nodes agree on +all rules. + +At a high level, this package provides support for inserting new blocks into +the block chain according to the aforementioned rules. It includes +functionality such as rejecting duplicate blocks, ensuring blocks and +transactions follow all rules, orphan handling, and best chain selection along +with reorganization. + +Since this package does not deal with other bitcoin specifics such as network +communication or wallets, it provides a notification system which gives the +caller a high level of flexibility in how they want to react to certain events +such as orphan blocks which need their parents requested and newly connected +main chain blocks which might result in wallet updates. + +Bitcoin Chain Processing Overview + +Before a block is allowed into the block chain, it must go through an intensive +series of validation rules. The following list serves as a general outline of +those rules to provide some intuition into what is going on under the hood, but +is by no means exhaustive: + + - Reject duplicate blocks + - Perform a series of sanity checks on the block and its transactions such as + verifying proof of work, timestamps, number and character of transactions, + transaction amounts, script complexity, and merkle root calculations + - Compare the block against predetermined checkpoints for expected timestamps + and difficulty based on elapsed time since the checkpoint + - Save the most recent orphan blocks for a limited time in case their parent + blocks become available + - Stop processing if the block is an orphan as the rest of the processing + depends on the block's position within the block chain + - Perform a series of more thorough checks that depend on the block's position + within the block chain such as verifying block difficulties adhere to + difficulty retarget rules, timestamps are after the median of the last + several blocks, all transactions are finalized, checkpoint blocks match, and + block versions are in line with the previous blocks + - Determine how the block fits into the chain and perform different actions + accordingly in order to ensure any side chains which have higher difficulty + than the main chain become the new main chain + - When a block is being connected to the main chain (either through + reorganization of a side chain to the main chain or just extending the + main chain), perform further checks on the block's transactions such as + verifying transaction duplicates, script complexity for the combination of + connected scripts, coinbase maturity, double spends, and connected + transaction values + - Run the transaction scripts to verify the spender is allowed to spend the + coins + - Insert the block into the block database + +Errors + +Errors returned by this package are either the raw errors provided by underlying +calls or of type blockchain.RuleError. This allows the caller to differentiate +between unexpected errors, such as database errors, versus errors due to rule +violations through type assertions. In addition, callers can programmatically +determine the specific rule violation by examining the ErrorCode field of the +type asserted blockchain.RuleError. + +Bitcoin Improvement Proposals + +This package includes spec changes outlined by the following BIPs: + + BIP0016 (https://en.bitcoin.it/wiki/BIP_0016) + BIP0030 (https://en.bitcoin.it/wiki/BIP_0030) + BIP0034 (https://en.bitcoin.it/wiki/BIP_0034) +*/ +package blockchain diff --git a/vendor/github.com/btcsuite/btcd/blockchain/error.go b/vendor/github.com/btcsuite/btcd/blockchain/error.go new file mode 100644 index 0000000000000000000000000000000000000000..a31123a02986e17bff51e88381d597862996918d --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/error.go @@ -0,0 +1,261 @@ +// Copyright (c) 2014-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "fmt" +) + +// AssertError identifies an error that indicates an internal code consistency +// issue and should be treated as a critical and unrecoverable error. +type AssertError string + +// Error returns the assertion error as a huma-readable string and satisfies +// the error interface. +func (e AssertError) Error() string { + return "assertion failed: " + string(e) +} + +// ErrorCode identifies a kind of error. +type ErrorCode int + +// These constants are used to identify a specific RuleError. +const ( + // ErrDuplicateBlock indicates a block with the same hash already + // exists. + ErrDuplicateBlock ErrorCode = iota + + // ErrBlockTooBig indicates the serialized block size exceeds the + // maximum allowed size. + ErrBlockTooBig + + // ErrBlockVersionTooOld indicates the block version is too old and is + // no longer accepted since the majority of the network has upgraded + // to a newer version. + ErrBlockVersionTooOld + + // ErrInvalidTime indicates the time in the passed block has a precision + // that is more than one second. The chain consensus rules require + // timestamps to have a maximum precision of one second. + ErrInvalidTime + + // ErrTimeTooOld indicates the time is either before the median time of + // the last several blocks per the chain consensus rules or prior to the + // most recent checkpoint. + ErrTimeTooOld + + // ErrTimeTooNew indicates the time is too far in the future as compared + // the current time. + ErrTimeTooNew + + // ErrDifficultyTooLow indicates the difficulty for the block is lower + // than the difficulty required by the most recent checkpoint. + ErrDifficultyTooLow + + // ErrUnexpectedDifficulty indicates specified bits do not align with + // the expected value either because it doesn't match the calculated + // valued based on difficulty regarted rules or it is out of the valid + // range. + ErrUnexpectedDifficulty + + // ErrHighHash indicates the block does not hash to a value which is + // lower than the required target difficultly. + ErrHighHash + + // ErrBadMerkleRoot indicates the calculated merkle root does not match + // the expected value. + ErrBadMerkleRoot + + // ErrBadCheckpoint indicates a block that is expected to be at a + // checkpoint height does not match the expected one. + ErrBadCheckpoint + + // ErrForkTooOld indicates a block is attempting to fork the block chain + // before the most recent checkpoint. + ErrForkTooOld + + // ErrCheckpointTimeTooOld indicates a block has a timestamp before the + // most recent checkpoint. + ErrCheckpointTimeTooOld + + // ErrNoTransactions indicates the block does not have a least one + // transaction. A valid block must have at least the coinbase + // transaction. + ErrNoTransactions + + // ErrTooManyTransactions indicates the block has more transactions than + // are allowed. + ErrTooManyTransactions + + // ErrNoTxInputs indicates a transaction does not have any inputs. A + // valid transaction must have at least one input. + ErrNoTxInputs + + // ErrNoTxOutputs indicates a transaction does not have any outputs. A + // valid transaction must have at least one output. + ErrNoTxOutputs + + // ErrTxTooBig indicates a transaction exceeds the maximum allowed size + // when serialized. + ErrTxTooBig + + // ErrBadTxOutValue indicates an output value for a transaction is + // invalid in some way such as being out of range. + ErrBadTxOutValue + + // ErrDuplicateTxInputs indicates a transaction references the same + // input more than once. + ErrDuplicateTxInputs + + // ErrBadTxInput indicates a transaction input is invalid in some way + // such as referencing a previous transaction outpoint which is out of + // range or not referencing one at all. + ErrBadTxInput + + // ErrMissingTx indicates a transaction referenced by an input is + // missing. + ErrMissingTx + + // ErrUnfinalizedTx indicates a transaction has not been finalized. + // A valid block may only contain finalized transactions. + ErrUnfinalizedTx + + // ErrDuplicateTx indicates a block contains an identical transaction + // (or at least two transactions which hash to the same value). A + // valid block may only contain unique transactions. + ErrDuplicateTx + + // ErrOverwriteTx indicates a block contains a transaction that has + // the same hash as a previous transaction which has not been fully + // spent. + ErrOverwriteTx + + // ErrImmatureSpend indicates a transaction is attempting to spend a + // coinbase that has not yet reached the required maturity. + ErrImmatureSpend + + // ErrDoubleSpend indicates a transaction is attempting to spend coins + // that have already been spent. + ErrDoubleSpend + + // ErrSpendTooHigh indicates a transaction is attempting to spend more + // value than the sum of all of its inputs. + ErrSpendTooHigh + + // ErrBadFees indicates the total fees for a block are invalid due to + // exceeding the maximum possible value. + ErrBadFees + + // ErrTooManySigOps indicates the total number of signature operations + // for a transaction or block exceed the maximum allowed limits. + ErrTooManySigOps + + // ErrFirstTxNotCoinbase indicates the first transaction in a block + // is not a coinbase transaction. + ErrFirstTxNotCoinbase + + // ErrMultipleCoinbases indicates a block contains more than one + // coinbase transaction. + ErrMultipleCoinbases + + // ErrBadCoinbaseScriptLen indicates the length of the signature script + // for a coinbase transaction is not within the valid range. + ErrBadCoinbaseScriptLen + + // ErrBadCoinbaseValue indicates the amount of a coinbase value does + // not match the expected value of the subsidy plus the sum of all fees. + ErrBadCoinbaseValue + + // ErrMissingCoinbaseHeight indicates the coinbase transaction for a + // block does not start with the serialized block block height as + // required for version 2 and higher blocks. + ErrMissingCoinbaseHeight + + // ErrBadCoinbaseHeight indicates the serialized block height in the + // coinbase transaction for version 2 and higher blocks does not match + // the expected value. + ErrBadCoinbaseHeight + + // ErrScriptMalformed indicates a transaction script is malformed in + // some way. For example, it might be longer than the maximum allowed + // length or fail to parse. + ErrScriptMalformed + + // ErrScriptValidation indicates the result of executing transaction + // script failed. The error covers any failure when executing scripts + // such signature verification failures and execution past the end of + // the stack. + ErrScriptValidation +) + +// Map of ErrorCode values back to their constant names for pretty printing. +var errorCodeStrings = map[ErrorCode]string{ + ErrDuplicateBlock: "ErrDuplicateBlock", + ErrBlockTooBig: "ErrBlockTooBig", + ErrBlockVersionTooOld: "ErrBlockVersionTooOld", + ErrInvalidTime: "ErrInvalidTime", + ErrTimeTooOld: "ErrTimeTooOld", + ErrTimeTooNew: "ErrTimeTooNew", + ErrDifficultyTooLow: "ErrDifficultyTooLow", + ErrUnexpectedDifficulty: "ErrUnexpectedDifficulty", + ErrHighHash: "ErrHighHash", + ErrBadMerkleRoot: "ErrBadMerkleRoot", + ErrBadCheckpoint: "ErrBadCheckpoint", + ErrForkTooOld: "ErrForkTooOld", + ErrCheckpointTimeTooOld: "ErrCheckpointTimeTooOld", + ErrNoTransactions: "ErrNoTransactions", + ErrTooManyTransactions: "ErrTooManyTransactions", + ErrNoTxInputs: "ErrNoTxInputs", + ErrNoTxOutputs: "ErrNoTxOutputs", + ErrTxTooBig: "ErrTxTooBig", + ErrBadTxOutValue: "ErrBadTxOutValue", + ErrDuplicateTxInputs: "ErrDuplicateTxInputs", + ErrBadTxInput: "ErrBadTxInput", + ErrMissingTx: "ErrMissingTx", + ErrUnfinalizedTx: "ErrUnfinalizedTx", + ErrDuplicateTx: "ErrDuplicateTx", + ErrOverwriteTx: "ErrOverwriteTx", + ErrImmatureSpend: "ErrImmatureSpend", + ErrDoubleSpend: "ErrDoubleSpend", + ErrSpendTooHigh: "ErrSpendTooHigh", + ErrBadFees: "ErrBadFees", + ErrTooManySigOps: "ErrTooManySigOps", + ErrFirstTxNotCoinbase: "ErrFirstTxNotCoinbase", + ErrMultipleCoinbases: "ErrMultipleCoinbases", + ErrBadCoinbaseScriptLen: "ErrBadCoinbaseScriptLen", + ErrBadCoinbaseValue: "ErrBadCoinbaseValue", + ErrMissingCoinbaseHeight: "ErrMissingCoinbaseHeight", + ErrBadCoinbaseHeight: "ErrBadCoinbaseHeight", + ErrScriptMalformed: "ErrScriptMalformed", + ErrScriptValidation: "ErrScriptValidation", +} + +// String returns the ErrorCode as a human-readable name. +func (e ErrorCode) String() string { + if s := errorCodeStrings[e]; s != "" { + return s + } + return fmt.Sprintf("Unknown ErrorCode (%d)", int(e)) +} + +// RuleError identifies a rule violation. It is used to indicate that +// processing of a block or transaction failed due to one of the many validation +// rules. The caller can use type assertions to determine if a failure was +// specifically due to a rule violation and access the ErrorCode field to +// ascertain the specific reason for the rule violation. +type RuleError struct { + ErrorCode ErrorCode // Describes the kind of error + Description string // Human readable description of the issue +} + +// Error satisfies the error interface and prints human-readable errors. +func (e RuleError) Error() string { + return e.Description +} + +// ruleError creates an RuleError given a set of arguments. +func ruleError(c ErrorCode, desc string) RuleError { + return RuleError{ErrorCode: c, Description: desc} +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/error_test.go b/vendor/github.com/btcsuite/btcd/blockchain/error_test.go new file mode 100644 index 0000000000000000000000000000000000000000..640f504c9cc2440ad399037cc5c4ea56060edf2e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/error_test.go @@ -0,0 +1,97 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain_test + +import ( + "testing" + + "github.com/btcsuite/btcd/blockchain" +) + +// TestErrorCodeStringer tests the stringized output for the ErrorCode type. +func TestErrorCodeStringer(t *testing.T) { + tests := []struct { + in blockchain.ErrorCode + want string + }{ + {blockchain.ErrDuplicateBlock, "ErrDuplicateBlock"}, + {blockchain.ErrBlockTooBig, "ErrBlockTooBig"}, + {blockchain.ErrBlockVersionTooOld, "ErrBlockVersionTooOld"}, + {blockchain.ErrInvalidTime, "ErrInvalidTime"}, + {blockchain.ErrTimeTooOld, "ErrTimeTooOld"}, + {blockchain.ErrTimeTooNew, "ErrTimeTooNew"}, + {blockchain.ErrDifficultyTooLow, "ErrDifficultyTooLow"}, + {blockchain.ErrUnexpectedDifficulty, "ErrUnexpectedDifficulty"}, + {blockchain.ErrHighHash, "ErrHighHash"}, + {blockchain.ErrBadMerkleRoot, "ErrBadMerkleRoot"}, + {blockchain.ErrBadCheckpoint, "ErrBadCheckpoint"}, + {blockchain.ErrForkTooOld, "ErrForkTooOld"}, + {blockchain.ErrCheckpointTimeTooOld, "ErrCheckpointTimeTooOld"}, + {blockchain.ErrNoTransactions, "ErrNoTransactions"}, + {blockchain.ErrTooManyTransactions, "ErrTooManyTransactions"}, + {blockchain.ErrNoTxInputs, "ErrNoTxInputs"}, + {blockchain.ErrNoTxOutputs, "ErrNoTxOutputs"}, + {blockchain.ErrTxTooBig, "ErrTxTooBig"}, + {blockchain.ErrBadTxOutValue, "ErrBadTxOutValue"}, + {blockchain.ErrDuplicateTxInputs, "ErrDuplicateTxInputs"}, + {blockchain.ErrBadTxInput, "ErrBadTxInput"}, + {blockchain.ErrBadCheckpoint, "ErrBadCheckpoint"}, + {blockchain.ErrMissingTx, "ErrMissingTx"}, + {blockchain.ErrUnfinalizedTx, "ErrUnfinalizedTx"}, + {blockchain.ErrDuplicateTx, "ErrDuplicateTx"}, + {blockchain.ErrOverwriteTx, "ErrOverwriteTx"}, + {blockchain.ErrImmatureSpend, "ErrImmatureSpend"}, + {blockchain.ErrDoubleSpend, "ErrDoubleSpend"}, + {blockchain.ErrSpendTooHigh, "ErrSpendTooHigh"}, + {blockchain.ErrBadFees, "ErrBadFees"}, + {blockchain.ErrTooManySigOps, "ErrTooManySigOps"}, + {blockchain.ErrFirstTxNotCoinbase, "ErrFirstTxNotCoinbase"}, + {blockchain.ErrMultipleCoinbases, "ErrMultipleCoinbases"}, + {blockchain.ErrBadCoinbaseScriptLen, "ErrBadCoinbaseScriptLen"}, + {blockchain.ErrBadCoinbaseValue, "ErrBadCoinbaseValue"}, + {blockchain.ErrMissingCoinbaseHeight, "ErrMissingCoinbaseHeight"}, + {blockchain.ErrBadCoinbaseHeight, "ErrBadCoinbaseHeight"}, + {blockchain.ErrScriptMalformed, "ErrScriptMalformed"}, + {blockchain.ErrScriptValidation, "ErrScriptValidation"}, + {0xffff, "Unknown ErrorCode (65535)"}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.in.String() + if result != test.want { + t.Errorf("String #%d\n got: %s want: %s", i, result, + test.want) + continue + } + } +} + +// TestRuleError tests the error output for the RuleError type. +func TestRuleError(t *testing.T) { + tests := []struct { + in blockchain.RuleError + want string + }{ + { + blockchain.RuleError{Description: "duplicate block"}, + "duplicate block", + }, + { + blockchain.RuleError{Description: "human-readable error"}, + "human-readable error", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.in.Error() + if result != test.want { + t.Errorf("Error #%d\n got: %s want: %s", i, result, + test.want) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/example_test.go b/vendor/github.com/btcsuite/btcd/blockchain/example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..aba595570b0b2729319b284712ffd6fb13f893c8 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/example_test.go @@ -0,0 +1,107 @@ +// Copyright (c) 2014-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain_test + +import ( + "fmt" + "math/big" + "os" + "path/filepath" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + _ "github.com/btcsuite/btcd/database/ffldb" + "github.com/btcsuite/btcutil" +) + +// This example demonstrates how to create a new chain instance and use +// ProcessBlock to attempt to attempt add a block to the chain. As the package +// overview documentation describes, this includes all of the Bitcoin consensus +// rules. This example intentionally attempts to insert a duplicate genesis +// block to illustrate how an invalid block is handled. +func ExampleBlockChain_ProcessBlock() { + // Create a new database to store the accepted blocks into. Typically + // this would be opening an existing database and would not be deleting + // and creating a new database like this, but it is done here so this is + // a complete working example and does not leave temporary files laying + // around. + dbPath := filepath.Join(os.TempDir(), "exampleprocessblock") + _ = os.RemoveAll(dbPath) + db, err := database.Create("ffldb", dbPath, chaincfg.MainNetParams.Net) + if err != nil { + fmt.Printf("Failed to create database: %v\n", err) + return + } + defer os.RemoveAll(dbPath) + defer db.Close() + + // Create a new BlockChain instance using the underlying database for + // the main bitcoin network. This example does not demonstrate some + // of the other available configuration options such as specifying a + // notification callback and signature cache. + chain, err := blockchain.New(&blockchain.Config{ + DB: db, + ChainParams: &chaincfg.MainNetParams, + }) + if err != nil { + fmt.Printf("Failed to create chain instance: %v\n", err) + return + } + + // Create a new median time source that is required by the upcoming + // call to ProcessBlock. Ordinarily this would also add time values + // obtained from other peers on the network so the local time is + // adjusted to be in agreement with other peers. + timeSource := blockchain.NewMedianTime() + + // Process a block. For this example, we are going to intentionally + // cause an error by trying to process the genesis block which already + // exists. + genesisBlock := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) + isOrphan, err := chain.ProcessBlock(genesisBlock, timeSource, blockchain.BFNone) + if err != nil { + fmt.Printf("Failed to process block: %v\n", err) + return + } + fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan) + + // Output: + // Failed to process block: already have block 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f +} + +// This example demonstrates how to convert the compact "bits" in a block header +// which represent the target difficulty to a big integer and display it using +// the typical hex notation. +func ExampleCompactToBig() { + // Convert the bits from block 300000 in the main block chain. + bits := uint32(419465580) + targetDifficulty := blockchain.CompactToBig(bits) + + // Display it in hex. + fmt.Printf("%064x\n", targetDifficulty.Bytes()) + + // Output: + // 0000000000000000896c00000000000000000000000000000000000000000000 +} + +// This example demonstrates how to convert a target difficulty into the compact +// "bits" in a block header which represent that target difficulty . +func ExampleBigToCompact() { + // Convert the target difficulty from block 300000 in the main block + // chain to compact form. + t := "0000000000000000896c00000000000000000000000000000000000000000000" + targetDifficulty, success := new(big.Int).SetString(t, 16) + if !success { + fmt.Println("invalid target difficulty") + return + } + bits := blockchain.BigToCompact(targetDifficulty) + + fmt.Println(bits) + + // Output: + // 419465580 +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/indexers/README.md b/vendor/github.com/btcsuite/btcd/blockchain/indexers/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8ec12e2b7b24006a97f26c5c200e638a3a5f6c0e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/indexers/README.md @@ -0,0 +1,44 @@ +indexers +======== + +[] +(https://travis-ci.org/btcsuite/btcd) + +Package indexers implements optional block chain indexes. + +These indexes are typically used to enhance the amount of information available +via an RPC interface. + +## Supported Indexers + +- Transaction-by-hash (txbyhashidx) Index + - Creates a mapping from the hash of each transaction to the block that + contains it along with its offset and length within the serialized block +- Transaction-by-address (txbyaddridx) Index + - Creates a mapping from every address to all transactions which either credit + or debit the address + - Requires the transaction-by-hash index + +## Documentation + +[] +(http://godoc.org/github.com/btcsuite/btcd/blockchain/indexers) + +Full `go doc` style documentation for the project can be viewed online without +installing this package by using the GoDoc site here: +http://godoc.org/github.com/btcsuite/btcd/blockchain/indexers + +You can also view the documentation locally once the package is installed with +the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to +http://localhost:6060/pkg/github.com/btcsuite/btcd/blockchain/indexers + +## Installation + +```bash +$ go get -u github.com/btcsuite/btcd/blockchain/indexers +``` + +## License + +Package indexers is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/vendor/github.com/btcsuite/btcd/blockchain/indexers/addrindex.go b/vendor/github.com/btcsuite/btcd/blockchain/indexers/addrindex.go new file mode 100644 index 0000000000000000000000000000000000000000..230d65026d968f3f46556e93fc39ea5104e510b5 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/indexers/addrindex.go @@ -0,0 +1,932 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package indexers + +import ( + "errors" + "fmt" + "sync" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +const ( + // addrIndexName is the human-readable name for the index. + addrIndexName = "address index" + + // level0MaxEntries is the maximum number of transactions that are + // stored in level 0 of an address index entry. Subsequent levels store + // 2^n * level0MaxEntries entries, or in words, double the maximum of + // the previous level. + level0MaxEntries = 8 + + // addrKeySize is the number of bytes an address key consumes in the + // index. It consists of 1 byte address type + 20 bytes hash160. + addrKeySize = 1 + 20 + + // levelKeySize is the number of bytes a level key in the address index + // consumes. It consists of the address key + 1 byte for the level. + levelKeySize = addrKeySize + 1 + + // levelOffset is the offset in the level key which identifes the level. + levelOffset = levelKeySize - 1 + + // addrKeyTypePubKeyHash is the address type in an address key which + // represents both a pay-to-pubkey-hash and a pay-to-pubkey address. + // This is done because both are identical for the purposes of the + // address index. + addrKeyTypePubKeyHash = 0 + + // addrKeyTypeScriptHash is the address type in an address key which + // represents a pay-to-script-hash address. This is necessary because + // the hash of a pubkey address might be the same as that of a script + // hash. + addrKeyTypeScriptHash = 1 + + // Size of a transaction entry. It consists of 4 bytes block id + 4 + // bytes offset + 4 bytes length. + txEntrySize = 4 + 4 + 4 +) + +var ( + // addrIndexKey is the key of the address index and the db bucket used + // to house it. + addrIndexKey = []byte("txbyaddridx") + + // errUnsupportedAddressType is an error that is used to signal an + // unsupported address type has been used. + errUnsupportedAddressType = errors.New("address type is not supported " + + "by the address index") +) + +// ----------------------------------------------------------------------------- +// The address index maps addresses referenced in the blockchain to a list of +// all the transactions involving that address. Transactions are stored +// according to their order of appearance in the blockchain. That is to say +// first by block height and then by offset inside the block. It is also +// important to note that this implementation requires the transaction index +// since it is needed in order to catch up old blocks due to the fact the spent +// outputs will already be pruned from the utxo set. +// +// The approach used to store the index is similar to a log-structured merge +// tree (LSM tree) and is thus similar to how leveldb works internally. +// +// Every address consists of one or more entries identified by a level starting +// from 0 where each level holds a maximum number of entries such that each +// subsequent level holds double the maximum of the previous one. In equation +// form, the number of entries each level holds is 2^n * firstLevelMaxSize. +// +// New transactions are appended to level 0 until it becomes full at which point +// the entire level 0 entry is appended to the level 1 entry and level 0 is +// cleared. This process continues until level 1 becomes full at which point it +// will be appended to level 2 and cleared and so on. +// +// The result of this is the lower levels contain newer transactions and the +// transactions within each level are ordered from oldest to newest. +// +// The intent of this approach is to provide a balance between space efficiency +// and indexing cost. Storing one entry per transaction would have the lowest +// indexing cost, but would waste a lot of space because the same address hash +// would be duplicated for every transaction key. On the other hand, storing a +// single entry with all transactions would be the most space efficient, but +// would cause indexing cost to grow quadratically with the number of +// transactions involving the same address. The approach used here provides +// logarithmic insertion and retrieval. +// +// The serialized key format is: +// +// <addr type><addr hash><level> +// +// Field Type Size +// addr type uint8 1 byte +// addr hash hash160 20 bytes +// level uint8 1 byte +// ----- +// Total: 22 bytes +// +// The serialized value format is: +// +// [<block id><start offset><tx length>,...] +// +// Field Type Size +// block id uint32 4 bytes +// start offset uint32 4 bytes +// tx length uint32 4 bytes +// ----- +// Total: 12 bytes per indexed tx +// ----------------------------------------------------------------------------- + +// fetchBlockHashFunc defines a callback function to use in order to convert a +// serialized block ID to an associated block hash. +type fetchBlockHashFunc func(serializedID []byte) (*wire.ShaHash, error) + +// serializeAddrIndexEntry serializes the provided block id and transaction +// location according to the format described in detail above. +func serializeAddrIndexEntry(blockID uint32, txLoc wire.TxLoc) []byte { + // Serialize the entry. + serialized := make([]byte, 12) + byteOrder.PutUint32(serialized, blockID) + byteOrder.PutUint32(serialized[4:], uint32(txLoc.TxStart)) + byteOrder.PutUint32(serialized[8:], uint32(txLoc.TxLen)) + return serialized +} + +// deserializeAddrIndexEntry decodes the passed serialized byte slice into the +// provided region struct according to the format described in detail above and +// uses the passed block hash fetching function in order to conver the block ID +// to the associated block hash. +func deserializeAddrIndexEntry(serialized []byte, region *database.BlockRegion, fetchBlockHash fetchBlockHashFunc) error { + // Ensure there are enough bytes to decode. + if len(serialized) < txEntrySize { + return errDeserialize("unexpected end of data") + } + + hash, err := fetchBlockHash(serialized[0:4]) + if err != nil { + return err + } + region.Hash = hash + region.Offset = byteOrder.Uint32(serialized[4:8]) + region.Len = byteOrder.Uint32(serialized[8:12]) + return nil +} + +// keyForLevel returns the key for a specific address and level in the address +// index entry. +func keyForLevel(addrKey [addrKeySize]byte, level uint8) [levelKeySize]byte { + var key [levelKeySize]byte + copy(key[:], addrKey[:]) + key[levelOffset] = level + return key +} + +// dbPutAddrIndexEntry updates the address index to include the provided entry +// according to the level-based scheme described in detail above. +func dbPutAddrIndexEntry(bucket internalBucket, addrKey [addrKeySize]byte, blockID uint32, txLoc wire.TxLoc) error { + // Start with level 0 and its initial max number of entries. + curLevel := uint8(0) + maxLevelBytes := level0MaxEntries * txEntrySize + + // Simply append the new entry to level 0 and return now when it will + // fit. This is the most common path. + newData := serializeAddrIndexEntry(blockID, txLoc) + level0Key := keyForLevel(addrKey, 0) + level0Data := bucket.Get(level0Key[:]) + if len(level0Data)+len(newData) <= maxLevelBytes { + mergedData := newData + if len(level0Data) > 0 { + mergedData = make([]byte, len(level0Data)+len(newData)) + copy(mergedData, level0Data) + copy(mergedData[len(level0Data):], newData) + } + return bucket.Put(level0Key[:], mergedData) + } + + // At this point, level 0 is full, so merge each level into higher + // levels as many times as needed to free up level 0. + prevLevelData := level0Data + for { + // Each new level holds twice as much as the previous one. + curLevel++ + maxLevelBytes *= 2 + + // Move to the next level as long as the current level is full. + curLevelKey := keyForLevel(addrKey, curLevel) + curLevelData := bucket.Get(curLevelKey[:]) + if len(curLevelData) == maxLevelBytes { + prevLevelData = curLevelData + continue + } + + // The current level has room for the data in the previous one, + // so merge the data from previous level into it. + mergedData := prevLevelData + if len(curLevelData) > 0 { + mergedData = make([]byte, len(curLevelData)+ + len(prevLevelData)) + copy(mergedData, curLevelData) + copy(mergedData[len(curLevelData):], prevLevelData) + } + err := bucket.Put(curLevelKey[:], mergedData) + if err != nil { + return err + } + + // Move all of the levels before the previous one up a level. + for mergeLevel := curLevel - 1; mergeLevel > 0; mergeLevel-- { + mergeLevelKey := keyForLevel(addrKey, mergeLevel) + prevLevelKey := keyForLevel(addrKey, mergeLevel-1) + prevData := bucket.Get(prevLevelKey[:]) + err := bucket.Put(mergeLevelKey[:], prevData) + if err != nil { + return err + } + } + break + } + + // Finally, insert the new entry into level 0 now that it is empty. + return bucket.Put(level0Key[:], newData) +} + +// dbFetchAddrIndexEntries returns block regions for transactions referenced by +// the given address key and the number of entries skipped since it could have +// been less in the case where there are less total entries than the requested +// number of entries to skip. +func dbFetchAddrIndexEntries(bucket internalBucket, addrKey [addrKeySize]byte, numToSkip, numRequested uint32, reverse bool, fetchBlockHash fetchBlockHashFunc) ([]database.BlockRegion, uint32, error) { + // When the reverse flag is not set, all levels need to be fetched + // because numToSkip and numRequested are counted from the oldest + // transactions (highest level) and thus the total count is needed. + // However, when the reverse flag is set, only enough records to satisfy + // the requested amount are needed. + var level uint8 + var serialized []byte + for !reverse || len(serialized) < int(numToSkip+numRequested)*txEntrySize { + curLevelKey := keyForLevel(addrKey, level) + levelData := bucket.Get(curLevelKey[:]) + if levelData == nil { + // Stop when there are no more levels. + break + } + + // Higher levels contain older transactions, so prepend them. + prepended := make([]byte, len(serialized)+len(levelData)) + copy(prepended, levelData) + copy(prepended[len(levelData):], serialized) + serialized = prepended + level++ + } + + // When the requested number of entries to skip is larger than the + // number available, skip them all and return now with the actual number + // skipped. + numEntries := uint32(len(serialized) / txEntrySize) + if numToSkip >= numEntries { + return nil, numEntries, nil + } + + // Nothing more to do when there are no requested entries. + if numRequested == 0 { + return nil, numToSkip, nil + } + + // Limit the number to load based on the number of available entries, + // the number to skip, and the number requested. + numToLoad := numEntries - numToSkip + if numToLoad > numRequested { + numToLoad = numRequested + } + + // Start the offset after all skipped entries and load the calculated + // number. + results := make([]database.BlockRegion, numToLoad) + for i := uint32(0); i < numToLoad; i++ { + // Calculate the read offset according to the reverse flag. + var offset uint32 + if reverse { + offset = (numEntries - numToSkip - i - 1) * txEntrySize + } else { + offset = (numToSkip + i) * txEntrySize + } + + // Deserialize and populate the result. + err := deserializeAddrIndexEntry(serialized[offset:], + &results[i], fetchBlockHash) + if err != nil { + // Ensure any deserialization errors are returned as + // database corruption errors. + if isDeserializeErr(err) { + err = database.Error{ + ErrorCode: database.ErrCorruption, + Description: fmt.Sprintf("failed to "+ + "deserialized address index "+ + "for key %x: %v", addrKey, err), + } + } + + return nil, 0, err + } + } + + return results, numToSkip, nil +} + +// minEntriesToReachLevel returns the minimum number of entries that are +// required to reach the given address index level. +func minEntriesToReachLevel(level uint8) int { + maxEntriesForLevel := level0MaxEntries + minRequired := 1 + for l := uint8(1); l <= level; l++ { + minRequired += maxEntriesForLevel + maxEntriesForLevel *= 2 + } + return minRequired +} + +// maxEntriesForLevel returns the maximum number of entries allowed for the +// given address index level. +func maxEntriesForLevel(level uint8) int { + numEntries := level0MaxEntries + for l := level; l > 0; l-- { + numEntries *= 2 + } + return numEntries +} + +// dbRemoveAddrIndexEntries removes the specified number of entries from from +// the address index for the provided key. An assertion error will be returned +// if the count exceeds the total number of entries in the index. +func dbRemoveAddrIndexEntries(bucket internalBucket, addrKey [addrKeySize]byte, count int) error { + // Nothing to do if no entries are being deleted. + if count <= 0 { + return nil + } + + // Make use of a local map to track pending updates and define a closure + // to apply it to the database. This is done in order to reduce the + // number of database reads and because there is more than one exit + // path that needs to apply the updates. + pendingUpdates := make(map[uint8][]byte) + applyPending := func() error { + for level, data := range pendingUpdates { + curLevelKey := keyForLevel(addrKey, level) + if len(data) == 0 { + err := bucket.Delete(curLevelKey[:]) + if err != nil { + return err + } + continue + } + err := bucket.Put(curLevelKey[:], data) + if err != nil { + return err + } + } + return nil + } + + // Loop fowards through the levels while removing entries until the + // specified number has been removed. This will potentially result in + // entirely empty lower levels which will be backfilled below. + var highestLoadedLevel uint8 + numRemaining := count + for level := uint8(0); numRemaining > 0; level++ { + // Load the data for the level from the database. + curLevelKey := keyForLevel(addrKey, level) + curLevelData := bucket.Get(curLevelKey[:]) + if len(curLevelData) == 0 && numRemaining > 0 { + return AssertError(fmt.Sprintf("dbRemoveAddrIndexEntries "+ + "not enough entries for address key %x to "+ + "delete %d entries", addrKey, count)) + } + pendingUpdates[level] = curLevelData + highestLoadedLevel = level + + // Delete the entire level as needed. + numEntries := len(curLevelData) / txEntrySize + if numRemaining >= numEntries { + pendingUpdates[level] = nil + numRemaining -= numEntries + continue + } + + // Remove remaining entries to delete from the level. + offsetEnd := len(curLevelData) - (numRemaining * txEntrySize) + pendingUpdates[level] = curLevelData[:offsetEnd] + break + } + + // When all elements in level 0 were not removed there is nothing left + // to do other than updating the database. + if len(pendingUpdates[0]) != 0 { + return applyPending() + } + + // At this point there are one or more empty levels before the current + // level which need to be backfilled and the current level might have + // had some entries deleted from it as well. Since all levels after + // level 0 are required to either be empty, half full, or completely + // full, the current level must be adjusted accordingly by backfilling + // each previous levels in a way which satisfies the requirements. Any + // entries that are left are assigned to level 0 after the loop as they + // are guaranteed to fit by the logic in the loop. In other words, this + // effectively squashes all remaining entries in the current level into + // the lowest possible levels while following the level rules. + // + // Note that the level after the current level might also have entries + // and gaps are not allowed, so this also keeps track of the lowest + // empty level so the code below knows how far to backfill in case it is + // required. + lowestEmptyLevel := uint8(255) + curLevelData := pendingUpdates[highestLoadedLevel] + curLevelMaxEntries := maxEntriesForLevel(highestLoadedLevel) + for level := highestLoadedLevel; level > 0; level-- { + // When there are not enough entries left in the current level + // for the number that would be required to reach it, clear the + // the current level which effectively moves them all up to the + // previous level on the next iteration. Otherwise, there are + // are sufficient entries, so update the current level to + // contain as many entries as possible while still leaving + // enough remaining entries required to reach the level. + numEntries := len(curLevelData) / txEntrySize + prevLevelMaxEntries := curLevelMaxEntries / 2 + minPrevRequired := minEntriesToReachLevel(level - 1) + if numEntries < prevLevelMaxEntries+minPrevRequired { + lowestEmptyLevel = level + pendingUpdates[level] = nil + } else { + // This level can only be completely full or half full, + // so choose the appropriate offset to ensure enough + // entries remain to reach the level. + var offset int + if numEntries-curLevelMaxEntries >= minPrevRequired { + offset = curLevelMaxEntries * txEntrySize + } else { + offset = prevLevelMaxEntries * txEntrySize + } + pendingUpdates[level] = curLevelData[:offset] + curLevelData = curLevelData[offset:] + } + + curLevelMaxEntries = prevLevelMaxEntries + } + pendingUpdates[0] = curLevelData + if len(curLevelData) == 0 { + lowestEmptyLevel = 0 + } + + // When the highest loaded level is empty, it's possible the level after + // it still has data and thus that data needs to be backfilled as well. + for len(pendingUpdates[highestLoadedLevel]) == 0 { + // When the next level is empty too, the is no data left to + // continue backfilling, so there is nothing left to do. + // Otherwise, populate the pending updates map with the newly + // loaded data and update the highest loaded level accordingly. + level := highestLoadedLevel + 1 + curLevelKey := keyForLevel(addrKey, level) + levelData := bucket.Get(curLevelKey[:]) + if len(levelData) == 0 { + break + } + pendingUpdates[level] = levelData + highestLoadedLevel = level + + // At this point the highest level is not empty, but it might + // be half full. When that is the case, move it up a level to + // simplify the code below which backfills all lower levels that + // are still empty. This also means the current level will be + // empty, so the loop will perform another another iteration to + // potentially backfill this level with data from the next one. + curLevelMaxEntries := maxEntriesForLevel(level) + if len(levelData)/txEntrySize != curLevelMaxEntries { + pendingUpdates[level] = nil + pendingUpdates[level-1] = levelData + level-- + curLevelMaxEntries /= 2 + } + + // Backfill all lower levels that are still empty by iteratively + // halfing the data until the lowest empty level is filled. + for level > lowestEmptyLevel { + offset := (curLevelMaxEntries / 2) * txEntrySize + pendingUpdates[level] = levelData[:offset] + levelData = levelData[offset:] + pendingUpdates[level-1] = levelData + level-- + curLevelMaxEntries /= 2 + } + + // The lowest possible empty level is now the highest loaded + // level. + lowestEmptyLevel = highestLoadedLevel + } + + // Apply the pending updates. + return applyPending() +} + +// addrToKey converts known address types to an addrindex key. An error is +// returned for unsupported types. +func addrToKey(addr btcutil.Address) ([addrKeySize]byte, error) { + switch addr := addr.(type) { + case *btcutil.AddressPubKeyHash: + var result [addrKeySize]byte + result[0] = addrKeyTypePubKeyHash + copy(result[1:], addr.Hash160()[:]) + return result, nil + + case *btcutil.AddressScriptHash: + var result [addrKeySize]byte + result[0] = addrKeyTypeScriptHash + copy(result[1:], addr.Hash160()[:]) + return result, nil + + case *btcutil.AddressPubKey: + var result [addrKeySize]byte + result[0] = addrKeyTypePubKeyHash + copy(result[1:], addr.AddressPubKeyHash().Hash160()[:]) + return result, nil + } + + return [addrKeySize]byte{}, errUnsupportedAddressType +} + +// AddrIndex implements a transaction by address index. That is to say, it +// supports querying all transactions that reference a given address because +// they are either crediting or debiting the address. The returned transactions +// are ordered according to their order of appearance in the blockchain. In +// other words, first by block height and then by offset inside the block. +// +// In addition, support is provided for a memory-only index of unconfirmed +// transactions such as those which are kept in the memory pool before inclusion +// in a block. +type AddrIndex struct { + // The following fields are set when the instance is created and can't + // be changed afterwards, so there is no need to protect them with a + // separate mutex. + db database.DB + chainParams *chaincfg.Params + + // The following fields are used to quickly link transactions and + // addresses that have not been included into a block yet when an + // address index is being maintained. The are protected by the + // unconfirmedLock field. + // + // The txnsByAddr field is used to keep an index of all transactions + // which either create an output to a given address or spend from a + // previous output to it keyed by the address. + // + // The addrsByTx field is essentially the reverse and is used to + // keep an index of all addresses which a given transaction involves. + // This allows fairly efficient updates when transactions are removed + // once they are included into a block. + unconfirmedLock sync.RWMutex + txnsByAddr map[[addrKeySize]byte]map[wire.ShaHash]*btcutil.Tx + addrsByTx map[wire.ShaHash]map[[addrKeySize]byte]struct{} +} + +// Ensure the AddrIndex type implements the Indexer interface. +var _ Indexer = (*AddrIndex)(nil) + +// Ensure the AddrIndex type implements the NeedsInputser interface. +var _ NeedsInputser = (*AddrIndex)(nil) + +// NeedsInputs signals that the index requires the referenced inputs in order +// to properly create the index. +// +// This implements the NeedsInputser interface. +func (idx *AddrIndex) NeedsInputs() bool { + return true +} + +// Init is only provided to satisfy the Indexer interface as there is nothing to +// initialize for this index. +// +// This is part of the Indexer interface. +func (idx *AddrIndex) Init() error { + // Nothing to do. + return nil +} + +// Key returns the database key to use for the index as a byte slice. +// +// This is part of the Indexer interface. +func (idx *AddrIndex) Key() []byte { + return addrIndexKey +} + +// Name returns the human-readable name of the index. +// +// This is part of the Indexer interface. +func (idx *AddrIndex) Name() string { + return addrIndexName +} + +// Create is invoked when the indexer manager determines the index needs +// to be created for the first time. It creates the bucket for the address +// index. +// +// This is part of the Indexer interface. +func (idx *AddrIndex) Create(dbTx database.Tx) error { + _, err := dbTx.Metadata().CreateBucket(addrIndexKey) + return err +} + +// writeIndexData represents the address index data to be written for one block. +// It consistens of the address mapped to an ordered list of the transactions +// that involve the address in block. It is ordered so the transactions can be +// stored in the order they appear in the block. +type writeIndexData map[[addrKeySize]byte][]int + +// indexPkScript extracts all standard addresses from the passed public key +// script and maps each of them to the associated transaction using the passed +// map. +func (idx *AddrIndex) indexPkScript(data writeIndexData, pkScript []byte, txIdx int) { + // Nothing to index if the script is non-standard or otherwise doesn't + // contain any addresses. + _, addrs, _, err := txscript.ExtractPkScriptAddrs(pkScript, + idx.chainParams) + if err != nil || len(addrs) == 0 { + return + } + + for _, addr := range addrs { + addrKey, err := addrToKey(addr) + if err != nil { + // Ignore unsupported address types. + continue + } + + // Avoid inserting the transaction more than once. Since the + // transactions are indexed serially any duplicates will be + // indexed in a row, so checking the most recent entry for the + // address is enough to detect duplicates. + indexedTxns := data[addrKey] + numTxns := len(indexedTxns) + if numTxns > 0 && indexedTxns[numTxns-1] == txIdx { + continue + } + indexedTxns = append(indexedTxns, txIdx) + data[addrKey] = indexedTxns + } +} + +// indexBlock extract all of the standard addresses from all of the transactions +// in the passed block and maps each of them to the assocaited transaction using +// the passed map. +func (idx *AddrIndex) indexBlock(data writeIndexData, block *btcutil.Block, view *blockchain.UtxoViewpoint) { + for txIdx, tx := range block.Transactions() { + // Coinbases do not reference any inputs. Since the block is + // required to have already gone through full validation, it has + // already been proven on the first transaction in the block is + // a coinbase. + if txIdx != 0 { + for _, txIn := range tx.MsgTx().TxIn { + // The view should always have the input since + // the index contract requires it, however, be + // safe and simply ignore any missing entries. + origin := &txIn.PreviousOutPoint + entry := view.LookupEntry(&origin.Hash) + if entry == nil { + continue + } + + pkScript := entry.PkScriptByIndex(origin.Index) + idx.indexPkScript(data, pkScript, txIdx) + } + } + + for _, txOut := range tx.MsgTx().TxOut { + idx.indexPkScript(data, txOut.PkScript, txIdx) + } + } +} + +// ConnectBlock is invoked by the index manager when a new block has been +// connected to the main chain. This indexer adds a mapping for each address +// the transactions in the block involve. +// +// This is part of the Indexer interface. +func (idx *AddrIndex) ConnectBlock(dbTx database.Tx, block *btcutil.Block, view *blockchain.UtxoViewpoint) error { + // The offset and length of the transactions within the serialized + // block. + txLocs, err := block.TxLoc() + if err != nil { + return err + } + + // Get the internal block ID associated with the block. + blockID, err := dbFetchBlockIDByHash(dbTx, block.Sha()) + if err != nil { + return err + } + + // Build all of the address to transaction mappings in a local map. + addrsToTxns := make(writeIndexData) + idx.indexBlock(addrsToTxns, block, view) + + // Add all of the index entries for each address. + addrIdxBucket := dbTx.Metadata().Bucket(addrIndexKey) + for addrKey, txIdxs := range addrsToTxns { + for _, txIdx := range txIdxs { + err := dbPutAddrIndexEntry(addrIdxBucket, addrKey, + blockID, txLocs[txIdx]) + if err != nil { + return err + } + } + } + + return nil +} + +// DisconnectBlock is invoked by the index manager when a block has been +// disconnected from the main chain. This indexer removes the address mappings +// each transaction in the block involve. +// +// This is part of the Indexer interface. +func (idx *AddrIndex) DisconnectBlock(dbTx database.Tx, block *btcutil.Block, view *blockchain.UtxoViewpoint) error { + // Build all of the address to transaction mappings in a local map. + addrsToTxns := make(writeIndexData) + idx.indexBlock(addrsToTxns, block, view) + + // Remove all of the index entries for each address. + bucket := dbTx.Metadata().Bucket(addrIndexKey) + for addrKey, txIdxs := range addrsToTxns { + err := dbRemoveAddrIndexEntries(bucket, addrKey, len(txIdxs)) + if err != nil { + return err + } + } + + return nil +} + +// TxRegionsForAddress returns a slice of block regions which identify each +// transaction that involves the passed address according to the specified +// number to skip, number requested, and whether or not the results should be +// reversed. It also returns the number actually skipped since it could be less +// in the case where there are not enough entries. +// +// NOTE: These results only include transactions confirmed in blocks. See the +// UnconfirmedTxnsForAddress method for obtaining unconfirmed transactions +// that involve a given address. +// +// This function is safe for concurrent access. +func (idx *AddrIndex) TxRegionsForAddress(dbTx database.Tx, addr btcutil.Address, numToSkip, numRequested uint32, reverse bool) ([]database.BlockRegion, uint32, error) { + addrKey, err := addrToKey(addr) + if err != nil { + return nil, 0, err + } + + var regions []database.BlockRegion + var skipped uint32 + err = idx.db.View(func(dbTx database.Tx) error { + // Create closure to lookup the block hash given the ID using + // the database transaction. + fetchBlockHash := func(id []byte) (*wire.ShaHash, error) { + // Deserialize and populate the result. + return dbFetchBlockHashBySerializedID(dbTx, id) + } + + var err error + addrIdxBucket := dbTx.Metadata().Bucket(addrIndexKey) + regions, skipped, err = dbFetchAddrIndexEntries(addrIdxBucket, + addrKey, numToSkip, numRequested, reverse, + fetchBlockHash) + return err + }) + + return regions, skipped, err +} + +// indexUnconfirmedAddresses modifies the unconfirmed (memory-only) address +// index to include mappings for the addresses encoded by the passed public key +// script to the transaction. +// +// This function is safe for concurrent access. +func (idx *AddrIndex) indexUnconfirmedAddresses(pkScript []byte, tx *btcutil.Tx) { + // The error is ignored here since the only reason it can fail is if the + // script fails to parse and it was already validated before being + // admitted to the mempool. + _, addresses, _, _ := txscript.ExtractPkScriptAddrs(pkScript, + idx.chainParams) + for _, addr := range addresses { + // Ignore unsupported address types. + addrKey, err := addrToKey(addr) + if err != nil { + continue + } + + // Add a mapping from the address to the transaction. + idx.unconfirmedLock.Lock() + addrIndexEntry := idx.txnsByAddr[addrKey] + if addrIndexEntry == nil { + addrIndexEntry = make(map[wire.ShaHash]*btcutil.Tx) + idx.txnsByAddr[addrKey] = addrIndexEntry + } + addrIndexEntry[*tx.Sha()] = tx + + // Add a mapping from the transaction to the address. + addrsByTxEntry := idx.addrsByTx[*tx.Sha()] + if addrsByTxEntry == nil { + addrsByTxEntry = make(map[[addrKeySize]byte]struct{}) + idx.addrsByTx[*tx.Sha()] = addrsByTxEntry + } + addrsByTxEntry[addrKey] = struct{}{} + idx.unconfirmedLock.Unlock() + } +} + +// AddUnconfirmedTx adds all addresses related to the transaction to the +// unconfirmed (memory-only) address index. +// +// NOTE: This transaction MUST have already been validated by the memory pool +// before calling this function with it and have all of the inputs available in +// the provided utxo view. Failure to do so could result in some or all +// addresses not being indexed. +// +// This function is safe for concurrent access. +func (idx *AddrIndex) AddUnconfirmedTx(tx *btcutil.Tx, utxoView *blockchain.UtxoViewpoint) { + // Index addresses of all referenced previous transaction outputs. + // + // The existence checks are elided since this is only called after the + // transaction has already been validated and thus all inputs are + // already known to exist. + for _, txIn := range tx.MsgTx().TxIn { + entry := utxoView.LookupEntry(&txIn.PreviousOutPoint.Hash) + if entry == nil { + // Ignore missing entries. This should never happen + // in practice since the function comments specifically + // call out all inputs must be available. + continue + } + pkScript := entry.PkScriptByIndex(txIn.PreviousOutPoint.Index) + idx.indexUnconfirmedAddresses(pkScript, tx) + } + + // Index addresses of all created outputs. + for _, txOut := range tx.MsgTx().TxOut { + idx.indexUnconfirmedAddresses(txOut.PkScript, tx) + } +} + +// RemoveUnconfirmedTx removes the passed transaction from the unconfirmed +// (memory-only) address index. +// +// This function is safe for concurrent access. +func (idx *AddrIndex) RemoveUnconfirmedTx(hash *wire.ShaHash) { + idx.unconfirmedLock.Lock() + defer idx.unconfirmedLock.Unlock() + + // Remove all address references to the transaction from the address + // index and remove the entry for the address altogether if it no longer + // references any transactions. + for addrKey := range idx.addrsByTx[*hash] { + delete(idx.txnsByAddr[addrKey], *hash) + if len(idx.txnsByAddr[addrKey]) == 0 { + delete(idx.txnsByAddr, addrKey) + } + } + + // Remove the entry from the transaction to address lookup map as well. + delete(idx.addrsByTx, *hash) +} + +// UnconfirmedTxnsForAddress returns all transactions currently in the +// unconfirmed (memory-only) address index that involve the passed address. +// Unsupported address types are ignored and will result in no results. +// +// This function is safe for concurrent access. +func (idx *AddrIndex) UnconfirmedTxnsForAddress(addr btcutil.Address) []*btcutil.Tx { + // Ignore unsupported address types. + addrKey, err := addrToKey(addr) + if err != nil { + return nil + } + + // Protect concurrent access. + idx.unconfirmedLock.RLock() + defer idx.unconfirmedLock.RUnlock() + + // Return a new slice with the results if there are any. This ensures + // safe concurrency. + if txns, exists := idx.txnsByAddr[addrKey]; exists { + addressTxns := make([]*btcutil.Tx, 0, len(txns)) + for _, tx := range txns { + addressTxns = append(addressTxns, tx) + } + return addressTxns + } + + return nil +} + +// NewAddrIndex returns a new instance of an indexer that is used to create a +// mapping of all addresses in the blockchain to the respective transactions +// that involve them. +// +// It implements the Indexer interface which plugs into the IndexManager that in +// turn is used by the blockchain package. This allows the index to be +// seamlessly maintained along with the chain. +func NewAddrIndex(db database.DB, chainParams *chaincfg.Params) *AddrIndex { + return &AddrIndex{ + db: db, + chainParams: chainParams, + txnsByAddr: make(map[[addrKeySize]byte]map[wire.ShaHash]*btcutil.Tx), + addrsByTx: make(map[wire.ShaHash]map[[addrKeySize]byte]struct{}), + } +} + +// DropAddrIndex drops the address index from the provided database if it +// exists. +func DropAddrIndex(db database.DB) error { + return dropIndex(db, addrIndexKey, addrIndexName) +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/indexers/addrindex_test.go b/vendor/github.com/btcsuite/btcd/blockchain/indexers/addrindex_test.go new file mode 100644 index 0000000000000000000000000000000000000000..92fce5385b9e7a923e9ac0b6e7e99385d6d56798 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/indexers/addrindex_test.go @@ -0,0 +1,275 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package indexers + +import ( + "bytes" + "fmt" + "testing" + + "github.com/btcsuite/btcd/wire" +) + +// addrIndexBucket provides a mock address index database bucket by implementing +// the internalBucket interface. +type addrIndexBucket struct { + levels map[[levelKeySize]byte][]byte +} + +// Clone returns a deep copy of the mock adress index bucket. +func (b *addrIndexBucket) Clone() *addrIndexBucket { + levels := make(map[[levelKeySize]byte][]byte) + for k, v := range b.levels { + vCopy := make([]byte, len(v)) + copy(vCopy, v) + levels[k] = vCopy + } + return &addrIndexBucket{levels: levels} +} + +// Get returns the value associated with the key from the mock address index +// bucket. +// +// This is part of the internalBucket interface. +func (b *addrIndexBucket) Get(key []byte) []byte { + var levelKey [levelKeySize]byte + copy(levelKey[:], key) + return b.levels[levelKey] +} + +// Put stores the provided key/value pair to the mock address index bucket. +// +// This is part of the internalBucket interface. +func (b *addrIndexBucket) Put(key []byte, value []byte) error { + var levelKey [levelKeySize]byte + copy(levelKey[:], key) + b.levels[levelKey] = value + return nil +} + +// Delete removes the provided key from the mock address index bucket. +// +// This is part of the internalBucket interface. +func (b *addrIndexBucket) Delete(key []byte) error { + var levelKey [levelKeySize]byte + copy(levelKey[:], key) + delete(b.levels, levelKey) + return nil +} + +// printLevels returns a string with a visual representation of the provided +// address key taking into account the max size of each level. It is useful +// when creating and debugging test cases. +func (b *addrIndexBucket) printLevels(addrKey [addrKeySize]byte) string { + highestLevel := uint8(0) + for k := range b.levels { + if !bytes.Equal(k[:levelOffset], addrKey[:]) { + continue + } + level := uint8(k[levelOffset]) + if level > highestLevel { + highestLevel = level + } + } + + var levelBuf bytes.Buffer + _, _ = levelBuf.WriteString("\n") + maxEntries := level0MaxEntries + for level := uint8(0); level <= highestLevel; level++ { + data := b.levels[keyForLevel(addrKey, level)] + numEntries := len(data) / txEntrySize + for i := 0; i < numEntries; i++ { + start := i * txEntrySize + num := byteOrder.Uint32(data[start:]) + _, _ = levelBuf.WriteString(fmt.Sprintf("%02d ", num)) + } + for i := numEntries; i < maxEntries; i++ { + _, _ = levelBuf.WriteString("_ ") + } + _, _ = levelBuf.WriteString("\n") + maxEntries *= 2 + } + + return levelBuf.String() +} + +// sanityCheck ensures that all data stored in the bucket for the given address +// adheres to the level-based rules described by the address index +// documentation. +func (b *addrIndexBucket) sanityCheck(addrKey [addrKeySize]byte, expectedTotal int) error { + // Find the highest level for the key. + highestLevel := uint8(0) + for k := range b.levels { + if !bytes.Equal(k[:levelOffset], addrKey[:]) { + continue + } + level := uint8(k[levelOffset]) + if level > highestLevel { + highestLevel = level + } + } + + // Ensure the expected total number of entries are present and that + // all levels adhere to the rules described in the address index + // documentation. + var totalEntries int + maxEntries := level0MaxEntries + for level := uint8(0); level <= highestLevel; level++ { + // Level 0 can'have more entries than the max allowed if the + // levels after it have data and it can't be empty. All other + // levels must either be half full or full. + data := b.levels[keyForLevel(addrKey, level)] + numEntries := len(data) / txEntrySize + totalEntries += numEntries + if level == 0 { + if (highestLevel != 0 && numEntries == 0) || + numEntries > maxEntries { + + return fmt.Errorf("level %d has %d entries", + level, numEntries) + } + } else if numEntries != maxEntries && numEntries != maxEntries/2 { + return fmt.Errorf("level %d has %d entries", level, + numEntries) + } + maxEntries *= 2 + } + if totalEntries != expectedTotal { + return fmt.Errorf("expected %d entries - got %d", expectedTotal, + totalEntries) + } + + // Ensure all of the numbers are in order starting from the highest + // level moving to the lowest level. + expectedNum := uint32(0) + for level := highestLevel + 1; level > 0; level-- { + data := b.levels[keyForLevel(addrKey, level)] + numEntries := len(data) / txEntrySize + for i := 0; i < numEntries; i++ { + start := i * txEntrySize + num := byteOrder.Uint32(data[start:]) + if num != expectedNum { + return fmt.Errorf("level %d offset %d does "+ + "not contain the expected number of "+ + "%d - got %d", level, i, num, + expectedNum) + } + expectedNum++ + } + } + + return nil +} + +// TestAddrIndexLevels ensures that adding and deleting entries to the address +// index creates multiple levels as decribed by the address index documentation. +func TestAddrIndexLevels(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + key [addrKeySize]byte + numInsert int + printLevels bool // Set to help debug a specific test. + }{ + { + name: "level 0 not full", + numInsert: level0MaxEntries - 1, + }, + { + name: "level 1 half", + numInsert: level0MaxEntries + 1, + }, + { + name: "level 1 full", + numInsert: level0MaxEntries*2 + 1, + }, + { + name: "level 2 half, level 1 half", + numInsert: level0MaxEntries*3 + 1, + }, + { + name: "level 2 half, level 1 full", + numInsert: level0MaxEntries*4 + 1, + }, + { + name: "level 2 full, level 1 half", + numInsert: level0MaxEntries*5 + 1, + }, + { + name: "level 2 full, level 1 full", + numInsert: level0MaxEntries*6 + 1, + }, + { + name: "level 3 half, level 2 half, level 1 half", + numInsert: level0MaxEntries*7 + 1, + }, + { + name: "level 3 full, level 2 half, level 1 full", + numInsert: level0MaxEntries*12 + 1, + }, + } + +nextTest: + for testNum, test := range tests { + // Insert entries in order. + populatedBucket := &addrIndexBucket{ + levels: make(map[[levelKeySize]byte][]byte), + } + for i := 0; i < test.numInsert; i++ { + txLoc := wire.TxLoc{TxStart: i * 2} + err := dbPutAddrIndexEntry(populatedBucket, test.key, + uint32(i), txLoc) + if err != nil { + t.Errorf("dbPutAddrIndexEntry #%d (%s) - "+ + "unexpected error: %v", testNum, + test.name, err) + continue nextTest + } + } + if test.printLevels { + t.Log(populatedBucket.printLevels(test.key)) + } + + // Delete entries from the populated bucket until all entries + // have been deleted. The bucket is reset to the fully + // populated bucket on each iteration so every combination is + // tested. Notice the upper limit purposes exceeds the number + // of entries to ensure attempting to delete more entries than + // there are works correctly. + for numDelete := 0; numDelete <= test.numInsert+1; numDelete++ { + // Clone populated bucket to run each delete against. + bucket := populatedBucket.Clone() + + // Remove the number of entries for this iteration. + err := dbRemoveAddrIndexEntries(bucket, test.key, + numDelete) + if err != nil { + if numDelete <= test.numInsert { + t.Errorf("dbRemoveAddrIndexEntries (%s) "+ + " delete %d - unexpected error: "+ + "%v", test.name, numDelete, err) + continue nextTest + } + } + if test.printLevels { + t.Log(bucket.printLevels(test.key)) + } + + // Sanity check the levels to ensure the adhere to all + // rules. + numExpected := test.numInsert + if numDelete <= test.numInsert { + numExpected -= numDelete + } + err = bucket.sanityCheck(test.key, numExpected) + if err != nil { + t.Errorf("sanity check fail (%s) delete %d: %v", + test.name, numDelete, err) + continue nextTest + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/indexers/blocklogger.go b/vendor/github.com/btcsuite/btcd/blockchain/indexers/blocklogger.go new file mode 100644 index 0000000000000000000000000000000000000000..88d6f26998b21c0178d5e062dc3545c2f7379183 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/indexers/blocklogger.go @@ -0,0 +1,76 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package indexers + +import ( + "sync" + "time" + + "github.com/btcsuite/btclog" + "github.com/btcsuite/btcutil" +) + +// blockProgressLogger provides periodic logging for other services in order +// to show users progress of certain "actions" involving some or all current +// blocks. Ex: syncing to best chain, indexing all blocks, etc. +type blockProgressLogger struct { + receivedLogBlocks int64 + receivedLogTx int64 + lastBlockLogTime time.Time + + subsystemLogger btclog.Logger + progressAction string + sync.Mutex +} + +// newBlockProgressLogger returns a new block progress logger. +// The progress message is templated as follows: +// {progressAction} {numProcessed} {blocks|block} in the last {timePeriod} +// ({numTxs}, height {lastBlockHeight}, {lastBlockTimeStamp}) +func newBlockProgressLogger(progressMessage string, logger btclog.Logger) *blockProgressLogger { + return &blockProgressLogger{ + lastBlockLogTime: time.Now(), + progressAction: progressMessage, + subsystemLogger: logger, + } +} + +// LogBlockHeight logs a new block height as an information message to show +// progress to the user. In order to prevent spam, it limits logging to one +// message every 10 seconds with duration and totals included. +func (b *blockProgressLogger) LogBlockHeight(block *btcutil.Block) { + b.Lock() + defer b.Unlock() + + b.receivedLogBlocks++ + b.receivedLogTx += int64(len(block.MsgBlock().Transactions)) + + now := time.Now() + duration := now.Sub(b.lastBlockLogTime) + if duration < time.Second*10 { + return + } + + // Truncate the duration to 10s of milliseconds. + durationMillis := int64(duration / time.Millisecond) + tDuration := 10 * time.Millisecond * time.Duration(durationMillis/10) + + // Log information about new block height. + blockStr := "blocks" + if b.receivedLogBlocks == 1 { + blockStr = "block" + } + txStr := "transactions" + if b.receivedLogTx == 1 { + txStr = "transaction" + } + b.subsystemLogger.Infof("%s %d %s in the last %s (%d %s, height %d, %s)", + b.progressAction, b.receivedLogBlocks, blockStr, tDuration, b.receivedLogTx, + txStr, block.Height(), block.MsgBlock().Header.Timestamp) + + b.receivedLogBlocks = 0 + b.receivedLogTx = 0 + b.lastBlockLogTime = now +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/indexers/common.go b/vendor/github.com/btcsuite/btcd/blockchain/indexers/common.go new file mode 100644 index 0000000000000000000000000000000000000000..be4f07afd21021d4a13dd1d796b9ead8f7c5eba0 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/indexers/common.go @@ -0,0 +1,90 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package indexers implements optional block chain indexes. +*/ +package indexers + +import ( + "encoding/binary" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcutil" +) + +var ( + // byteOrder is the preferred byte order used for serializing numeric + // fields for storage in the database. + byteOrder = binary.LittleEndian +) + +// NeedsInputser provides a generic interface for an indexer to specify the it +// requires the ability to look up inputs for a transaction. +type NeedsInputser interface { + NeedsInputs() bool +} + +// Indexer provides a generic interface for an indexer that is managed by an +// index manager such as the Manager type provided by this package. +type Indexer interface { + // Key returns the key of the index as a byte slice. + Key() []byte + + // Name returns the human-readable name of the index. + Name() string + + // Create is invoked when the indexer manager determines the index needs + // to be created for the first time. + Create(dbTx database.Tx) error + + // Init is invoked when the index manager is first initializing the + // index. This differs from the Create method in that it is called on + // every load, including the case the index was just created. + Init() error + + // ConnectBlock is invoked when the index manager is notified that a new + // block has been connected to the main chain. + ConnectBlock(dbTx database.Tx, block *btcutil.Block, view *blockchain.UtxoViewpoint) error + + // DisconnectBlock is invoked when the index manager is notified that a + // block has been disconnected from the main chain. + DisconnectBlock(dbTx database.Tx, block *btcutil.Block, view *blockchain.UtxoViewpoint) error +} + +// AssertError identifies an error that indicates an internal code consistency +// issue and should be treated as a critical and unrecoverable error. +type AssertError string + +// Error returns the assertion error as a huma-readable string and satisfies +// the error interface. +func (e AssertError) Error() string { + return "assertion failed: " + string(e) +} + +// errDeserialize signifies that a problem was encountered when deserializing +// data. +type errDeserialize string + +// Error implements the error interface. +func (e errDeserialize) Error() string { + return string(e) +} + +// isDeserializeErr returns whether or not the passed error is an errDeserialize +// error. +func isDeserializeErr(err error) bool { + _, ok := err.(errDeserialize) + return ok +} + +// internalBucket is an abstraction over a database bucket. It is used to make +// the code easier to test since it allows mock objects in the tests to only +// implement these functions instead of everything a database.Bucket supports. +type internalBucket interface { + Get(key []byte) []byte + Put(key []byte, value []byte) error + Delete(key []byte) error +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/indexers/log.go b/vendor/github.com/btcsuite/btcd/blockchain/indexers/log.go new file mode 100644 index 0000000000000000000000000000000000000000..0172da0787a7c2c0e221a08cddfc72a40a41da21 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/indexers/log.go @@ -0,0 +1,30 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package indexers + +import "github.com/btcsuite/btclog" + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + DisableLog() +} + +// DisableLog disables all library log output. Logging output is disabled +// by default until either UseLogger or SetLogWriter are called. +func DisableLog() { + log = btclog.Disabled +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/indexers/manager.go b/vendor/github.com/btcsuite/btcd/blockchain/indexers/manager.go new file mode 100644 index 0000000000000000000000000000000000000000..eca70d9a583f39af51afcb693e848f22a69daef5 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/indexers/manager.go @@ -0,0 +1,653 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package indexers + +import ( + "bytes" + "fmt" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +var ( + // indexTipsBucketName is the name of the db bucket used to house the + // current tip of each index. + indexTipsBucketName = []byte("idxtips") +) + +// ----------------------------------------------------------------------------- +// The index manager tracks the current tip of each index by using a parent +// bucket that contains an entry for index. +// +// The serialized format for an index tip is: +// +// [<block hash><block height>],... +// +// Field Type Size +// block hash wire.ShaHash wire.HashSize +// block height uint32 4 bytes +// ----------------------------------------------------------------------------- + +// dbPutIndexerTip uses an existing database transaction to update or add the +// current tip for the given index to the provided values. +func dbPutIndexerTip(dbTx database.Tx, idxKey []byte, hash *wire.ShaHash, height int32) error { + serialized := make([]byte, wire.HashSize+4) + copy(serialized, hash[:]) + byteOrder.PutUint32(serialized[wire.HashSize:], uint32(height)) + + indexesBucket := dbTx.Metadata().Bucket(indexTipsBucketName) + return indexesBucket.Put(idxKey, serialized) +} + +// dbFetchIndexerTip uses an existing database transaction to retrieve the +// hash and height of the current tip for the provided index. +func dbFetchIndexerTip(dbTx database.Tx, idxKey []byte) (*wire.ShaHash, int32, error) { + indexesBucket := dbTx.Metadata().Bucket(indexTipsBucketName) + serialized := indexesBucket.Get(idxKey) + if len(serialized) < wire.HashSize+4 { + return nil, 0, database.Error{ + ErrorCode: database.ErrCorruption, + Description: fmt.Sprintf("unexpected end of data for "+ + "index %q tip", string(idxKey)), + } + } + + var hash wire.ShaHash + copy(hash[:], serialized[:wire.HashSize]) + height := int32(byteOrder.Uint32(serialized[wire.HashSize:])) + return &hash, height, nil +} + +// dbIndexConnectBlock adds all of the index entries associated with the +// given block using the provided indexer and updates the tip of the indexer +// accordingly. An error will be returned if the current tip for the indexer is +// not the previous block for the passed block. +func dbIndexConnectBlock(dbTx database.Tx, indexer Indexer, block *btcutil.Block, view *blockchain.UtxoViewpoint) error { + // Assert that the block being connected properly connects to the + // current tip of the index. + idxKey := indexer.Key() + curTipHash, _, err := dbFetchIndexerTip(dbTx, idxKey) + if err != nil { + return err + } + if !curTipHash.IsEqual(&block.MsgBlock().Header.PrevBlock) { + return AssertError(fmt.Sprintf("dbIndexConnectBlock must be "+ + "called with a block that extends the current index "+ + "tip (%s, tip %s, block %s)", indexer.Name(), + curTipHash, block.Sha())) + } + + // Notify the indexer with the connected block so it can index it. + if err := indexer.ConnectBlock(dbTx, block, view); err != nil { + return err + } + + // Update the current index tip. + return dbPutIndexerTip(dbTx, idxKey, block.Sha(), block.Height()) +} + +// dbIndexDisconnectBlock removes all of the index entries associated with the +// given block using the provided indexer and updates the tip of the indexer +// accordingly. An error will be returned if the current tip for the indexer is +// not the passed block. +func dbIndexDisconnectBlock(dbTx database.Tx, indexer Indexer, block *btcutil.Block, view *blockchain.UtxoViewpoint) error { + // Assert that the block being disconnected is the current tip of the + // index. + idxKey := indexer.Key() + curTipHash, _, err := dbFetchIndexerTip(dbTx, idxKey) + if err != nil { + return err + } + if !curTipHash.IsEqual(block.Sha()) { + return AssertError(fmt.Sprintf("dbIndexDisconnectBlock must "+ + "be called with the block at the current index tip "+ + "(%s, tip %s, block %s)", indexer.Name(), + curTipHash, block.Sha())) + } + + // Notify the indexer with the disconnected block so it can remove all + // of the appropriate entries. + if err := indexer.DisconnectBlock(dbTx, block, view); err != nil { + return err + } + + // Update the current index tip. + prevHash := &block.MsgBlock().Header.PrevBlock + return dbPutIndexerTip(dbTx, idxKey, prevHash, block.Height()-1) +} + +// Manager defines an index manager that manages multiple optional indexes and +// implements the blockchain.IndexManager interface so it can be seamlessly +// plugged into normal chain processing. +type Manager struct { + db database.DB + enabledIndexes []Indexer +} + +// Ensure the Manager type implements the blockchain.IndexManager interface. +var _ blockchain.IndexManager = (*Manager)(nil) + +// indexDropKey returns the key for an index which indicates it is in the +// process of being dropped. +func indexDropKey(idxKey []byte) []byte { + dropKey := make([]byte, len(idxKey)+1) + dropKey[0] = 'd' + copy(dropKey[1:], idxKey) + return dropKey +} + +// maybeFinishDrops determines if each of the enabled indexes are in the middle +// of being dropped and finishes dropping them when the are. This is necessary +// because dropping and index has to be done in several atomic steps rather than +// one big atomic step due to the massive number of entries. +func (m *Manager) maybeFinishDrops() error { + indexNeedsDrop := make([]bool, len(m.enabledIndexes)) + err := m.db.View(func(dbTx database.Tx) error { + // None of the indexes needs to be dropped if the index tips + // bucket hasn't been created yet. + indexesBucket := dbTx.Metadata().Bucket(indexTipsBucketName) + if indexesBucket == nil { + return nil + } + + // Make the indexer as requiring a drop if one is already in + // progress. + for i, indexer := range m.enabledIndexes { + dropKey := indexDropKey(indexer.Key()) + if indexesBucket.Get(dropKey) != nil { + indexNeedsDrop[i] = true + } + } + + return nil + }) + if err != nil { + return err + } + + // Finish dropping any of the enabled indexes that are already in the + // middle of being dropped. + for i, indexer := range m.enabledIndexes { + if !indexNeedsDrop[i] { + continue + } + + log.Infof("Resuming %s drop", indexer.Name()) + err := dropIndex(m.db, indexer.Key(), indexer.Name()) + if err != nil { + return err + } + } + + return nil +} + +// maybeCreateIndexes determines if each of the enabled indexes have already +// been created and creates them if not. +func (m *Manager) maybeCreateIndexes(dbTx database.Tx) error { + indexesBucket := dbTx.Metadata().Bucket(indexTipsBucketName) + for _, indexer := range m.enabledIndexes { + // Nothing to do if the index tip already exists. + idxKey := indexer.Key() + if indexesBucket.Get(idxKey) != nil { + continue + } + + // The tip for the index does not exist, so create it and + // invoke the create callback for the index so it can perform + // any one-time initialization it requires. + if err := indexer.Create(dbTx); err != nil { + return err + } + + // Set the tip for the index to values which represent an + // uninitialized index. + err := dbPutIndexerTip(dbTx, idxKey, &wire.ShaHash{}, -1) + if err != nil { + return err + } + } + + return nil +} + +// Init initializes the enabled indexes. This is called during chain +// initialization and primarily consists of catching up all indexes to the +// current best chain tip. This is necessary since each index can be disabled +// and re-enabled at any time and attempting to catch-up indexes at the same +// time new blocks are being downloaded would lead to an overall longer time to +// catch up due to the I/O contention. +// +// This is part of the blockchain.IndexManager interface. +func (m *Manager) Init(chain *blockchain.BlockChain) error { + // Nothing to do when no indexes are enabled. + if len(m.enabledIndexes) == 0 { + return nil + } + + // Finish and drops that were previously interrupted. + if err := m.maybeFinishDrops(); err != nil { + return err + } + + // Create the initial state for the indexes as needed. + err := m.db.Update(func(dbTx database.Tx) error { + // Create the bucket for the current tips as needed. + meta := dbTx.Metadata() + _, err := meta.CreateBucketIfNotExists(indexTipsBucketName) + if err != nil { + return err + } + + return m.maybeCreateIndexes(dbTx) + }) + if err != nil { + return err + } + + // Initialize each of the enabled indexes. + for _, indexer := range m.enabledIndexes { + if err := indexer.Init(); err != nil { + return err + } + } + + // Rollback indexes to the main chain if their tip is an orphaned fork. + // This is fairly unlikely, but it can happen if the chain is + // reorganized while the index is disabled. This has to be done in + // reverse order because later indexes can depend on earlier ones. + for i := len(m.enabledIndexes); i > 0; i-- { + indexer := m.enabledIndexes[i-1] + + // Fetch the current tip for the index. + var height int32 + var hash *wire.ShaHash + err := m.db.View(func(dbTx database.Tx) error { + idxKey := indexer.Key() + hash, height, err = dbFetchIndexerTip(dbTx, idxKey) + if err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + + // Nothing to do if the index does not have any entries yet. + if height == -1 { + continue + } + + // Loop until the tip is a block that exists in the main chain. + initialHeight := height + for { + exists, err := chain.MainChainHasBlock(hash) + if err != nil { + return err + } + if exists { + break + } + + // At this point the index tip is orphaned, so load the + // orphaned block from the database directly and + // disconnect it from the index. The block has to be + // loaded directly since it is no longer in the main + // chain and thus the chain.BlockByHash function would + // error. + err = m.db.Update(func(dbTx database.Tx) error { + blockBytes, err := dbTx.FetchBlock(hash) + if err != nil { + return err + } + block, err := btcutil.NewBlockFromBytes(blockBytes) + if err != nil { + return err + } + block.SetHeight(height) + + // When the index requires all of the referenced + // txouts they need to be retrieved from the + // transaction index. + var view *blockchain.UtxoViewpoint + if indexNeedsInputs(indexer) { + var err error + view, err = makeUtxoView(dbTx, block) + if err != nil { + return err + } + } + + // Remove all of the index entries associated + // with the block and update the indexer tip. + err = dbIndexDisconnectBlock(dbTx, indexer, + block, view) + if err != nil { + return err + } + + // Update the tip to the previous block. + hash = &block.MsgBlock().Header.PrevBlock + height-- + + return nil + }) + if err != nil { + return err + } + } + + if initialHeight != height { + log.Infof("Removed %d orphaned blocks from %s "+ + "(heights %d to %d)", initialHeight-height, + indexer.Name(), height+1, initialHeight) + } + } + + // Fetch the current tip heights for each index along with tracking the + // lowest one so the catchup code only needs to start at the earliest + // block and is able to skip connecting the block for the indexes that + // don't need it. + bestHeight := chain.BestSnapshot().Height + lowestHeight := bestHeight + indexerHeights := make([]int32, len(m.enabledIndexes)) + err = m.db.View(func(dbTx database.Tx) error { + for i, indexer := range m.enabledIndexes { + idxKey := indexer.Key() + hash, height, err := dbFetchIndexerTip(dbTx, idxKey) + if err != nil { + return err + } + + log.Debugf("Current %s tip (height %d, hash %v)", + indexer.Name(), height, hash) + indexerHeights[i] = height + if height < lowestHeight { + lowestHeight = height + } + } + return nil + }) + if err != nil { + return err + } + + // Nothing to index if all of the indexes are caught up. + if lowestHeight == bestHeight { + return nil + } + + // Create a progress logger for the indexing process below. + progressLogger := newBlockProgressLogger("Indexed", log) + + // At this point, one or more indexes are behind the current best chain + // tip and need to be caught up, so log the details and loop through + // each block that needs to be indexed. + log.Infof("Catching up indexes from height %d to %d", lowestHeight, + bestHeight) + for height := lowestHeight + 1; height <= bestHeight; height++ { + // Load the block for the height since it is required to index + // it. + block, err := chain.BlockByHeight(height) + if err != nil { + return err + } + + // Connect the block for all indexes that need it. + var view *blockchain.UtxoViewpoint + for i, indexer := range m.enabledIndexes { + // Skip indexes that don't need to be updated with this + // block. + if indexerHeights[i] >= height { + continue + } + + err := m.db.Update(func(dbTx database.Tx) error { + // When the index requires all of the referenced + // txouts and they haven't been loaded yet, they + // need to be retrieved from the transaction + // index. + if view == nil && indexNeedsInputs(indexer) { + var err error + view, err = makeUtxoView(dbTx, block) + if err != nil { + return err + } + } + return dbIndexConnectBlock(dbTx, indexer, block, + view) + }) + if err != nil { + return err + } + indexerHeights[i] = height + } + + // Log indexing progress. + progressLogger.LogBlockHeight(block) + } + + log.Infof("Indexes caught up to height %d", bestHeight) + return nil +} + +// indexNeedsInputs returns whether or not the index needs access to the txouts +// referenced by the transaction inputs being indexed. +func indexNeedsInputs(index Indexer) bool { + if idx, ok := index.(NeedsInputser); ok { + return idx.NeedsInputs() + } + + return false +} + +// dbFetchTx looks up the passed transaction hash in the transaction index and +// loads it from the database. +func dbFetchTx(dbTx database.Tx, hash *wire.ShaHash) (*wire.MsgTx, error) { + // Look up the location of the transaction. + blockRegion, err := dbFetchTxIndexEntry(dbTx, hash) + if err != nil { + return nil, err + } + if blockRegion == nil { + return nil, fmt.Errorf("transaction %v not found", hash) + } + + // Load the raw transaction bytes from the database. + txBytes, err := dbTx.FetchBlockRegion(blockRegion) + if err != nil { + return nil, err + } + + // Deserialize the transaction. + var msgTx wire.MsgTx + err = msgTx.Deserialize(bytes.NewReader(txBytes)) + if err != nil { + return nil, err + } + + return &msgTx, nil +} + +// makeUtxoView creates a mock unspent transaction output view by using the +// transaction index in order to look up all inputs referenced by the +// transactions in the block. This is sometimes needed when catching indexes up +// because many of the txouts could actually already be spent however the +// associated scripts are still required to index them. +func makeUtxoView(dbTx database.Tx, block *btcutil.Block) (*blockchain.UtxoViewpoint, error) { + view := blockchain.NewUtxoViewpoint() + for txIdx, tx := range block.Transactions() { + // Coinbases do not reference any inputs. Since the block is + // required to have already gone through full validation, it has + // already been proven on the first transaction in the block is + // a coinbase. + if txIdx == 0 { + continue + } + + // Use the transaction index to load all of the referenced + // inputs and add their outputs to the view. + for _, txIn := range tx.MsgTx().TxIn { + originOut := &txIn.PreviousOutPoint + originTx, err := dbFetchTx(dbTx, &originOut.Hash) + if err != nil { + return nil, err + } + + view.AddTxOuts(btcutil.NewTx(originTx), 0) + } + } + + return view, nil +} + +// ConnectBlock must be invoked when a block is extending the main chain. It +// keeps track of the state of each index it is managing, performs some sanity +// checks, and invokes each indexer. +// +// This is part of the blockchain.IndexManager interface. +func (m *Manager) ConnectBlock(dbTx database.Tx, block *btcutil.Block, view *blockchain.UtxoViewpoint) error { + // Call each of the currently active optional indexes with the block + // being connected so they can update accordingly. + for _, index := range m.enabledIndexes { + err := dbIndexConnectBlock(dbTx, index, block, view) + if err != nil { + return err + } + } + return nil +} + +// DisconnectBlock must be invoked when a block is being disconnected from the +// end of the main chain. It keeps track of the state of each index it is +// managing, performs some sanity checks, and invokes each indexer to remove +// the index entries associated with the block. +// +// This is part of the blockchain.IndexManager interface. +func (m *Manager) DisconnectBlock(dbTx database.Tx, block *btcutil.Block, view *blockchain.UtxoViewpoint) error { + // Call each of the currently active optional indexes with the block + // being disconnected so they can update accordingly. + for _, index := range m.enabledIndexes { + err := dbIndexDisconnectBlock(dbTx, index, block, view) + if err != nil { + return err + } + } + return nil +} + +// NewManager returns a new index manager with the provided indexes enabled. +// +// The manager returned satisfies the blockchain.IndexManager interface and thus +// cleanly plugs into the normal blockchain processing path. +func NewManager(db database.DB, enabledIndexes []Indexer) *Manager { + return &Manager{ + db: db, + enabledIndexes: enabledIndexes, + } +} + +// dropIndex drops the passed index from the database. Since indexes can be +// massive, it deletes the index in multiple database transactions in order to +// keep memory usage to reasonable levels. It also marks the drop in progress +// so the drop can be resumed if it is stopped before it is done before the +// index can be used again. +func dropIndex(db database.DB, idxKey []byte, idxName string) error { + // Nothing to do if the index doesn't already exist. + var needsDelete bool + err := db.View(func(dbTx database.Tx) error { + indexesBucket := dbTx.Metadata().Bucket(indexTipsBucketName) + if indexesBucket != nil && indexesBucket.Get(idxKey) != nil { + needsDelete = true + } + return nil + }) + if err != nil { + return err + } + if !needsDelete { + log.Infof("Not dropping %s because it does not exist", idxName) + return nil + } + + // Mark that the index is in the process of being dropped so that it + // can be resumed on the next start if interrupted before the process is + // complete. + log.Infof("Dropping all %s entries. This might take a while...", + idxName) + err = db.Update(func(dbTx database.Tx) error { + indexesBucket := dbTx.Metadata().Bucket(indexTipsBucketName) + return indexesBucket.Put(indexDropKey(idxKey), idxKey) + }) + if err != nil { + return err + } + + // Since the indexes can be so large, attempting to simply delete + // the bucket in a single database transaction would result in massive + // memory usage and likely crash many systems due to ulimits. In order + // to avoid this, use a cursor to delete a maximum number of entries out + // of the bucket at a time. + const maxDeletions = 2000000 + var totalDeleted uint64 + for numDeleted := maxDeletions; numDeleted == maxDeletions; { + numDeleted = 0 + err := db.Update(func(dbTx database.Tx) error { + bucket := dbTx.Metadata().Bucket(idxKey) + cursor := bucket.Cursor() + for ok := cursor.First(); ok; ok = cursor.Next() && + numDeleted < maxDeletions { + + if err := cursor.Delete(); err != nil { + return err + } + numDeleted++ + } + return nil + }) + if err != nil { + return err + } + + if numDeleted > 0 { + totalDeleted += uint64(numDeleted) + log.Infof("Deleted %d keys (%d total) from %s", + numDeleted, totalDeleted, idxName) + } + } + + // Call extra index specific deinitialization for the transaction index. + if idxName == txIndexName { + if err := dropBlockIDIndex(db); err != nil { + return err + } + } + + // Remove the index tip, index bucket, and in-progress drop flag now + // that all index entries have been removed. + err = db.Update(func(dbTx database.Tx) error { + meta := dbTx.Metadata() + indexesBucket := meta.Bucket(indexTipsBucketName) + if err := indexesBucket.Delete(idxKey); err != nil { + return err + } + + if err := meta.DeleteBucket(idxKey); err != nil { + return err + } + + return indexesBucket.Delete(indexDropKey(idxKey)) + }) + if err != nil { + return err + } + + log.Infof("Dropped %s", idxName) + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/indexers/txindex.go b/vendor/github.com/btcsuite/btcd/blockchain/indexers/txindex.go new file mode 100644 index 0000000000000000000000000000000000000000..5f94229e58af9e0b0efe8ef624059fad5847755c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/indexers/txindex.go @@ -0,0 +1,477 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package indexers + +import ( + "errors" + "fmt" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +const ( + // txIndexName is the human-readable name for the index. + txIndexName = "transaction index" +) + +var ( + // txIndexKey is the key of the transaction index and the db bucket used + // to house it. + txIndexKey = []byte("txbyhashidx") + + // idByHashIndexBucketName is the name of the db bucket used to house + // the block id -> block hash index. + idByHashIndexBucketName = []byte("idbyhashidx") + + // hashByIDIndexBucketName is the name of the db bucket used to house + // the block hash -> block id index. + hashByIDIndexBucketName = []byte("hashbyididx") + + // errNoBlockIDEntry is an error that indicates a requested entry does + // not exist in the block ID index. + errNoBlockIDEntry = errors.New("no entry in the block ID index") +) + +// ----------------------------------------------------------------------------- +// The transaction index consists of an entry for every transaction in the main +// chain. In order to significanly optimize the space requirements a separate +// index which provides an internal mapping between each block that has been +// indexed and a unique ID for use within the hash to location mappings. The ID +// is simply a sequentially incremented uint32. This is useful because it is +// only 4 bytes versus 32 bytes hashes and thus saves a ton of space in the +// index. +// +// There are three buckets used in total. The first bucket maps the hash of +// each transaction to the specific block location. The second bucket maps the +// hash of each block to the unique ID and the third maps that ID back to the +// block hash. +// +// NOTE: Although it is technically possible for multiple transactions to have +// the same hash as long as the previous transaction with the same hash is fully +// spent, this code only stores the most recent one because doing otherwise +// would add a non-trivial amount of space and overhead for something that will +// realistically never happen per the probability and even if it did, the old +// one must be fully spent and so the most likely transaction a caller would +// want for a given hash is the most recent one anyways. +// +// The serialized format for keys and values in the block hash to ID bucket is: +// <hash> = <ID> +// +// Field Type Size +// hash wire.ShaHash 32 bytes +// ID uint32 4 bytes +// ----- +// Total: 36 bytes +// +// The serialized format for keys and values in the ID to block hash bucket is: +// <ID> = <hash> +// +// Field Type Size +// ID uint32 4 bytes +// hash wire.ShaHash 32 bytes +// ----- +// Total: 36 bytes +// +// The serialized format for the keys and values in the tx index bucket is: +// +// <txhash> = <block id><start offset><tx length> +// +// Field Type Size +// txhash wire.ShaHash 32 bytes +// block id uint32 4 bytes +// start offset uint32 4 bytes +// tx length uint32 4 bytes +// ----- +// Total: 44 bytes +// ----------------------------------------------------------------------------- + +// dbPutBlockIDIndexEntry uses an existing database transaction to update or add +// the index entries for the hash to id and id to hash mappings for the provided +// values. +func dbPutBlockIDIndexEntry(dbTx database.Tx, hash *wire.ShaHash, id uint32) error { + // Serialize the height for use in the index entries. + var serializedID [4]byte + byteOrder.PutUint32(serializedID[:], id) + + // Add the block hash to ID mapping to the index. + meta := dbTx.Metadata() + hashIndex := meta.Bucket(idByHashIndexBucketName) + if err := hashIndex.Put(hash[:], serializedID[:]); err != nil { + return err + } + + // Add the block ID to hash mapping to the index. + idIndex := meta.Bucket(hashByIDIndexBucketName) + return idIndex.Put(serializedID[:], hash[:]) +} + +// dbRemoveBlockIDIndexEntry uses an existing database transaction remove index +// entries from the hash to id and id to hash mappings for the provided hash. +func dbRemoveBlockIDIndexEntry(dbTx database.Tx, hash *wire.ShaHash) error { + // Remove the block hash to ID mapping. + meta := dbTx.Metadata() + hashIndex := meta.Bucket(idByHashIndexBucketName) + serializedID := hashIndex.Get(hash[:]) + if serializedID == nil { + return nil + } + if err := hashIndex.Delete(hash[:]); err != nil { + return err + } + + // Remove the block ID to hash mapping. + idIndex := meta.Bucket(hashByIDIndexBucketName) + return idIndex.Delete(serializedID) +} + +// dbFetchBlockIDByHash uses an existing database transaction to retrieve the +// block id for the provided hash from the index. +func dbFetchBlockIDByHash(dbTx database.Tx, hash *wire.ShaHash) (uint32, error) { + hashIndex := dbTx.Metadata().Bucket(idByHashIndexBucketName) + serializedID := hashIndex.Get(hash[:]) + if serializedID == nil { + return 0, errNoBlockIDEntry + } + + return byteOrder.Uint32(serializedID), nil +} + +// dbFetchBlockHashBySerializedID uses an existing database transaction to +// retrieve the hash for the provided serialized block id from the index. +func dbFetchBlockHashBySerializedID(dbTx database.Tx, serializedID []byte) (*wire.ShaHash, error) { + idIndex := dbTx.Metadata().Bucket(hashByIDIndexBucketName) + hashBytes := idIndex.Get(serializedID) + if hashBytes == nil { + return nil, errNoBlockIDEntry + } + + var hash wire.ShaHash + copy(hash[:], hashBytes) + return &hash, nil +} + +// dbFetchBlockHashByID uses an existing database transaction to retrieve the +// hash for the provided block id from the index. +func dbFetchBlockHashByID(dbTx database.Tx, id uint32) (*wire.ShaHash, error) { + var serializedID [4]byte + byteOrder.PutUint32(serializedID[:], id) + return dbFetchBlockHashBySerializedID(dbTx, serializedID[:]) +} + +// putTxIndexEntry serializes the provided values according to the format +// described about for a transaction index entry. The target byte slice must +// be at least large enough to handle the number of bytes defined by the +// txEntrySize constant or it will panic. +func putTxIndexEntry(target []byte, blockID uint32, txLoc wire.TxLoc) { + byteOrder.PutUint32(target, blockID) + byteOrder.PutUint32(target[4:], uint32(txLoc.TxStart)) + byteOrder.PutUint32(target[8:], uint32(txLoc.TxLen)) +} + +// dbPutTxIndexEntry uses an existing database transaction to update the +// transaction index given the provided serialized data that is expected to have +// been serialized putTxIndexEntry. +func dbPutTxIndexEntry(dbTx database.Tx, txHash *wire.ShaHash, serializedData []byte) error { + txIndex := dbTx.Metadata().Bucket(txIndexKey) + return txIndex.Put(txHash[:], serializedData) +} + +// dbFetchTxIndexEntry uses an existing database transaction to fetch the block +// region for the provided transaction hash from the transaction index. When +// there is no entry for the provided hash, nil will be returned for the both +// the region and the error. +func dbFetchTxIndexEntry(dbTx database.Tx, txHash *wire.ShaHash) (*database.BlockRegion, error) { + // Load the record from the database and return now if it doesn't exist. + txIndex := dbTx.Metadata().Bucket(txIndexKey) + serializedData := txIndex.Get(txHash[:]) + if len(serializedData) == 0 { + return nil, nil + } + + // Ensure the serialized data has enough bytes to properly deserialize. + if len(serializedData) < 12 { + return nil, database.Error{ + ErrorCode: database.ErrCorruption, + Description: fmt.Sprintf("corrupt transaction index "+ + "entry for %s", txHash), + } + } + + // Load the block hash associated with the block ID. + hash, err := dbFetchBlockHashBySerializedID(dbTx, serializedData[0:4]) + if err != nil { + return nil, database.Error{ + ErrorCode: database.ErrCorruption, + Description: fmt.Sprintf("corrupt transaction index "+ + "entry for %s: %v", txHash, err), + } + } + + // Deserialize the final entry. + region := database.BlockRegion{Hash: &wire.ShaHash{}} + copy(region.Hash[:], hash[:]) + region.Offset = byteOrder.Uint32(serializedData[4:8]) + region.Len = byteOrder.Uint32(serializedData[8:12]) + + return ®ion, nil +} + +// dbAddTxIndexEntries uses an existing database transaction to add a +// transaction index entry for every transaction in the passed block. +func dbAddTxIndexEntries(dbTx database.Tx, block *btcutil.Block, blockID uint32) error { + // The offset and length of the transactions within the serialized + // block. + txLocs, err := block.TxLoc() + if err != nil { + return err + } + + // As an optimization, allocate a single slice big enough to hold all + // of the serialized transaction index entries for the block and + // serialize them directly into the slice. Then, pass the appropriate + // subslice to the database to be written. This approach significantly + // cuts down on the number of required allocations. + offset := 0 + serializedValues := make([]byte, len(block.Transactions())*txEntrySize) + for i, tx := range block.Transactions() { + putTxIndexEntry(serializedValues[offset:], blockID, txLocs[i]) + endOffset := offset + txEntrySize + err := dbPutTxIndexEntry(dbTx, tx.Sha(), + serializedValues[offset:endOffset:endOffset]) + if err != nil { + return err + } + offset += txEntrySize + } + + return nil +} + +// dbRemoveTxIndexEntry uses an existing database transaction to remove the most +// recent transaction index entry for the given hash. +func dbRemoveTxIndexEntry(dbTx database.Tx, txHash *wire.ShaHash) error { + txIndex := dbTx.Metadata().Bucket(txIndexKey) + serializedData := txIndex.Get(txHash[:]) + if len(serializedData) == 0 { + return fmt.Errorf("can't remove non-existent transaction %s "+ + "from the transaction index", txHash) + } + + return txIndex.Delete(txHash[:]) +} + +// dbRemoveTxIndexEntries uses an existing database transaction to remove the +// latest transaction entry for every transaction in the passed block. +func dbRemoveTxIndexEntries(dbTx database.Tx, block *btcutil.Block) error { + for _, tx := range block.Transactions() { + err := dbRemoveTxIndexEntry(dbTx, tx.Sha()) + if err != nil { + return err + } + } + + return nil +} + +// TxIndex implements a transaction by hash index. That is to say, it supports +// querying all transactions by their hash. +type TxIndex struct { + db database.DB + curBlockID uint32 +} + +// Ensure the TxIndex type implements the Indexer interface. +var _ Indexer = (*TxIndex)(nil) + +// Init initializes the hash-based transaction index. In particular, it finds +// the highest used block ID and stores it for later use when connecting or +// disconnecting blocks. +// +// This is part of the Indexer interface. +func (idx *TxIndex) Init() error { + // Find the latest known block id field for the internal block id + // index and initialize it. This is done because it's a lot more + // efficient to do a single search at initialize time than it is to + // write another value to the database on every update. + err := idx.db.View(func(dbTx database.Tx) error { + // Scan forward in large gaps to find a block id that doesn't + // exist yet to serve as an upper bound for the binary search + // below. + var highestKnown, nextUnknown uint32 + testBlockID := uint32(1) + increment := uint32(100000) + for { + _, err := dbFetchBlockHashByID(dbTx, testBlockID) + if err != nil { + nextUnknown = testBlockID + break + } + + highestKnown = testBlockID + testBlockID += increment + } + log.Tracef("Forward scan (highest known %d, next unknown %d)", + highestKnown, nextUnknown) + + // No used block IDs due to new database. + if nextUnknown == 1 { + return nil + } + + // Use a binary search to find the final highest used block id. + // This will take at most ceil(log_2(increment)) attempts. + for { + testBlockID = (highestKnown + nextUnknown) / 2 + _, err := dbFetchBlockHashByID(dbTx, testBlockID) + if err != nil { + nextUnknown = testBlockID + } else { + highestKnown = testBlockID + } + log.Tracef("Binary scan (highest known %d, next "+ + "unknown %d)", highestKnown, nextUnknown) + if highestKnown+1 == nextUnknown { + break + } + } + + idx.curBlockID = highestKnown + return nil + }) + if err != nil { + return err + } + + log.Debugf("Current internal block ID: %d", idx.curBlockID) + return nil +} + +// Key returns the database key to use for the index as a byte slice. +// +// This is part of the Indexer interface. +func (idx *TxIndex) Key() []byte { + return txIndexKey +} + +// Name returns the human-readable name of the index. +// +// This is part of the Indexer interface. +func (idx *TxIndex) Name() string { + return txIndexName +} + +// Create is invoked when the indexer manager determines the index needs +// to be created for the first time. It creates the buckets for the hash-based +// transaction index and the internal block ID indexes. +// +// This is part of the Indexer interface. +func (idx *TxIndex) Create(dbTx database.Tx) error { + meta := dbTx.Metadata() + if _, err := meta.CreateBucket(idByHashIndexBucketName); err != nil { + return err + } + if _, err := meta.CreateBucket(hashByIDIndexBucketName); err != nil { + return err + } + _, err := meta.CreateBucket(txIndexKey) + return err +} + +// ConnectBlock is invoked by the index manager when a new block has been +// connected to the main chain. This indexer adds a hash-to-transaction mapping +// for every transaction in the passed block. +// +// This is part of the Indexer interface. +func (idx *TxIndex) ConnectBlock(dbTx database.Tx, block *btcutil.Block, view *blockchain.UtxoViewpoint) error { + // Increment the internal block ID to use for the block being connected + // and add all of the transactions in the block to the index. + newBlockID := idx.curBlockID + 1 + if err := dbAddTxIndexEntries(dbTx, block, newBlockID); err != nil { + return err + } + + // Add the new block ID index entry for the block being connected and + // update the current internal block ID accordingly. + err := dbPutBlockIDIndexEntry(dbTx, block.Sha(), newBlockID) + if err != nil { + return err + } + idx.curBlockID = newBlockID + return nil +} + +// DisconnectBlock is invoked by the index manager when a block has been +// disconnected from the main chain. This indexer removes the +// hash-to-transaction mapping for every transaction in the block. +// +// This is part of the Indexer interface. +func (idx *TxIndex) DisconnectBlock(dbTx database.Tx, block *btcutil.Block, view *blockchain.UtxoViewpoint) error { + // Remove all of the transactions in the block from the index. + if err := dbRemoveTxIndexEntries(dbTx, block); err != nil { + return err + } + + // Remove the block ID index entry for the block being disconnected and + // decrement the current internal block ID to account for it. + if err := dbRemoveBlockIDIndexEntry(dbTx, block.Sha()); err != nil { + return err + } + idx.curBlockID-- + return nil +} + +// TxBlockRegion returns the block region for the provided transaction hash +// from the transaction index. The block region can in turn be used to load the +// raw transaction bytes. When there is no entry for the provided hash, nil +// will be returned for the both the entry and the error. +// +// This function is safe for concurrent access. +func (idx *TxIndex) TxBlockRegion(hash *wire.ShaHash) (*database.BlockRegion, error) { + var region *database.BlockRegion + err := idx.db.View(func(dbTx database.Tx) error { + var err error + region, err = dbFetchTxIndexEntry(dbTx, hash) + return err + }) + return region, err +} + +// NewTxIndex returns a new instance of an indexer that is used to create a +// mapping of the hashes of all transactions in the blockchain to the respective +// block, location within the block, and size of the transaction. +// +// It implements the Indexer interface which plugs into the IndexManager that in +// turn is used by the blockchain package. This allows the index to be +// seamlessly maintained along with the chain. +func NewTxIndex(db database.DB) *TxIndex { + return &TxIndex{db: db} +} + +// dropBlockIDIndex drops the internal block id index. +func dropBlockIDIndex(db database.DB) error { + return db.Update(func(dbTx database.Tx) error { + meta := dbTx.Metadata() + err := meta.DeleteBucket(idByHashIndexBucketName) + if err != nil { + return err + } + + return meta.DeleteBucket(hashByIDIndexBucketName) + }) +} + +// DropTxIndex drops the transaction index from the provided database if it +// exists. Since the address index relies on it, the address index will also be +// dropped when it exists. +func DropTxIndex(db database.DB) error { + if err := dropIndex(db, addrIndexKey, addrIndexName); err != nil { + return err + } + + return dropIndex(db, txIndexKey, txIndexName) +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/internal_test.go b/vendor/github.com/btcsuite/btcd/blockchain/internal_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2f681f2c5dec5e1ce7b1587d13a77590650c1d4a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/internal_test.go @@ -0,0 +1,48 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +This test file is part of the blockchain package rather than than the +blockchain_test package so it can bridge access to the internals to properly +test cases which are either not possible or can't reliably be tested via the +public interface. The functions are only exported while the tests are being +run. +*/ + +package blockchain + +import ( + "sort" + "time" +) + +// TstSetCoinbaseMaturity makes the ability to set the coinbase maturity +// available to the test package. +func TstSetCoinbaseMaturity(maturity int32) { + coinbaseMaturity = maturity +} + +// TstTimeSorter makes the internal timeSorter type available to the test +// package. +func TstTimeSorter(times []time.Time) sort.Interface { + return timeSorter(times) +} + +// TstCheckSerializedHeight makes the internal checkSerializedHeight function +// available to the test package. +var TstCheckSerializedHeight = checkSerializedHeight + +// TstSetMaxMedianTimeEntries makes the ability to set the maximum number of +// median time entries available to the test package. +func TstSetMaxMedianTimeEntries(val int) { + maxMedianTimeEntries = val +} + +// TstCheckBlockScripts makes the internal checkBlockScripts function available +// to the test package. +var TstCheckBlockScripts = checkBlockScripts + +// TstDeserializeUtxoEntry makes the internal deserializeUtxoEntry function +// available to the test package. +var TstDeserializeUtxoEntry = deserializeUtxoEntry diff --git a/vendor/github.com/btcsuite/btcd/blockchain/log.go b/vendor/github.com/btcsuite/btcd/blockchain/log.go new file mode 100644 index 0000000000000000000000000000000000000000..c80e4d9eb515654b0c8b4497f2e9b5d090fd0b97 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/log.go @@ -0,0 +1,71 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "errors" + "io" + + "github.com/btcsuite/btclog" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + DisableLog() +} + +// DisableLog disables all library log output. Logging output is disabled +// by default until either UseLogger or SetLogWriter are called. +func DisableLog() { + log = btclog.Disabled +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} + +// SetLogWriter uses a specified io.Writer to output package logging info. +// This allows a caller to direct package logging output without needing a +// dependency on seelog. If the caller is also using btclog, UseLogger should +// be used instead. +func SetLogWriter(w io.Writer, level string) error { + if w == nil { + return errors.New("nil writer") + } + + lvl, ok := btclog.LogLevelFromString(level) + if !ok { + return errors.New("invalid log level") + } + + l, err := btclog.NewLoggerFromWriter(w, lvl) + if err != nil { + return err + } + + UseLogger(l) + return nil +} + +// LogClosure is a closure that can be printed with %v to be used to +// generate expensive-to-create data for a detailed log level and avoid doing +// the work if the data isn't printed. +type logClosure func() string + +func (c logClosure) String() string { + return c() +} + +func newLogClosure(c func() string) logClosure { + return logClosure(c) +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/mediantime.go b/vendor/github.com/btcsuite/btcd/blockchain/mediantime.go new file mode 100644 index 0000000000000000000000000000000000000000..ac0689e28553640b94fa5fbfa2210b8b3286723c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/mediantime.go @@ -0,0 +1,218 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "math" + "sort" + "sync" + "time" +) + +const ( + // maxAllowedOffsetSeconds is the maximum number of seconds in either + // direction that local clock will be adjusted. When the median time + // of the network is outside of this range, no offset will be applied. + maxAllowedOffsetSecs = 70 * 60 // 1 hour 10 minutes + + // similarTimeSecs is the number of seconds in either direction from the + // local clock that is used to determine that it is likley wrong and + // hence to show a warning. + similarTimeSecs = 5 * 60 // 5 minutes +) + +var ( + // maxMedianTimeEntries is the maximum number of entries allowed in the + // median time data. This is a variable as opposed to a constant so the + // test code can modify it. + maxMedianTimeEntries = 200 +) + +// MedianTimeSource provides a mechanism to add several time samples which are +// used to determine a median time which is then used as an offset to the local +// clock. +type MedianTimeSource interface { + // AdjustedTime returns the current time adjusted by the median time + // offset as calculated from the time samples added by AddTimeSample. + AdjustedTime() time.Time + + // AddTimeSample adds a time sample that is used when determining the + // median time of the added samples. + AddTimeSample(id string, timeVal time.Time) + + // Offset returns the number of seconds to adjust the local clock based + // upon the median of the time samples added by AddTimeData. + Offset() time.Duration +} + +// int64Sorter implements sort.Interface to allow a slice of 64-bit integers to +// be sorted. +type int64Sorter []int64 + +// Len returns the number of 64-bit integers in the slice. It is part of the +// sort.Interface implementation. +func (s int64Sorter) Len() int { + return len(s) +} + +// Swap swaps the 64-bit integers at the passed indices. It is part of the +// sort.Interface implementation. +func (s int64Sorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Less returns whether the 64-bit integer with index i should sort before the +// 64-bit integer with index j. It is part of the sort.Interface +// implementation. +func (s int64Sorter) Less(i, j int) bool { + return s[i] < s[j] +} + +// medianTime provides an implementation of the MedianTimeSource interface. +// It is limited to maxMedianTimeEntries includes the same buggy behavior as +// the time offset mechanism in Bitcoin Core. This is necessary because it is +// used in the consensus code. +type medianTime struct { + mtx sync.Mutex + knownIDs map[string]struct{} + offsets []int64 + offsetSecs int64 + invalidTimeChecked bool +} + +// Ensure the medianTime type implements the MedianTimeSource interface. +var _ MedianTimeSource = (*medianTime)(nil) + +// AdjustedTime returns the current time adjusted by the median time offset as +// calculated from the time samples added by AddTimeSample. +// +// This function is safe for concurrent access and is part of the +// MedianTimeSource interface implementation. +func (m *medianTime) AdjustedTime() time.Time { + m.mtx.Lock() + defer m.mtx.Unlock() + + // Limit the adjusted time to 1 second precision. + now := time.Unix(time.Now().Unix(), 0) + return now.Add(time.Duration(m.offsetSecs) * time.Second) +} + +// AddTimeSample adds a time sample that is used when determining the median +// time of the added samples. +// +// This function is safe for concurrent access and is part of the +// MedianTimeSource interface implementation. +func (m *medianTime) AddTimeSample(sourceID string, timeVal time.Time) { + m.mtx.Lock() + defer m.mtx.Unlock() + + // Don't add time data from the same source. + if _, exists := m.knownIDs[sourceID]; exists { + return + } + m.knownIDs[sourceID] = struct{}{} + + // Truncate the provided offset to seconds and append it to the slice + // of offsets while respecting the maximum number of allowed entries by + // replacing the oldest entry with the new entry once the maximum number + // of entries is reached. + now := time.Unix(time.Now().Unix(), 0) + offsetSecs := int64(timeVal.Sub(now).Seconds()) + numOffsets := len(m.offsets) + if numOffsets == maxMedianTimeEntries && maxMedianTimeEntries > 0 { + m.offsets = m.offsets[1:] + numOffsets-- + } + m.offsets = append(m.offsets, offsetSecs) + numOffsets++ + + // Sort the offsets so the median can be obtained as needed later. + sortedOffsets := make([]int64, numOffsets) + copy(sortedOffsets, m.offsets) + sort.Sort(int64Sorter(sortedOffsets)) + + offsetDuration := time.Duration(offsetSecs) * time.Second + log.Debugf("Added time sample of %v (total: %v)", offsetDuration, + numOffsets) + + // NOTE: The following code intentionally has a bug to mirror the + // buggy behavior in Bitcoin Core since the median time is used in the + // consensus rules. + // + // In particular, the offset is only updated when the number of entries + // is odd, but the max number of entries is 200, an even number. Thus, + // the offset will never be updated again once the max number of entries + // is reached. + + // The median offset is only updated when there are enough offsets and + // the number of offsets is odd so the middle value is the true median. + // Thus, there is nothing to do when those conditions are not met. + if numOffsets < 5 || numOffsets&0x01 != 1 { + return + } + + // At this point the number of offsets in the list is odd, so the + // middle value of the sorted offsets is the median. + median := sortedOffsets[numOffsets/2] + + // Set the new offset when the median offset is within the allowed + // offset range. + if math.Abs(float64(median)) < maxAllowedOffsetSecs { + m.offsetSecs = median + } else { + // The median offset of all added time data is larger than the + // maximum allowed offset, so don't use an offset. This + // effectively limits how far the local clock can be skewed. + m.offsetSecs = 0 + + if !m.invalidTimeChecked { + m.invalidTimeChecked = true + + // Find if any time samples have a time that is close + // to the local time. + var remoteHasCloseTime bool + for _, offset := range sortedOffsets { + if math.Abs(float64(offset)) < similarTimeSecs { + remoteHasCloseTime = true + break + } + } + + // Warn if none of the time samples are close. + if !remoteHasCloseTime { + log.Warnf("Please check your date and time " + + "are correct! btcd will not work " + + "properly with an invalid time") + } + } + } + + medianDuration := time.Duration(m.offsetSecs) * time.Second + log.Debugf("New time offset: %v", medianDuration) +} + +// Offset returns the number of seconds to adjust the local clock based upon the +// median of the time samples added by AddTimeData. +// +// This function is safe for concurrent access and is part of the +// MedianTimeSource interface implementation. +func (m *medianTime) Offset() time.Duration { + m.mtx.Lock() + defer m.mtx.Unlock() + + return time.Duration(m.offsetSecs) * time.Second +} + +// NewMedianTime returns a new instance of concurrency-safe implementation of +// the MedianTimeSource interface. The returned implementation contains the +// rules necessary for proper time handling in the chain consensus rules and +// expects the time samples to be added from the timestamp field of the version +// message received from remote peers that successfully connect and negotiate. +func NewMedianTime() MedianTimeSource { + return &medianTime{ + knownIDs: make(map[string]struct{}), + offsets: make([]int64, 0, maxMedianTimeEntries), + } +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/mediantime_test.go b/vendor/github.com/btcsuite/btcd/blockchain/mediantime_test.go new file mode 100644 index 0000000000000000000000000000000000000000..44d3105a2e07b4fbc6f0b3a5f9eddf9724b8af2e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/mediantime_test.go @@ -0,0 +1,106 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain_test + +import ( + "strconv" + "testing" + "time" + + "github.com/btcsuite/btcd/blockchain" +) + +// TestMedianTime tests the medianTime implementation. +func TestMedianTime(t *testing.T) { + tests := []struct { + in []int64 + wantOffset int64 + useDupID bool + }{ + // Not enough samples must result in an offset of 0. + {in: []int64{1}, wantOffset: 0}, + {in: []int64{1, 2}, wantOffset: 0}, + {in: []int64{1, 2, 3}, wantOffset: 0}, + {in: []int64{1, 2, 3, 4}, wantOffset: 0}, + + // Various number of entries. The expected offset is only + // updated on odd number of elements. + {in: []int64{-13, 57, -4, -23, -12}, wantOffset: -12}, + {in: []int64{55, -13, 61, -52, 39, 55}, wantOffset: 39}, + {in: []int64{-62, -58, -30, -62, 51, -30, 15}, wantOffset: -30}, + {in: []int64{29, -47, 39, 54, 42, 41, 8, -33}, wantOffset: 39}, + {in: []int64{37, 54, 9, -21, -56, -36, 5, -11, -39}, wantOffset: -11}, + {in: []int64{57, -28, 25, -39, 9, 63, -16, 19, -60, 25}, wantOffset: 9}, + {in: []int64{-5, -4, -3, -2, -1}, wantOffset: -3, useDupID: true}, + + // The offset stops being updated once the max number of entries + // has been reached. This is actually a bug from Bitcoin Core, + // but since the time is ultimately used as a part of the + // consensus rules, it must be mirrored. + {in: []int64{-67, 67, -50, 24, 63, 17, 58, -14, 5, -32, -52}, wantOffset: 17}, + {in: []int64{-67, 67, -50, 24, 63, 17, 58, -14, 5, -32, -52, 45}, wantOffset: 17}, + {in: []int64{-67, 67, -50, 24, 63, 17, 58, -14, 5, -32, -52, 45, 4}, wantOffset: 17}, + + // Offsets that are too far away from the local time should + // be ignored. + {in: []int64{-4201, 4202, -4203, 4204, -4205}, wantOffset: 0}, + + // Excerise the condition where the median offset is greater + // than the max allowed adjustment, but there is at least one + // sample that is close enough to the current time to avoid + // triggering a warning about an invalid local clock. + {in: []int64{4201, 4202, 4203, 4204, -299}, wantOffset: 0}, + } + + // Modify the max number of allowed median time entries for these tests. + blockchain.TstSetMaxMedianTimeEntries(10) + defer blockchain.TstSetMaxMedianTimeEntries(200) + + for i, test := range tests { + filter := blockchain.NewMedianTime() + for j, offset := range test.in { + id := strconv.Itoa(j) + now := time.Unix(time.Now().Unix(), 0) + tOffset := now.Add(time.Duration(offset) * time.Second) + filter.AddTimeSample(id, tOffset) + + // Ensure the duplicate IDs are ignored. + if test.useDupID { + // Modify the offsets to ensure the final median + // would be different if the duplicate is added. + tOffset = tOffset.Add(time.Duration(offset) * + time.Second) + filter.AddTimeSample(id, tOffset) + } + } + + // Since it is possible that the time.Now call in AddTimeSample + // and the time.Now calls here in the tests will be off by one + // second, allow a fudge factor to compensate. + gotOffset := filter.Offset() + wantOffset := time.Duration(test.wantOffset) * time.Second + wantOffset2 := time.Duration(test.wantOffset-1) * time.Second + if gotOffset != wantOffset && gotOffset != wantOffset2 { + t.Errorf("Offset #%d: unexpected offset -- got %v, "+ + "want %v or %v", i, gotOffset, wantOffset, + wantOffset2) + continue + } + + // Since it is possible that the time.Now call in AdjustedTime + // and the time.Now call here in the tests will be off by one + // second, allow a fudge factor to compensate. + adjustedTime := filter.AdjustedTime() + now := time.Unix(time.Now().Unix(), 0) + wantTime := now.Add(filter.Offset()) + wantTime2 := now.Add(filter.Offset() - time.Second) + if !adjustedTime.Equal(wantTime) && !adjustedTime.Equal(wantTime2) { + t.Errorf("AdjustedTime #%d: unexpected result -- got %v, "+ + "want %v or %v", i, adjustedTime, wantTime, + wantTime2) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/merkle.go b/vendor/github.com/btcsuite/btcd/blockchain/merkle.go new file mode 100644 index 0000000000000000000000000000000000000000..2dc00018a8783000aa27e1866214f736de22ddbe --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/merkle.go @@ -0,0 +1,106 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "math" + + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// nextPowerOfTwo returns the next highest power of two from a given number if +// it is not already a power of two. This is a helper function used during the +// calculation of a merkle tree. +func nextPowerOfTwo(n int) int { + // Return the number if it's already a power of 2. + if n&(n-1) == 0 { + return n + } + + // Figure out and return the next power of two. + exponent := uint(math.Log2(float64(n))) + 1 + return 1 << exponent // 2^exponent +} + +// HashMerkleBranches takes two hashes, treated as the left and right tree +// nodes, and returns the hash of their concatenation. This is a helper +// function used to aid in the generation of a merkle tree. +func HashMerkleBranches(left *wire.ShaHash, right *wire.ShaHash) *wire.ShaHash { + // Concatenate the left and right nodes. + var sha [wire.HashSize * 2]byte + copy(sha[:wire.HashSize], left[:]) + copy(sha[wire.HashSize:], right[:]) + + newSha := wire.DoubleSha256SH(sha[:]) + return &newSha +} + +// BuildMerkleTreeStore creates a merkle tree from a slice of transactions, +// stores it using a linear array, and returns a slice of the backing array. A +// linear array was chosen as opposed to an actual tree structure since it uses +// about half as much memory. The following describes a merkle tree and how it +// is stored in a linear array. +// +// A merkle tree is a tree in which every non-leaf node is the hash of its +// children nodes. A diagram depicting how this works for bitcoin transactions +// where h(x) is a double sha256 follows: +// +// root = h1234 = h(h12 + h34) +// / \ +// h12 = h(h1 + h2) h34 = h(h3 + h4) +// / \ / \ +// h1 = h(tx1) h2 = h(tx2) h3 = h(tx3) h4 = h(tx4) +// +// The above stored as a linear array is as follows: +// +// [h1 h2 h3 h4 h12 h34 root] +// +// As the above shows, the merkle root is always the last element in the array. +// +// The number of inputs is not always a power of two which results in a +// balanced tree structure as above. In that case, parent nodes with no +// children are also zero and parent nodes with only a single left node +// are calculated by concatenating the left node with itself before hashing. +// Since this function uses nodes that are pointers to the hashes, empty nodes +// will be nil. +func BuildMerkleTreeStore(transactions []*btcutil.Tx) []*wire.ShaHash { + // Calculate how many entries are required to hold the binary merkle + // tree as a linear array and create an array of that size. + nextPoT := nextPowerOfTwo(len(transactions)) + arraySize := nextPoT*2 - 1 + merkles := make([]*wire.ShaHash, arraySize) + + // Create the base transaction shas and populate the array with them. + for i, tx := range transactions { + merkles[i] = tx.Sha() + } + + // Start the array offset after the last transaction and adjusted to the + // next power of two. + offset := nextPoT + for i := 0; i < arraySize-1; i += 2 { + switch { + // When there is no left child node, the parent is nil too. + case merkles[i] == nil: + merkles[offset] = nil + + // When there is no right child, the parent is generated by + // hashing the concatenation of the left child with itself. + case merkles[i+1] == nil: + newSha := HashMerkleBranches(merkles[i], merkles[i]) + merkles[offset] = newSha + + // The normal case sets the parent node to the double sha256 + // of the concatentation of the left and right children. + default: + newSha := HashMerkleBranches(merkles[i], merkles[i+1]) + merkles[offset] = newSha + } + offset++ + } + + return merkles +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/merkle_test.go b/vendor/github.com/btcsuite/btcd/blockchain/merkle_test.go new file mode 100644 index 0000000000000000000000000000000000000000..633be110e778ff586d1fd1713b79cb79a95385ce --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/merkle_test.go @@ -0,0 +1,24 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain_test + +import ( + "testing" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcutil" +) + +// TestMerkle tests the BuildMerkleTreeStore API. +func TestMerkle(t *testing.T) { + block := btcutil.NewBlock(&Block100000) + merkles := blockchain.BuildMerkleTreeStore(block.Transactions()) + calculatedMerkleRoot := merkles[len(merkles)-1] + wantMerkle := &Block100000.Header.MerkleRoot + if !wantMerkle.IsEqual(calculatedMerkleRoot) { + t.Errorf("BuildMerkleTreeStore: merkle root mismatch - "+ + "got %v, want %v", calculatedMerkleRoot, wantMerkle) + } +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/notifications.go b/vendor/github.com/btcsuite/btcd/blockchain/notifications.go new file mode 100644 index 0000000000000000000000000000000000000000..e55ed2632617659d920cac31cdf8622331218c99 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/notifications.go @@ -0,0 +1,73 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "fmt" +) + +// NotificationType represents the type of a notification message. +type NotificationType int + +// NotificationCallback is used for a caller to provide a callback for +// notifications about various chain events. +type NotificationCallback func(*Notification) + +// Constants for the type of a notification message. +const ( + // NTBlockAccepted indicates the associated block was accepted into + // the block chain. Note that this does not necessarily mean it was + // added to the main chain. For that, use NTBlockConnected. + NTBlockAccepted NotificationType = iota + + // NTBlockConnected indicates the associated block was connected to the + // main chain. + NTBlockConnected + + // NTBlockDisconnected indicates the associated block was disconnected + // from the main chain. + NTBlockDisconnected +) + +// notificationTypeStrings is a map of notification types back to their constant +// names for pretty printing. +var notificationTypeStrings = map[NotificationType]string{ + NTBlockAccepted: "NTBlockAccepted", + NTBlockConnected: "NTBlockConnected", + NTBlockDisconnected: "NTBlockDisconnected", +} + +// String returns the NotificationType in human-readable form. +func (n NotificationType) String() string { + if s, ok := notificationTypeStrings[n]; ok { + return s + } + return fmt.Sprintf("Unknown Notification Type (%d)", int(n)) +} + +// Notification defines notification that is sent to the caller via the callback +// function provided during the call to New and consists of a notification type +// as well as associated data that depends on the type as follows: +// - NTBlockAccepted: *btcutil.Block +// - NTBlockConnected: *btcutil.Block +// - NTBlockDisconnected: *btcutil.Block +type Notification struct { + Type NotificationType + Data interface{} +} + +// sendNotification sends a notification with the passed type and data if the +// caller requested notifications by providing a callback function in the call +// to New. +func (b *BlockChain) sendNotification(typ NotificationType, data interface{}) { + // Ignore it if the caller didn't request notifications. + if b.notifications == nil { + return + } + + // Generate and send the notification. + n := Notification{Type: typ, Data: data} + b.notifications(&n) +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/process.go b/vendor/github.com/btcsuite/btcd/blockchain/process.go new file mode 100644 index 0000000000000000000000000000000000000000..930966f152897308d3f44ec5eae2d8fe635be000 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/process.go @@ -0,0 +1,240 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "fmt" + + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// BehaviorFlags is a bitmask defining tweaks to the normal behavior when +// performing chain processing and consensus rules checks. +type BehaviorFlags uint32 + +const ( + // BFFastAdd may be set to indicate that several checks can be avoided + // for the block since it is already known to fit into the chain due to + // already proving it correct links into the chain up to a known + // checkpoint. This is primarily used for headers-first mode. + BFFastAdd BehaviorFlags = 1 << iota + + // BFNoPoWCheck may be set to indicate the proof of work check which + // ensures a block hashes to a value less than the required target will + // not be performed. + BFNoPoWCheck + + // BFDryRun may be set to indicate the block should not modify the chain + // or memory chain index. This is useful to test that a block is valid + // without modifying the current state. + BFDryRun + + // BFNone is a convenience value to specifically indicate no flags. + BFNone BehaviorFlags = 0 +) + +// blockExists determines whether a block with the given hash exists either in +// the main chain or any side chains. +// +// This function MUST be called with the chain state lock held (for reads). +func (b *BlockChain) blockExists(hash *wire.ShaHash) (bool, error) { + // Check memory chain first (could be main chain or side chain blocks). + if _, ok := b.index[*hash]; ok { + return true, nil + } + + // Check in the database. + var exists bool + err := b.db.View(func(dbTx database.Tx) error { + var err error + exists, err = dbTx.HasBlock(hash) + return err + }) + return exists, err +} + +// processOrphans determines if there are any orphans which depend on the passed +// block hash (they are no longer orphans if true) and potentially accepts them. +// It repeats the process for the newly accepted blocks (to detect further +// orphans which may no longer be orphans) until there are no more. +// +// The flags do not modify the behavior of this function directly, however they +// are needed to pass along to maybeAcceptBlock. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) processOrphans(hash *wire.ShaHash, flags BehaviorFlags) error { + // Start with processing at least the passed hash. Leave a little room + // for additional orphan blocks that need to be processed without + // needing to grow the array in the common case. + processHashes := make([]*wire.ShaHash, 0, 10) + processHashes = append(processHashes, hash) + for len(processHashes) > 0 { + // Pop the first hash to process from the slice. + processHash := processHashes[0] + processHashes[0] = nil // Prevent GC leak. + processHashes = processHashes[1:] + + // Look up all orphans that are parented by the block we just + // accepted. This will typically only be one, but it could + // be multiple if multiple blocks are mined and broadcast + // around the same time. The one with the most proof of work + // will eventually win out. An indexing for loop is + // intentionally used over a range here as range does not + // reevaluate the slice on each iteration nor does it adjust the + // index for the modified slice. + for i := 0; i < len(b.prevOrphans[*processHash]); i++ { + orphan := b.prevOrphans[*processHash][i] + if orphan == nil { + log.Warnf("Found a nil entry at index %d in the "+ + "orphan dependency list for block %v", i, + processHash) + continue + } + + // Remove the orphan from the orphan pool. + orphanHash := orphan.block.Sha() + b.removeOrphanBlock(orphan) + i-- + + // Potentially accept the block into the block chain. + err := b.maybeAcceptBlock(orphan.block, flags) + if err != nil { + return err + } + + // Add this block to the list of blocks to process so + // any orphan blocks that depend on this block are + // handled too. + processHashes = append(processHashes, orphanHash) + } + } + return nil +} + +// ProcessBlock is the main workhorse for handling insertion of new blocks into +// the block chain. It includes functionality such as rejecting duplicate +// blocks, ensuring blocks follow all rules, orphan handling, and insertion into +// the block chain along with best chain selection and reorganization. +// +// It returns a bool which indicates whether or not the block is an orphan and +// any errors that occurred during processing. The returned bool is only valid +// when the error is nil. +// +// This function is safe for concurrent access. +func (b *BlockChain) ProcessBlock(block *btcutil.Block, timeSource MedianTimeSource, flags BehaviorFlags) (bool, error) { + b.chainLock.Lock() + defer b.chainLock.Unlock() + + fastAdd := flags&BFFastAdd == BFFastAdd + dryRun := flags&BFDryRun == BFDryRun + + blockHash := block.Sha() + log.Tracef("Processing block %v", blockHash) + + // The block must not already exist in the main chain or side chains. + exists, err := b.blockExists(blockHash) + if err != nil { + return false, err + } + if exists { + str := fmt.Sprintf("already have block %v", blockHash) + return false, ruleError(ErrDuplicateBlock, str) + } + + // The block must not already exist as an orphan. + if _, exists := b.orphans[*blockHash]; exists { + str := fmt.Sprintf("already have block (orphan) %v", blockHash) + return false, ruleError(ErrDuplicateBlock, str) + } + + // Perform preliminary sanity checks on the block and its transactions. + err = checkBlockSanity(block, b.chainParams.PowLimit, timeSource, flags) + if err != nil { + return false, err + } + + // Find the previous checkpoint and perform some additional checks based + // on the checkpoint. This provides a few nice properties such as + // preventing old side chain blocks before the last checkpoint, + // rejecting easy to mine, but otherwise bogus, blocks that could be + // used to eat memory, and ensuring expected (versus claimed) proof of + // work requirements since the previous checkpoint are met. + blockHeader := &block.MsgBlock().Header + checkpointBlock, err := b.findPreviousCheckpoint() + if err != nil { + return false, err + } + if checkpointBlock != nil { + // Ensure the block timestamp is after the checkpoint timestamp. + checkpointHeader := &checkpointBlock.MsgBlock().Header + checkpointTime := checkpointHeader.Timestamp + if blockHeader.Timestamp.Before(checkpointTime) { + str := fmt.Sprintf("block %v has timestamp %v before "+ + "last checkpoint timestamp %v", blockHash, + blockHeader.Timestamp, checkpointTime) + return false, ruleError(ErrCheckpointTimeTooOld, str) + } + if !fastAdd { + // Even though the checks prior to now have already ensured the + // proof of work exceeds the claimed amount, the claimed amount + // is a field in the block header which could be forged. This + // check ensures the proof of work is at least the minimum + // expected based on elapsed time since the last checkpoint and + // maximum adjustment allowed by the retarget rules. + duration := blockHeader.Timestamp.Sub(checkpointTime) + requiredTarget := CompactToBig(b.calcEasiestDifficulty( + checkpointHeader.Bits, duration)) + currentTarget := CompactToBig(blockHeader.Bits) + if currentTarget.Cmp(requiredTarget) > 0 { + str := fmt.Sprintf("block target difficulty of %064x "+ + "is too low when compared to the previous "+ + "checkpoint", currentTarget) + return false, ruleError(ErrDifficultyTooLow, str) + } + } + } + + // Handle orphan blocks. + prevHash := &blockHeader.PrevBlock + if !prevHash.IsEqual(zeroHash) { + prevHashExists, err := b.blockExists(prevHash) + if err != nil { + return false, err + } + if !prevHashExists { + if !dryRun { + log.Infof("Adding orphan block %v with parent %v", + blockHash, prevHash) + b.addOrphanBlock(block) + } + + return true, nil + } + } + + // The block has passed all context independent checks and appears sane + // enough to potentially accept it into the block chain. + err = b.maybeAcceptBlock(block, flags) + if err != nil { + return false, err + } + + // Don't process any orphans or log when the dry run flag is set. + if !dryRun { + // Accept any orphan blocks that depend on this block (they are + // no longer orphans) and repeat for those accepted blocks until + // there are no more. + err := b.processOrphans(blockHash, flags) + if err != nil { + return false, err + } + + log.Debugf("Accepted block %v", blockHash) + } + + return false, nil +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/reorganization_test.go b/vendor/github.com/btcsuite/btcd/blockchain/reorganization_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5ae4d3b2f44e4f2fd775a8001bd9e47fb337a6e8 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/reorganization_test.go @@ -0,0 +1,134 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain_test + +import ( + "compress/bzip2" + "encoding/binary" + "io" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// TestReorganization loads a set of test blocks which force a chain +// reorganization to test the block chain handling code. +// The test blocks were originally from a post on the bitcoin talk forums: +// https://bitcointalk.org/index.php?topic=46370.msg577556#msg577556 +func TestReorganization(t *testing.T) { + // Intentionally load the side chain blocks out of order to ensure + // orphans are handled properly along with chain reorganization. + testFiles := []string{ + "blk_0_to_4.dat.bz2", + "blk_4A.dat.bz2", + "blk_5A.dat.bz2", + "blk_3A.dat.bz2", + } + + var blocks []*btcutil.Block + for _, file := range testFiles { + blockTmp, err := loadBlocks(file) + if err != nil { + t.Errorf("Error loading file: %v\n", err) + } + for _, block := range blockTmp { + blocks = append(blocks, block) + } + } + + t.Logf("Number of blocks: %v\n", len(blocks)) + + // Create a new database and chain instance to run tests against. + chain, teardownFunc, err := chainSetup("reorg") + if err != nil { + t.Errorf("Failed to setup chain instance: %v", err) + return + } + defer teardownFunc() + + // Since we're not dealing with the real block chain, disable + // checkpoints and set the coinbase maturity to 1. + chain.DisableCheckpoints(true) + blockchain.TstSetCoinbaseMaturity(1) + + timeSource := blockchain.NewMedianTime() + expectedOrphans := map[int]struct{}{5: {}, 6: {}} + for i := 1; i < len(blocks); i++ { + isOrphan, err := chain.ProcessBlock(blocks[i], timeSource, blockchain.BFNone) + if err != nil { + t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) + return + } + if _, ok := expectedOrphans[i]; !ok && isOrphan { + t.Errorf("ProcessBlock incorrectly returned block %v "+ + "is an orphan\n", i) + } + } + + return +} + +// loadBlocks reads files containing bitcoin block data (gzipped but otherwise +// in the format bitcoind writes) from disk and returns them as an array of +// btcutil.Block. This is largely borrowed from the test code in btcdb. +func loadBlocks(filename string) (blocks []*btcutil.Block, err error) { + filename = filepath.Join("testdata/", filename) + + var network = wire.MainNet + var dr io.Reader + var fi io.ReadCloser + + fi, err = os.Open(filename) + if err != nil { + return + } + + if strings.HasSuffix(filename, ".bz2") { + dr = bzip2.NewReader(fi) + } else { + dr = fi + } + defer fi.Close() + + var block *btcutil.Block + + err = nil + for height := int64(1); err == nil; height++ { + var rintbuf uint32 + err = binary.Read(dr, binary.LittleEndian, &rintbuf) + if err == io.EOF { + // hit end of file at expected offset: no warning + height-- + err = nil + break + } + if err != nil { + break + } + if rintbuf != uint32(network) { + break + } + err = binary.Read(dr, binary.LittleEndian, &rintbuf) + blocklen := rintbuf + + rbytes := make([]byte, blocklen) + + // read block + dr.Read(rbytes) + + block, err = btcutil.NewBlockFromBytes(rbytes) + if err != nil { + return + } + blocks = append(blocks, block) + } + + return +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/scriptval.go b/vendor/github.com/btcsuite/btcd/blockchain/scriptval.go new file mode 100644 index 0000000000000000000000000000000000000000..87a68a7b4fcc839217616a3bf3877768cb817b0e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/scriptval.go @@ -0,0 +1,256 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "fmt" + "math" + "runtime" + + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// txValidateItem holds a transaction along with which input to validate. +type txValidateItem struct { + txInIndex int + txIn *wire.TxIn + tx *btcutil.Tx +} + +// txValidator provides a type which asynchronously validates transaction +// inputs. It provides several channels for communication and a processing +// function that is intended to be in run multiple goroutines. +type txValidator struct { + validateChan chan *txValidateItem + quitChan chan struct{} + resultChan chan error + utxoView *UtxoViewpoint + flags txscript.ScriptFlags + sigCache *txscript.SigCache +} + +// sendResult sends the result of a script pair validation on the internal +// result channel while respecting the quit channel. The allows orderly +// shutdown when the validation process is aborted early due to a validation +// error in one of the other goroutines. +func (v *txValidator) sendResult(result error) { + select { + case v.resultChan <- result: + case <-v.quitChan: + } +} + +// validateHandler consumes items to validate from the internal validate channel +// and returns the result of the validation on the internal result channel. It +// must be run as a goroutine. +func (v *txValidator) validateHandler() { +out: + for { + select { + case txVI := <-v.validateChan: + // Ensure the referenced input transaction is available. + txIn := txVI.txIn + originTxHash := &txIn.PreviousOutPoint.Hash + originTxIndex := txIn.PreviousOutPoint.Index + txEntry := v.utxoView.LookupEntry(originTxHash) + if txEntry == nil { + str := fmt.Sprintf("unable to find input "+ + "transaction %v referenced from "+ + "transaction %v", originTxHash, + txVI.tx.Sha()) + err := ruleError(ErrMissingTx, str) + v.sendResult(err) + break out + } + + // Ensure the referenced input transaction public key + // script is available. + pkScript := txEntry.PkScriptByIndex(originTxIndex) + if pkScript == nil { + str := fmt.Sprintf("unable to find unspent "+ + "output %v script referenced from "+ + "transaction %s:%d", + txIn.PreviousOutPoint, txVI.tx.Sha(), + txVI.txInIndex) + err := ruleError(ErrBadTxInput, str) + v.sendResult(err) + break out + } + + // Create a new script engine for the script pair. + sigScript := txIn.SignatureScript + vm, err := txscript.NewEngine(pkScript, txVI.tx.MsgTx(), + txVI.txInIndex, v.flags, v.sigCache) + if err != nil { + str := fmt.Sprintf("failed to parse input "+ + "%s:%d which references output %s:%d - "+ + "%v (input script bytes %x, prev output "+ + "script bytes %x)", txVI.tx.Sha(), + txVI.txInIndex, originTxHash, + originTxIndex, err, sigScript, pkScript) + err := ruleError(ErrScriptMalformed, str) + v.sendResult(err) + break out + } + + // Execute the script pair. + if err := vm.Execute(); err != nil { + str := fmt.Sprintf("failed to validate input "+ + "%s:%d which references output %s:%d - "+ + "%v (input script bytes %x, prev output "+ + "script bytes %x)", txVI.tx.Sha(), + txVI.txInIndex, originTxHash, + originTxIndex, err, sigScript, pkScript) + err := ruleError(ErrScriptValidation, str) + v.sendResult(err) + break out + } + + // Validation succeeded. + v.sendResult(nil) + + case <-v.quitChan: + break out + } + } +} + +// Validate validates the scripts for all of the passed transaction inputs using +// multiple goroutines. +func (v *txValidator) Validate(items []*txValidateItem) error { + if len(items) == 0 { + return nil + } + + // Limit the number of goroutines to do script validation based on the + // number of processor cores. This help ensure the system stays + // reasonably responsive under heavy load. + maxGoRoutines := runtime.NumCPU() * 3 + if maxGoRoutines <= 0 { + maxGoRoutines = 1 + } + if maxGoRoutines > len(items) { + maxGoRoutines = len(items) + } + + // Start up validation handlers that are used to asynchronously + // validate each transaction input. + for i := 0; i < maxGoRoutines; i++ { + go v.validateHandler() + } + + // Validate each of the inputs. The quit channel is closed when any + // errors occur so all processing goroutines exit regardless of which + // input had the validation error. + numInputs := len(items) + currentItem := 0 + processedItems := 0 + for processedItems < numInputs { + // Only send items while there are still items that need to + // be processed. The select statement will never select a nil + // channel. + var validateChan chan *txValidateItem + var item *txValidateItem + if currentItem < numInputs { + validateChan = v.validateChan + item = items[currentItem] + } + + select { + case validateChan <- item: + currentItem++ + + case err := <-v.resultChan: + processedItems++ + if err != nil { + close(v.quitChan) + return err + } + } + } + + close(v.quitChan) + return nil +} + +// newTxValidator returns a new instance of txValidator to be used for +// validating transaction scripts asynchronously. +func newTxValidator(utxoView *UtxoViewpoint, flags txscript.ScriptFlags, sigCache *txscript.SigCache) *txValidator { + return &txValidator{ + validateChan: make(chan *txValidateItem), + quitChan: make(chan struct{}), + resultChan: make(chan error), + utxoView: utxoView, + sigCache: sigCache, + flags: flags, + } +} + +// ValidateTransactionScripts validates the scripts for the passed transaction +// using multiple goroutines. +func ValidateTransactionScripts(tx *btcutil.Tx, utxoView *UtxoViewpoint, flags txscript.ScriptFlags, sigCache *txscript.SigCache) error { + // Collect all of the transaction inputs and required information for + // validation. + txIns := tx.MsgTx().TxIn + txValItems := make([]*txValidateItem, 0, len(txIns)) + for txInIdx, txIn := range txIns { + // Skip coinbases. + if txIn.PreviousOutPoint.Index == math.MaxUint32 { + continue + } + + txVI := &txValidateItem{ + txInIndex: txInIdx, + txIn: txIn, + tx: tx, + } + txValItems = append(txValItems, txVI) + } + + // Validate all of the inputs. + validator := newTxValidator(utxoView, flags, sigCache) + if err := validator.Validate(txValItems); err != nil { + return err + } + + return nil +} + +// checkBlockScripts executes and validates the scripts for all transactions in +// the passed block using multiple goroutines. +func checkBlockScripts(block *btcutil.Block, utxoView *UtxoViewpoint, scriptFlags txscript.ScriptFlags, sigCache *txscript.SigCache) error { + // Collect all of the transaction inputs and required information for + // validation for all transactions in the block into a single slice. + numInputs := 0 + for _, tx := range block.Transactions() { + numInputs += len(tx.MsgTx().TxIn) + } + txValItems := make([]*txValidateItem, 0, numInputs) + for _, tx := range block.Transactions() { + for txInIdx, txIn := range tx.MsgTx().TxIn { + // Skip coinbases. + if txIn.PreviousOutPoint.Index == math.MaxUint32 { + continue + } + + txVI := &txValidateItem{ + txInIndex: txInIdx, + txIn: txIn, + tx: tx, + } + txValItems = append(txValItems, txVI) + } + } + + // Validate all of the inputs. + validator := newTxValidator(utxoView, scriptFlags, sigCache) + if err := validator.Validate(txValItems); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/scriptval_test.go b/vendor/github.com/btcsuite/btcd/blockchain/scriptval_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d1babb941e0b9e76b53dc28cf8d42d7f3f39af42 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/scriptval_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain_test + +import ( + "fmt" + "runtime" + "testing" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/txscript" +) + +// TestCheckBlockScripts ensures that validating the all of the scripts in a +// known-good block doesn't return an error. +func TestCheckBlockScripts(t *testing.T) { + runtime.GOMAXPROCS(runtime.NumCPU()) + + testBlockNum := 277647 + blockDataFile := fmt.Sprintf("%d.dat.bz2", testBlockNum) + blocks, err := loadBlocks(blockDataFile) + if err != nil { + t.Errorf("Error loading file: %v\n", err) + return + } + if len(blocks) > 1 { + t.Errorf("The test block file must only have one block in it") + } + + storeDataFile := fmt.Sprintf("%d.utxostore.bz2", testBlockNum) + view, err := loadUtxoView(storeDataFile) + if err != nil { + t.Errorf("Error loading txstore: %v\n", err) + return + } + + scriptFlags := txscript.ScriptBip16 + err = blockchain.TstCheckBlockScripts(blocks[0], view, scriptFlags, + nil) + if err != nil { + t.Errorf("Transaction script validation failed: %v\n", err) + return + } +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/testdata/277647.dat.bz2 b/vendor/github.com/btcsuite/btcd/blockchain/testdata/277647.dat.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..598420a65ab8b2801d2d917147fb58e53ee6014a Binary files /dev/null and b/vendor/github.com/btcsuite/btcd/blockchain/testdata/277647.dat.bz2 differ diff --git a/vendor/github.com/btcsuite/btcd/blockchain/testdata/277647.utxostore.bz2 b/vendor/github.com/btcsuite/btcd/blockchain/testdata/277647.utxostore.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..3807a71299845e4c6dd7f8c037652b316e4c38ff Binary files /dev/null and b/vendor/github.com/btcsuite/btcd/blockchain/testdata/277647.utxostore.bz2 differ diff --git a/vendor/github.com/btcsuite/btcd/blockchain/testdata/blk_0_to_4.dat.bz2 b/vendor/github.com/btcsuite/btcd/blockchain/testdata/blk_0_to_4.dat.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..274c710d275f032791bcbd4b463be9c1901b85ed Binary files /dev/null and b/vendor/github.com/btcsuite/btcd/blockchain/testdata/blk_0_to_4.dat.bz2 differ diff --git a/vendor/github.com/btcsuite/btcd/blockchain/testdata/blk_3A.dat.bz2 b/vendor/github.com/btcsuite/btcd/blockchain/testdata/blk_3A.dat.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..01266565d573ca0f7e7601d011fb4f98e243445a Binary files /dev/null and b/vendor/github.com/btcsuite/btcd/blockchain/testdata/blk_3A.dat.bz2 differ diff --git a/vendor/github.com/btcsuite/btcd/blockchain/testdata/blk_4A.dat.bz2 b/vendor/github.com/btcsuite/btcd/blockchain/testdata/blk_4A.dat.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..19b409e752f37840b271e4e9f0b90dcd06e1cad3 Binary files /dev/null and b/vendor/github.com/btcsuite/btcd/blockchain/testdata/blk_4A.dat.bz2 differ diff --git a/vendor/github.com/btcsuite/btcd/blockchain/testdata/blk_5A.dat.bz2 b/vendor/github.com/btcsuite/btcd/blockchain/testdata/blk_5A.dat.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..47bff9038bdd596708d38440ed3a748e42c79988 Binary files /dev/null and b/vendor/github.com/btcsuite/btcd/blockchain/testdata/blk_5A.dat.bz2 differ diff --git a/vendor/github.com/btcsuite/btcd/blockchain/testdata/reorgtest.hex b/vendor/github.com/btcsuite/btcd/blockchain/testdata/reorgtest.hex new file mode 100644 index 0000000000000000000000000000000000000000..5b9e75e70b929f91c1c66baf2fd4d0b7be960314 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/testdata/reorgtest.hex @@ -0,0 +1,180 @@ +File path: reorgTest/blk_0_to_4.dat + +Block 0: + f9beb4d9 + 1d010000 + + 01000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + 00000000 3ba3edfd 7a7b12b2 7ac72c3e 67768f61 7fc81bc3 888a5132 3a9fb8aa + 4b1e5e4a 29ab5f49 ffff001d 1dac2b7c + 01 + + 01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000 + 00000000 00ffffff ff4d04ff ff001d01 04455468 65205469 6d657320 30332f4a + 616e2f32 30303920 4368616e 63656c6c 6f72206f 6e206272 696e6b20 6f662073 + 65636f6e 64206261 696c6f75 7420666f 72206261 6e6b73ff ffffff01 00f2052a + 01000000 43410467 8afdb0fe 55482719 67f1a671 30b7105c d6a828e0 3909a679 + 62e0ea1f 61deb649 f6bc3f4c ef38c4f3 5504e51e c112de5c 384df7ba 0b8d578a + 4c702b6b f11d5fac 00000000 +Block 1: + f9beb4d9 + d4000000 + + 01000000 6fe28c0a b6f1b372 c1a6a246 ae63f74f 931e8365 e15a089c 68d61900 + 00000000 3bbd67ad e98fbbb7 0718cd80 f9e9acf9 3b5fae91 7bb2b41d 4c3bb82c + 77725ca5 81ad5f49 ffff001d 44e69904 + 01 + + 01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000 + 00000000 00ffffff ff04722f 2e2bffff ffff0100 f2052a01 00000043 41046868 + 0737c76d abb801cb 2204f57d be4e4579 e4f710cd 67dc1b42 27592c81 e9b5cf02 + b5ac9e8b 4c9f49be 5251056b 6a6d011e 4c37f6b6 d17ede6b 55faa235 19e2ac00 + 000000 +Block 2: + f9beb4d9 + 95010000 + + 01000000 13ca7940 4c11c63e ca906bbd f190b751 2872b857 1b5143ae e8cb5737 + 00000000 fc07c983 d7391736 0aeda657 29d0d4d3 2533eb84 76ee9d64 aa27538f + 9b4fc00a d9af5f49 ffff001d 630bea22 + 02 + + 01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000 + 00000000 00ffffff ff04eb96 14e5ffff ffff0100 f2052a01 00000043 41046868 + 0737c76d abb801cb 2204f57d be4e4579 e4f710cd 67dc1b42 27592c81 e9b5cf02 + b5ac9e8b 4c9f49be 5251056b 6a6d011e 4c37f6b6 d17ede6b 55faa235 19e2ac00 + 000000 + + 01000000 0163451d 1002611c 1388d5ba 4ddfdf99 196a86b5 990fb5b0 dc786207 + 4fdcb8ee d2000000 004a4930 46022100 3dde52c6 5e339f45 7fe1015e 70eed208 + 872eb71e dd484c07 206b190e cb2ec3f8 02210011 c78dcfd0 3d43fa63 61242a33 + 6291ba2a 8c1ef5bc d5472126 2468f2bf 8dee4d01 ffffffff 0200ca9a 3b000000 + 001976a9 14cb2abd e8bccacc 32e893df 3a054b9e f7f227a4 ce88ac00 286bee00 + 00000019 76a914ee 26c56fc1 d942be8d 7a24b2a1 001dd894 69398088 ac000000 + 00 +Block 3: + f9beb4d9 + 96020000 + + 01000000 7d338254 0506faab 0d4cf179 45dda023 49db51f9 6233f24c 28002258 + 00000000 4806fe80 bf85931b 882ea645 77ca5a03 22bb8af2 3f277b20 55f160cd + 972c8e8b 31b25f49 ffff001d e8f0c653 + 03 + + 01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000 + 00000000 00ffffff ff044abd 8159ffff ffff0100 f2052a01 00000043 4104b95c + 249d84f4 17e3e395 a1274254 28b54067 1cc15881 eb828c17 b722a53f c599e21c + a5e56c90 f340988d 3933acc7 6beb832f d64cab07 8ddf3ce7 32923031 d1a8ac00 + 000000 + + 01000000 01f287b5 e067e1cf 80f7da8a f89917b5 505094db d82412d9 35b665eb + bad253d3 77010000 008c4930 46022100 96ee0d02 b35fd61e 4960b44f f396f67e + 01fe17f9 de4e0c17 b6a963bd ab2b50a6 02210034 920d4daa 7e9f8abe 5675c931 + 495809f9 0b9c1189 d05fbaf1 dd6696a5 b0d8f301 41046868 0737c76d abb801cb + 2204f57d be4e4579 e4f710cd 67dc1b42 27592c81 e9b5cf02 b5ac9e8b 4c9f49be + 5251056b 6a6d011e 4c37f6b6 d17ede6b 55faa235 19e2ffff ffff0100 286bee00 + 00000019 76a914c5 22664fb0 e55cdc5c 0cea73b4 aad97ec8 34323288 ac000000 + 00 + + 01000000 01f287b5 e067e1cf 80f7da8a f89917b5 505094db d82412d9 35b665eb + bad253d3 77000000 008c4930 46022100 b08b922a c4bde411 1c229f92 9fe6eb6a + 50161f98 1f4cf47e a9214d35 bf74d380 022100d2 f6640327 e677a1e1 cc474991 + b9a48ba5 bd1e0c94 d1c8df49 f7b0193b 7ea4fa01 4104b95c 249d84f4 17e3e395 + a1274254 28b54067 1cc15881 eb828c17 b722a53f c599e21c a5e56c90 f340988d + 3933acc7 6beb832f d64cab07 8ddf3ce7 32923031 d1a8ffff ffff0100 ca9a3b00 + 00000019 76a914c5 22664fb0 e55cdc5c 0cea73b4 aad97ec8 34323288 ac000000 + 00 + +Block 4: + f9beb4d9 + 73010000 + + 01000000 5da36499 06f35e09 9be42a1d 87b6dd42 11bc1400 6c220694 0807eaae + 00000000 48eeeaed 2d9d8522 e6201173 743823fd 4b87cd8a ca8e6408 ec75ca38 + 302c2ff0 89b45f49 ffff001d 00530839 + 02 + + 01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000 + 00000000 00ffffff ff04d41d 2213ffff ffff0100 f2052a01 00000043 4104678a + fdb0fe55 48271967 f1a67130 b7105cd6 a828e039 09a67962 e0ea1f61 deb649f6 + bc3f4cef 38c4f355 04e51ec1 12de5c38 4df7ba0b 8d578a4c 702b6bf1 1d5fac00 + 000000 + + 01000000 0163451d 1002611c 1388d5ba 4ddfdf99 196a86b5 990fb5b0 dc786207 + 4fdcb8ee d2000000 004a4930 46022100 8c8fd57b 48762135 8d8f3e69 19f33e08 + 804736ff 83db47aa 248512e2 6df9b8ba 022100b0 c59e5ee7 bfcbfcd1 a4d83da9 + 55fb260e fda7f42a 25522625 a3d6f2d9 1174a701 ffffffff 0100f205 2a010000 + 001976a9 14c52266 4fb0e55c dc5c0cea 73b4aad9 7ec83432 3288ac00 000000 + +File path: reorgTest/blk_3A.dat +Block 3A: + f9beb4d9 + 96020000 + + 01000000 7d338254 0506faab 0d4cf179 45dda023 49db51f9 6233f24c 28002258 + 00000000 5a15f573 1177a353 bdca7aab 20e16624 dfe90adc 70accadc 68016732 + 302c20a7 31b25f49 ffff001d 6a901440 + 03 + + 01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000 + 00000000 00ffffff ff04ad1b e7d5ffff ffff0100 f2052a01 00000043 4104ed83 + 704c95d8 29046f1a c2780621 1132102c 34e9ac7f fa1b7111 0658e5b9 d1bdedc4 + 16f5cefc 1db0625c d0c75de8 192d2b59 2d7e3b00 bcfb4a0e 860d880f d1fcac00 + 000000 + + 01000000 01f287b5 e067e1cf 80f7da8a f89917b5 505094db d82412d9 35b665eb + bad253d3 77010000 008c4930 46022100 96ee0d02 b35fd61e 4960b44f f396f67e + 01fe17f9 de4e0c17 b6a963bd ab2b50a6 02210034 920d4daa 7e9f8abe 5675c931 + 495809f9 0b9c1189 d05fbaf1 dd6696a5 b0d8f301 41046868 0737c76d abb801cb + 2204f57d be4e4579 e4f710cd 67dc1b42 27592c81 e9b5cf02 b5ac9e8b 4c9f49be + 5251056b 6a6d011e 4c37f6b6 d17ede6b 55faa235 19e2ffff ffff0100 286bee00 + 00000019 76a914c5 22664fb0 e55cdc5c 0cea73b4 aad97ec8 34323288 ac000000 + 00 + + 01000000 01f287b5 e067e1cf 80f7da8a f89917b5 505094db d82412d9 35b665eb + bad253d3 77000000 008c4930 46022100 9cc67ddd aa6f592a 6b2babd4 d6ff954f + 25a784cf 4fe4bb13 afb9f49b 08955119 022100a2 d99545b7 94080757 fcf2b563 + f2e91287 86332f46 0ec6b90f f085fb28 41a69701 4104b95c 249d84f4 17e3e395 + a1274254 28b54067 1cc15881 eb828c17 b722a53f c599e21c a5e56c90 f340988d + 3933acc7 6beb832f d64cab07 8ddf3ce7 32923031 d1a8ffff ffff0100 ca9a3b00 + 00000019 76a914ee 26c56fc1 d942be8d 7a24b2a1 001dd894 69398088 ac000000 + 00 + +File path: reorgTest/blk_4A.dat +Block 4A: + f9beb4d9 + d4000000 + + 01000000 aae77468 2205667d 4f413a58 47cc8fe8 9795f1d5 645d5b24 1daf3c92 + 00000000 361c9cde a09637a0 d0c05c3b 4e7a5d91 9edb184a 0a4c7633 d92e2ddd + f04cb854 89b45f49 ffff001d 9e9aa1e8 + 01 + + 01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000 + 00000000 00ffffff ff0401b8 f3eaffff ffff0100 f2052a01 00000043 4104678a + fdb0fe55 48271967 f1a67130 b7105cd6 a828e039 09a67962 e0ea1f61 deb649f6 + bc3f4cef 38c4f355 04e51ec1 12de5c38 4df7ba0b 8d578a4c 702b6bf1 1d5fac00 + 000000 + +File path: reorgTest/blk_5A.dat +Block 5A: + f9beb4d9 + 73010000 + + 01000000 ebc7d0de 9c31a71b 7f41d275 2c080ba4 11e1854b d45cb2cf 8c1e4624 + 00000000 a607774b 79b8eb50 b52a5a32 c1754281 ec67f626 9561df28 57d1fe6a + ea82c696 e1b65f49 ffff001d 4a263577 + 02 + + 01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000 + 00000000 00ffffff ff049971 0c7dffff ffff0100 f2052a01 00000043 4104678a + fdb0fe55 48271967 f1a67130 b7105cd6 a828e039 09a67962 e0ea1f61 deb649f6 + bc3f4cef 38c4f355 04e51ec1 12de5c38 4df7ba0b 8d578a4c 702b6bf1 1d5fac00 + 000000 + + 01000000 0163451d 1002611c 1388d5ba 4ddfdf99 196a86b5 990fb5b0 dc786207 + 4fdcb8ee d2000000 004a4930 46022100 8c8fd57b 48762135 8d8f3e69 19f33e08 + 804736ff 83db47aa 248512e2 6df9b8ba 022100b0 c59e5ee7 bfcbfcd1 a4d83da9 + 55fb260e fda7f42a 25522625 a3d6f2d9 1174a701 ffffffff 0100f205 2a010000 + 001976a9 14c52266 4fb0e55c dc5c0cea 73b4aad9 7ec83432 3288ac00 000000 + diff --git a/vendor/github.com/btcsuite/btcd/blockchain/timesorter.go b/vendor/github.com/btcsuite/btcd/blockchain/timesorter.go new file mode 100644 index 0000000000000000000000000000000000000000..516fe4fd010b421e3fa9d4373616609f53974937 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/timesorter.go @@ -0,0 +1,31 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "time" +) + +// timeSorter implements sort.Interface to allow a slice of timestamps to +// be sorted. +type timeSorter []time.Time + +// Len returns the number of timestamps in the slice. It is part of the +// sort.Interface implementation. +func (s timeSorter) Len() int { + return len(s) +} + +// Swap swaps the timestamps at the passed indices. It is part of the +// sort.Interface implementation. +func (s timeSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Less returns whether the timstamp with index i should sort before the +// timestamp with index j. It is part of the sort.Interface implementation. +func (s timeSorter) Less(i, j int) bool { + return s[i].Before(s[j]) +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/timesorter_test.go b/vendor/github.com/btcsuite/btcd/blockchain/timesorter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ca1b4afbd33f0fde5e61635be616b00533eb1a00 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/timesorter_test.go @@ -0,0 +1,52 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain_test + +import ( + "reflect" + "sort" + "testing" + "time" + + "github.com/btcsuite/btcd/blockchain" +) + +// TestTimeSorter tests the timeSorter implementation. +func TestTimeSorter(t *testing.T) { + tests := []struct { + in []time.Time + want []time.Time + }{ + { + in: []time.Time{ + time.Unix(1351228575, 0), // Fri Oct 26 05:16:15 UTC 2012 (Block #205000) + time.Unix(1351228575, 1), // Fri Oct 26 05:16:15 UTC 2012 (+1 nanosecond) + time.Unix(1348310759, 0), // Sat Sep 22 10:45:59 UTC 2012 (Block #200000) + time.Unix(1305758502, 0), // Wed May 18 22:41:42 UTC 2011 (Block #125000) + time.Unix(1347777156, 0), // Sun Sep 16 06:32:36 UTC 2012 (Block #199000) + time.Unix(1349492104, 0), // Sat Oct 6 02:55:04 UTC 2012 (Block #202000) + }, + want: []time.Time{ + time.Unix(1305758502, 0), // Wed May 18 22:41:42 UTC 2011 (Block #125000) + time.Unix(1347777156, 0), // Sun Sep 16 06:32:36 UTC 2012 (Block #199000) + time.Unix(1348310759, 0), // Sat Sep 22 10:45:59 UTC 2012 (Block #200000) + time.Unix(1349492104, 0), // Sat Oct 6 02:55:04 UTC 2012 (Block #202000) + time.Unix(1351228575, 0), // Fri Oct 26 05:16:15 UTC 2012 (Block #205000) + time.Unix(1351228575, 1), // Fri Oct 26 05:16:15 UTC 2012 (+1 nanosecond) + }, + }, + } + + for i, test := range tests { + result := make([]time.Time, len(test.in)) + copy(result, test.in) + sort.Sort(blockchain.TstTimeSorter(result)) + if !reflect.DeepEqual(result, test.want) { + t.Errorf("timeSorter #%d got %v want %v", i, result, + test.want) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/utxoviewpoint.go b/vendor/github.com/btcsuite/btcd/blockchain/utxoviewpoint.go new file mode 100644 index 0000000000000000000000000000000000000000..53a1f75fcb2628dc8ce4cbb06a1a31349c9062cc --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/utxoviewpoint.go @@ -0,0 +1,611 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "fmt" + + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// utxoOutput houses details about an individual unspent transaction output such +// as whether or not it is spent, its public key script, and how much it pays. +// +// Standard public key scripts are stored in the database using a compressed +// format. Since the vast majority of scripts are of the standard form, a fairly +// significant savings is achieved by discarding the portions of the standard +// scripts that can be reconstructed. +// +// Also, since it is common for only a specific output in a given utxo entry to +// be referenced from a redeeming transaction, the script and amount for a given +// output is not uncompressed until the first time it is accessed. This +// provides a mechanism to avoid the overhead of needlessly uncompressing all +// outputs for a given utxo entry at the time of load. +type utxoOutput struct { + spent bool // Output is spent. + compressed bool // The amount and public key script are compressed. + amount int64 // The amount of the output. + pkScript []byte // The public key script for the output. +} + +// maybeDecompress decompresses the amount and public key script fields of the +// utxo and marks it decompressed if needed. +func (o *utxoOutput) maybeDecompress(version int32) { + // Nothing to do if it's not compressed. + if !o.compressed { + return + } + + o.amount = int64(decompressTxOutAmount(uint64(o.amount))) + o.pkScript = decompressScript(o.pkScript, version) + o.compressed = false +} + +// UtxoEntry contains contextual information about an unspent transaction such +// as whether or not it is a coinbase transaction, which block it was found in, +// and the spent status of its outputs. +type UtxoEntry struct { + modified bool // Entry changed since load. + version int32 // The version of this tx. + isCoinBase bool // Whether entry is a coinbase tx. + blockHeight int32 // Height of block containing tx. + sparseOutputs map[uint32]*utxoOutput // Sparse map of unspent outputs. +} + +// Version returns the version of the transaction the utxo represents. +func (entry *UtxoEntry) Version() int32 { + return entry.version +} + +// IsCoinBase returns whether or not the transaction the utxo entry represents +// is a coinbase. +func (entry *UtxoEntry) IsCoinBase() bool { + return entry.isCoinBase +} + +// BlockHeight returns the height of the block containing the transaction the +// utxo entry represents. +func (entry *UtxoEntry) BlockHeight() int32 { + return entry.blockHeight +} + +// IsOutputSpent returns whether or not the provided output index has been +// spent based upon the current state of the unspent transaction output view +// the entry was obtained from. +// +// Returns true if the output index references an output that does not exist +// either due to it being invalid or because the output is not part of the view +// due to previously being spent/pruned. +func (entry *UtxoEntry) IsOutputSpent(outputIndex uint32) bool { + output, ok := entry.sparseOutputs[outputIndex] + if !ok { + return true + } + + return output.spent +} + +// SpendOutput marks the output at the provided index as spent. Specifying an +// output index that does not exist will not have any effect. +func (entry *UtxoEntry) SpendOutput(outputIndex uint32) { + output, ok := entry.sparseOutputs[outputIndex] + if !ok { + return + } + + // Nothing to do if the output is already spent. + if output.spent { + return + } + + entry.modified = true + output.spent = true + return +} + +// IsFullySpent returns whether or not the transaction the utxo entry represents +// is fully spent. +func (entry *UtxoEntry) IsFullySpent() bool { + // The entry is not fully spent if any of the outputs are unspent. + for _, output := range entry.sparseOutputs { + if !output.spent { + return false + } + } + + return true +} + +// AmountByIndex returns the amount of the provided output index. +// +// Returns 0 if the output index references an output that does not exist +// either due to it being invalid or because the output is not part of the view +// due to previously being spent/pruned. +func (entry *UtxoEntry) AmountByIndex(outputIndex uint32) int64 { + output, ok := entry.sparseOutputs[outputIndex] + if !ok { + return 0 + } + + // Ensure the output is decompressed before returning the amount. + output.maybeDecompress(entry.version) + return output.amount +} + +// PkScriptByIndex returns the public key script for the provided output index. +// +// Returns nil if the output index references an output that does not exist +// either due to it being invalid or because the output is not part of the view +// due to previously being spent/pruned. +func (entry *UtxoEntry) PkScriptByIndex(outputIndex uint32) []byte { + output, ok := entry.sparseOutputs[outputIndex] + if !ok { + return nil + } + + // Ensure the output is decompressed before returning the script. + output.maybeDecompress(entry.version) + return output.pkScript +} + +// newUtxoEntry returns a new unspent transaction output entry with the provided +// coinbase flag and block height ready to have unspent outputs added. +func newUtxoEntry(version int32, isCoinBase bool, blockHeight int32) *UtxoEntry { + return &UtxoEntry{ + version: version, + isCoinBase: isCoinBase, + blockHeight: blockHeight, + sparseOutputs: make(map[uint32]*utxoOutput), + } +} + +// UtxoViewpoint represents a view into the set of unspent transaction outputs +// from a specific point of view in the chain. For example, it could be for +// the end of the main chain, some point in the history of the main chain, or +// down a side chain. +// +// The unspent outputs are needed by other transactions for things such as +// script validation and double spend prevention. +type UtxoViewpoint struct { + entries map[wire.ShaHash]*UtxoEntry + bestHash wire.ShaHash +} + +// BestHash returns the hash of the best block in the chain the view currently +// respresents. +func (view *UtxoViewpoint) BestHash() *wire.ShaHash { + return &view.bestHash +} + +// SetBestHash sets the hash of the best block in the chain the view currently +// respresents. +func (view *UtxoViewpoint) SetBestHash(hash *wire.ShaHash) { + view.bestHash = *hash +} + +// LookupEntry returns information about a given transaction according to the +// current state of the view. It will return nil if the passed transaction +// hash does not exist in the view or is otherwise not available such as when +// it has been disconnected during a reorg. +func (view *UtxoViewpoint) LookupEntry(txHash *wire.ShaHash) *UtxoEntry { + entry, ok := view.entries[*txHash] + if !ok { + return nil + } + + return entry +} + +// AddTxOuts adds all outputs in the passed transaction which are not provably +// unspendable to the view. When the view already has entries for any of the +// outputs, they are simply marked unspent. All fields will be updated for +// existing entries since it's possible it has changed during a reorg. +func (view *UtxoViewpoint) AddTxOuts(tx *btcutil.Tx, blockHeight int32) { + // When there are not already any utxos associated with the transaction, + // add a new entry for it to the view. + entry := view.LookupEntry(tx.Sha()) + if entry == nil { + entry = newUtxoEntry(tx.MsgTx().Version, IsCoinBase(tx), + blockHeight) + view.entries[*tx.Sha()] = entry + } else { + entry.blockHeight = blockHeight + } + entry.modified = true + + // Loop all of the transaction outputs and add those which are not + // provably unspendable. + for txOutIdx, txOut := range tx.MsgTx().TxOut { + if txscript.IsUnspendable(txOut.PkScript) { + continue + } + + // Update existing entries. All fields are updated because it's + // possible (although extremely unlikely) that the existing + // entry is being replaced by a different transaction with the + // same hash. This is allowed so long as the previous + // transaction is fully spent. + if output, ok := entry.sparseOutputs[uint32(txOutIdx)]; ok { + output.spent = false + output.compressed = false + output.amount = txOut.Value + output.pkScript = txOut.PkScript + continue + } + + // Add the unspent transaction output. + entry.sparseOutputs[uint32(txOutIdx)] = &utxoOutput{ + spent: false, + compressed: false, + amount: txOut.Value, + pkScript: txOut.PkScript, + } + } + return +} + +// connectTransaction updates the view by adding all new utxos created by the +// passed transaction and marking all utxos that the transactions spend as +// spent. In addition, when the 'stxos' argument is not nil, it will be updated +// to append an entry for each spent txout. An error will be returned if the +// view does not contain the required utxos. +func (view *UtxoViewpoint) connectTransaction(tx *btcutil.Tx, blockHeight int32, stxos *[]spentTxOut) error { + // Coinbase transactions don't have any inputs to spend. + if IsCoinBase(tx) { + // Add the transaction's outputs as available utxos. + view.AddTxOuts(tx, blockHeight) + return nil + } + + // Spend the referenced utxos by marking them spent in the view and, + // if a slice was provided for the spent txout details, append an entry + // to it. + for _, txIn := range tx.MsgTx().TxIn { + originIndex := txIn.PreviousOutPoint.Index + entry := view.entries[txIn.PreviousOutPoint.Hash] + + // Ensure the referenced utxo exists in the view. This should + // never happen unless there is a bug is introduced in the code. + if entry == nil { + return AssertError(fmt.Sprintf("view missing input %v", + txIn.PreviousOutPoint)) + } + entry.SpendOutput(originIndex) + + // Don't create the stxo details if not requested. + if stxos == nil { + continue + } + + // Populate the stxo details using the utxo entry. When the + // transaction is fully spent, set the additional stxo fields + // accordingly since those details will no longer be available + // in the utxo set. + var stxo = spentTxOut{ + compressed: false, + version: entry.Version(), + amount: entry.AmountByIndex(originIndex), + pkScript: entry.PkScriptByIndex(originIndex), + } + if entry.IsFullySpent() { + stxo.height = entry.BlockHeight() + stxo.isCoinBase = entry.IsCoinBase() + } + + // Append the entry to the provided spent txouts slice. + *stxos = append(*stxos, stxo) + } + + // Add the transaction's outputs as available utxos. + view.AddTxOuts(tx, blockHeight) + return nil +} + +// connectTransactions updates the view by adding all new utxos created by all +// of the transactions in the passed block, marking all utxos the transactions +// spend as spent, and setting the best hash for the view to the passed block. +// In addition, when the 'stxos' argument is not nil, it will be updated to +// append an entry for each spent txout. +func (view *UtxoViewpoint) connectTransactions(block *btcutil.Block, stxos *[]spentTxOut) error { + for _, tx := range block.Transactions() { + err := view.connectTransaction(tx, block.Height(), stxos) + if err != nil { + return err + } + } + + // Update the best hash for view to include this block since all of its + // transactions have been connected. + view.SetBestHash(block.Sha()) + return nil +} + +// disconnectTransactions updates the view by removing all of the transactions +// created by the passed block, restoring all utxos the transactions spent by +// using the provided spent txo information, and setting the best hash for the +// view to the block before the passed block. +func (view *UtxoViewpoint) disconnectTransactions(block *btcutil.Block, stxos []spentTxOut) error { + // Sanity check the correct number of stxos are provided. + if len(stxos) != countSpentOutputs(block) { + return AssertError("disconnectTransactions called with bad " + + "spent transaction out information") + } + + // Loop backwards through all transactions so everything is unspent in + // reverse order. This is necessary since transactions later in a block + // can spend from previous ones. + stxoIdx := len(stxos) - 1 + transactions := block.Transactions() + for txIdx := len(transactions) - 1; txIdx > -1; txIdx-- { + tx := transactions[txIdx] + + // Clear this transaction from the view if it already exists or + // create a new empty entry for when it does not. This is done + // because the code relies on its existence in the view in order + // to signal modifications have happened. + isCoinbase := txIdx == 0 + entry := view.entries[*tx.Sha()] + if entry == nil { + entry = newUtxoEntry(tx.MsgTx().Version, isCoinbase, + block.Height()) + view.entries[*tx.Sha()] = entry + } + entry.modified = true + entry.sparseOutputs = make(map[uint32]*utxoOutput) + + // Loop backwards through all of the transaction inputs (except + // for the coinbase which has no inputs) and unspend the + // referenced txos. This is necessary to match the order of the + // spent txout entries. + if isCoinbase { + continue + } + for txInIdx := len(tx.MsgTx().TxIn) - 1; txInIdx > -1; txInIdx-- { + // Ensure the spent txout index is decremented to stay + // in sync with the transaction input. + stxo := &stxos[stxoIdx] + stxoIdx-- + + // When there is not already an entry for the referenced + // transaction in the view, it means it was fully spent, + // so create a new utxo entry in order to resurrect it. + txIn := tx.MsgTx().TxIn[txInIdx] + originHash := &txIn.PreviousOutPoint.Hash + originIndex := txIn.PreviousOutPoint.Index + entry := view.entries[*originHash] + if entry == nil { + entry = newUtxoEntry(stxo.version, + stxo.isCoinBase, stxo.height) + view.entries[*originHash] = entry + } + + // Mark the entry as modified since it is either new + // or will be changed below. + entry.modified = true + + // Restore the specific utxo using the stxo data from + // the spend journal if it doesn't already exist in the + // view. + output, ok := entry.sparseOutputs[originIndex] + if !ok { + // Add the unspent transaction output. + entry.sparseOutputs[originIndex] = &utxoOutput{ + spent: false, + compressed: stxo.compressed, + amount: stxo.amount, + pkScript: stxo.pkScript, + } + continue + } + + // Mark the existing referenced transaction output as + // unspent. + output.spent = false + } + } + + // Update the best hash for view to the previous block since all of the + // transactions for the current block have been disconnected. + view.SetBestHash(&block.MsgBlock().Header.PrevBlock) + return nil +} + +// Entries returns the underlying map that stores of all the utxo entries. +func (view *UtxoViewpoint) Entries() map[wire.ShaHash]*UtxoEntry { + return view.entries +} + +// commit prunes all entries marked modified that are now fully spent and marks +// all entries as unmodified. +func (view *UtxoViewpoint) commit() { + for txHash, entry := range view.entries { + if entry == nil || (entry.modified && entry.IsFullySpent()) { + delete(view.entries, txHash) + continue + } + + entry.modified = false + } +} + +// fetchUtxosMain fetches unspent transaction output data about the provided +// set of transactions from the point of view of the end of the main chain at +// the time of the call. +// +// Upon completion of this function, the view will contain an entry for each +// requested transaction. Fully spent transactions, or those which otherwise +// don't exist, will result in a nil entry in the view. +func (view *UtxoViewpoint) fetchUtxosMain(db database.DB, txSet map[wire.ShaHash]struct{}) error { + // Nothing to do if there are no requested hashes. + if len(txSet) == 0 { + return nil + } + + // Load the unspent transaction output information for the requested set + // of transactions from the point of view of the end of the main chain. + // + // NOTE: Missing entries are not considered an error here and instead + // will result in nil entries in the view. This is intentionally done + // since other code uses the presence of an entry in the store as a way + // to optimize spend and unspend updates to apply only to the specific + // utxos that the caller needs access to. + return db.View(func(dbTx database.Tx) error { + for hash := range txSet { + hashCopy := hash + entry, err := dbFetchUtxoEntry(dbTx, &hashCopy) + if err != nil { + return err + } + + view.entries[hash] = entry + } + + return nil + }) +} + +// fetchUtxos loads utxo details about provided set of transaction hashes into +// the view from the database as needed unless they already exist in the view in +// which case they are ignored. +func (view *UtxoViewpoint) fetchUtxos(db database.DB, txSet map[wire.ShaHash]struct{}) error { + // Nothing to do if there are no requested hashes. + if len(txSet) == 0 { + return nil + } + + // Filter entries that are already in the view. + txNeededSet := make(map[wire.ShaHash]struct{}) + for hash := range txSet { + // Already loaded into the current view. + if _, ok := view.entries[hash]; ok { + continue + } + + txNeededSet[hash] = struct{}{} + } + + // Request the input utxos from the database. + return view.fetchUtxosMain(db, txNeededSet) +} + +// fetchInputUtxos loads utxo details about the input transactions referenced +// by the transactions in the given block into the view from the database as +// needed. In particular, referenced entries that are earlier in the block are +// added to the view and entries that are already in the view are not modified. +func (view *UtxoViewpoint) fetchInputUtxos(db database.DB, block *btcutil.Block) error { + // Build a map of in-flight transactions because some of the inputs in + // this block could be referencing other transactions earlier in this + // block which are not yet in the chain. + txInFlight := map[wire.ShaHash]int{} + transactions := block.Transactions() + for i, tx := range transactions { + txInFlight[*tx.Sha()] = i + } + + // Loop through all of the transaction inputs (except for the coinbase + // which has no inputs) collecting them into sets of what is needed and + // what is already known (in-flight). + txNeededSet := make(map[wire.ShaHash]struct{}) + for i, tx := range transactions[1:] { + for _, txIn := range tx.MsgTx().TxIn { + // It is acceptable for a transaction input to reference + // the output of another transaction in this block only + // if the referenced transaction comes before the + // current one in this block. Add the outputs of the + // referenced transaction as available utxos when this + // is the case. Otherwise, the utxo details are still + // needed. + // + // NOTE: The >= is correct here because i is one less + // than the actual position of the transaction within + // the block due to skipping the coinbase. + originHash := &txIn.PreviousOutPoint.Hash + if inFlightIndex, ok := txInFlight[*originHash]; ok && + i >= inFlightIndex { + + originTx := transactions[inFlightIndex] + view.AddTxOuts(originTx, block.Height()) + continue + } + + // Don't request entries that are already in the view + // from the database. + if _, ok := view.entries[*originHash]; ok { + continue + } + + txNeededSet[*originHash] = struct{}{} + } + } + + // Request the input utxos from the database. + return view.fetchUtxosMain(db, txNeededSet) +} + +// NewUtxoViewpoint returns a new empty unspent transaction output view. +func NewUtxoViewpoint() *UtxoViewpoint { + return &UtxoViewpoint{ + entries: make(map[wire.ShaHash]*UtxoEntry), + } +} + +// FetchUtxoView loads utxo details about the input transactions referenced by +// the passed transaction from the point of view of the end of the main chain. +// It also attempts to fetch the utxo details for the transaction itself so the +// returned view can be examined for duplicate unspent transaction outputs. +// +// This function is safe for concurrent access however the returned view is NOT. +func (b *BlockChain) FetchUtxoView(tx *btcutil.Tx) (*UtxoViewpoint, error) { + b.chainLock.RLock() + defer b.chainLock.RUnlock() + + // Create a set of needed transactions based on those referenced by the + // inputs of the passed transaction. Also, add the passed transaction + // itself as a way for the caller to detect duplicates that are not + // fully spent. + txNeededSet := make(map[wire.ShaHash]struct{}) + txNeededSet[*tx.Sha()] = struct{}{} + if !IsCoinBase(tx) { + for _, txIn := range tx.MsgTx().TxIn { + txNeededSet[txIn.PreviousOutPoint.Hash] = struct{}{} + } + } + + // Request the utxos from the point of view of the end of the main + // chain. + view := NewUtxoViewpoint() + err := view.fetchUtxosMain(b.db, txNeededSet) + return view, err +} + +// FetchUtxoEntry loads and returns the unspent transaction output entry for the +// passed hash from the point of view of the end of the main chain. +// +// NOTE: Requesting a hash for which there is no data will NOT return an error. +// Instead both the entry and the error will be nil. This is done to allow +// pruning of fully spent transactions. In practice this means the caller must +// check if the returned entry is nil before invoking methods on it. +// +// This function is safe for concurrent access however the returned entry (if +// any) is NOT. +func (b *BlockChain) FetchUtxoEntry(txHash *wire.ShaHash) (*UtxoEntry, error) { + b.chainLock.RLock() + defer b.chainLock.RUnlock() + + var entry *UtxoEntry + err := b.db.View(func(dbTx database.Tx) error { + var err error + entry, err = dbFetchUtxoEntry(dbTx, txHash) + return err + }) + if err != nil { + return nil, err + } + + return entry, nil +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/validate.go b/vendor/github.com/btcsuite/btcd/blockchain/validate.go new file mode 100644 index 0000000000000000000000000000000000000000..31117a2d1b26b91c2fefdb948f930c66daaa26f3 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/validate.go @@ -0,0 +1,1184 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain + +import ( + "encoding/binary" + "fmt" + "math" + "math/big" + "time" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +const ( + // MaxSigOpsPerBlock is the maximum number of signature operations + // allowed for a block. It is a fraction of the max block payload size. + MaxSigOpsPerBlock = wire.MaxBlockPayload / 50 + + // MaxTimeOffsetSeconds is the maximum number of seconds a block time + // is allowed to be ahead of the current time. This is currently 2 + // hours. + MaxTimeOffsetSeconds = 2 * 60 * 60 + + // MinCoinbaseScriptLen is the minimum length a coinbase script can be. + MinCoinbaseScriptLen = 2 + + // MaxCoinbaseScriptLen is the maximum length a coinbase script can be. + MaxCoinbaseScriptLen = 100 + + // medianTimeBlocks is the number of previous blocks which should be + // used to calculate the median time used to validate block timestamps. + medianTimeBlocks = 11 + + // serializedHeightVersion is the block version which changed block + // coinbases to start with the serialized block height. + serializedHeightVersion = 2 + + // baseSubsidy is the starting subsidy amount for mined blocks. This + // value is halved every SubsidyHalvingInterval blocks. + baseSubsidy = 50 * btcutil.SatoshiPerBitcoin + + // CoinbaseMaturity is the number of blocks required before newly + // mined bitcoins (coinbase transactions) can be spent. + CoinbaseMaturity = 100 +) + +var ( + // coinbaseMaturity is the internal variable used for validating the + // spending of coinbase outputs. A variable rather than the exported + // constant is used because the tests need the ability to modify it. + coinbaseMaturity = int32(CoinbaseMaturity) + + // zeroHash is the zero value for a wire.ShaHash and is defined as + // a package level variable to avoid the need to create a new instance + // every time a check is needed. + zeroHash = &wire.ShaHash{} + + // block91842Hash is one of the two nodes which violate the rules + // set forth in BIP0030. It is defined as a package level variable to + // avoid the need to create a new instance every time a check is needed. + block91842Hash = newShaHashFromStr("00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec") + + // block91880Hash is one of the two nodes which violate the rules + // set forth in BIP0030. It is defined as a package level variable to + // avoid the need to create a new instance every time a check is needed. + block91880Hash = newShaHashFromStr("00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721") +) + +// isNullOutpoint determines whether or not a previous transaction output point +// is set. +func isNullOutpoint(outpoint *wire.OutPoint) bool { + if outpoint.Index == math.MaxUint32 && outpoint.Hash.IsEqual(zeroHash) { + return true + } + return false +} + +// ShouldHaveSerializedBlockHeight determines if a block should have a +// serialized block height embedded within the scriptSig of its +// coinbase transaction. Judgement is based on the block version in the block +// header. Blocks with version 2 and above satisfy this criteria. See BIP0034 +// for further information. +func ShouldHaveSerializedBlockHeight(header *wire.BlockHeader) bool { + return header.Version >= serializedHeightVersion +} + +// IsCoinBaseTx determines whether or not a transaction is a coinbase. A coinbase +// is a special transaction created by miners that has no inputs. This is +// represented in the block chain by a transaction with a single input that has +// a previous output transaction index set to the maximum value along with a +// zero hash. +// +// This function only differs from IsCoinBase in that it works with a raw wire +// transaction as opposed to a higher level util transaction. +func IsCoinBaseTx(msgTx *wire.MsgTx) bool { + // A coin base must only have one transaction input. + if len(msgTx.TxIn) != 1 { + return false + } + + // The previous output of a coin base must have a max value index and + // a zero hash. + prevOut := &msgTx.TxIn[0].PreviousOutPoint + if prevOut.Index != math.MaxUint32 || !prevOut.Hash.IsEqual(zeroHash) { + return false + } + + return true +} + +// IsCoinBase determines whether or not a transaction is a coinbase. A coinbase +// is a special transaction created by miners that has no inputs. This is +// represented in the block chain by a transaction with a single input that has +// a previous output transaction index set to the maximum value along with a +// zero hash. +// +// This function only differs from IsCoinBaseTx in that it works with a higher +// level util transaction as opposed to a raw wire transaction. +func IsCoinBase(tx *btcutil.Tx) bool { + return IsCoinBaseTx(tx.MsgTx()) +} + +// IsFinalizedTransaction determines whether or not a transaction is finalized. +func IsFinalizedTransaction(tx *btcutil.Tx, blockHeight int32, blockTime time.Time) bool { + msgTx := tx.MsgTx() + + // Lock time of zero means the transaction is finalized. + lockTime := msgTx.LockTime + if lockTime == 0 { + return true + } + + // The lock time field of a transaction is either a block height at + // which the transaction is finalized or a timestamp depending on if the + // value is before the txscript.LockTimeThreshold. When it is under the + // threshold it is a block height. + blockTimeOrHeight := int64(0) + if lockTime < txscript.LockTimeThreshold { + blockTimeOrHeight = int64(blockHeight) + } else { + blockTimeOrHeight = blockTime.Unix() + } + if int64(lockTime) < blockTimeOrHeight { + return true + } + + // At this point, the transaction's lock time hasn't occurred yet, but + // the transaction might still be finalized if the sequence number + // for all transaction inputs is maxed out. + for _, txIn := range msgTx.TxIn { + if txIn.Sequence != math.MaxUint32 { + return false + } + } + return true +} + +// isBIP0030Node returns whether or not the passed node represents one of the +// two blocks that violate the BIP0030 rule which prevents transactions from +// overwriting old ones. +func isBIP0030Node(node *blockNode) bool { + if node.height == 91842 && node.hash.IsEqual(block91842Hash) { + return true + } + + if node.height == 91880 && node.hash.IsEqual(block91880Hash) { + return true + } + + return false +} + +// CalcBlockSubsidy returns the subsidy amount a block at the provided height +// should have. This is mainly used for determining how much the coinbase for +// newly generated blocks awards as well as validating the coinbase for blocks +// has the expected value. +// +// The subsidy is halved every SubsidyHalvingInterval blocks. Mathematically +// this is: baseSubsidy / 2^(height/subsidyHalvingInterval) +// +// At the target block generation rate for the main network, this is +// approximately every 4 years. +func CalcBlockSubsidy(height int32, chainParams *chaincfg.Params) int64 { + if chainParams.SubsidyHalvingInterval == 0 { + return baseSubsidy + } + + // Equivalent to: baseSubsidy / 2^(height/subsidyHalvingInterval) + return baseSubsidy >> uint(height/chainParams.SubsidyHalvingInterval) +} + +// CheckTransactionSanity performs some preliminary checks on a transaction to +// ensure it is sane. These checks are context free. +func CheckTransactionSanity(tx *btcutil.Tx) error { + // A transaction must have at least one input. + msgTx := tx.MsgTx() + if len(msgTx.TxIn) == 0 { + return ruleError(ErrNoTxInputs, "transaction has no inputs") + } + + // A transaction must have at least one output. + if len(msgTx.TxOut) == 0 { + return ruleError(ErrNoTxOutputs, "transaction has no outputs") + } + + // A transaction must not exceed the maximum allowed block payload when + // serialized. + serializedTxSize := tx.MsgTx().SerializeSize() + if serializedTxSize > wire.MaxBlockPayload { + str := fmt.Sprintf("serialized transaction is too big - got "+ + "%d, max %d", serializedTxSize, wire.MaxBlockPayload) + return ruleError(ErrTxTooBig, str) + } + + // Ensure the transaction amounts are in range. Each transaction + // output must not be negative or more than the max allowed per + // transaction. Also, the total of all outputs must abide by the same + // restrictions. All amounts in a transaction are in a unit value known + // as a satoshi. One bitcoin is a quantity of satoshi as defined by the + // SatoshiPerBitcoin constant. + var totalSatoshi int64 + for _, txOut := range msgTx.TxOut { + satoshi := txOut.Value + if satoshi < 0 { + str := fmt.Sprintf("transaction output has negative "+ + "value of %v", satoshi) + return ruleError(ErrBadTxOutValue, str) + } + if satoshi > btcutil.MaxSatoshi { + str := fmt.Sprintf("transaction output value of %v is "+ + "higher than max allowed value of %v", satoshi, + btcutil.MaxSatoshi) + return ruleError(ErrBadTxOutValue, str) + } + + // Two's complement int64 overflow guarantees that any overflow + // is detected and reported. This is impossible for Bitcoin, but + // perhaps possible if an alt increases the total money supply. + totalSatoshi += satoshi + if totalSatoshi < 0 { + str := fmt.Sprintf("total value of all transaction "+ + "outputs exceeds max allowed value of %v", + btcutil.MaxSatoshi) + return ruleError(ErrBadTxOutValue, str) + } + if totalSatoshi > btcutil.MaxSatoshi { + str := fmt.Sprintf("total value of all transaction "+ + "outputs is %v which is higher than max "+ + "allowed value of %v", totalSatoshi, + btcutil.MaxSatoshi) + return ruleError(ErrBadTxOutValue, str) + } + } + + // Check for duplicate transaction inputs. + existingTxOut := make(map[wire.OutPoint]struct{}) + for _, txIn := range msgTx.TxIn { + if _, exists := existingTxOut[txIn.PreviousOutPoint]; exists { + return ruleError(ErrDuplicateTxInputs, "transaction "+ + "contains duplicate inputs") + } + existingTxOut[txIn.PreviousOutPoint] = struct{}{} + } + + // Coinbase script length must be between min and max length. + if IsCoinBase(tx) { + slen := len(msgTx.TxIn[0].SignatureScript) + if slen < MinCoinbaseScriptLen || slen > MaxCoinbaseScriptLen { + str := fmt.Sprintf("coinbase transaction script length "+ + "of %d is out of range (min: %d, max: %d)", + slen, MinCoinbaseScriptLen, MaxCoinbaseScriptLen) + return ruleError(ErrBadCoinbaseScriptLen, str) + } + } else { + // Previous transaction outputs referenced by the inputs to this + // transaction must not be null. + for _, txIn := range msgTx.TxIn { + prevOut := &txIn.PreviousOutPoint + if isNullOutpoint(prevOut) { + return ruleError(ErrBadTxInput, "transaction "+ + "input refers to previous output that "+ + "is null") + } + } + } + + return nil +} + +// checkProofOfWork ensures the block header bits which indicate the target +// difficulty is in min/max range and that the block hash is less than the +// target difficulty as claimed. +// +// The flags modify the behavior of this function as follows: +// - BFNoPoWCheck: The check to ensure the block hash is less than the target +// difficulty is not performed. +func checkProofOfWork(header *wire.BlockHeader, powLimit *big.Int, flags BehaviorFlags) error { + // The target difficulty must be larger than zero. + target := CompactToBig(header.Bits) + if target.Sign() <= 0 { + str := fmt.Sprintf("block target difficulty of %064x is too low", + target) + return ruleError(ErrUnexpectedDifficulty, str) + } + + // The target difficulty must be less than the maximum allowed. + if target.Cmp(powLimit) > 0 { + str := fmt.Sprintf("block target difficulty of %064x is "+ + "higher than max of %064x", target, powLimit) + return ruleError(ErrUnexpectedDifficulty, str) + } + + // The block hash must be less than the claimed target unless the flag + // to avoid proof of work checks is set. + if flags&BFNoPoWCheck != BFNoPoWCheck { + // The block hash must be less than the claimed target. + hash := header.BlockSha() + hashNum := ShaHashToBig(&hash) + if hashNum.Cmp(target) > 0 { + str := fmt.Sprintf("block hash of %064x is higher than "+ + "expected max of %064x", hashNum, target) + return ruleError(ErrHighHash, str) + } + } + + return nil +} + +// CheckProofOfWork ensures the block header bits which indicate the target +// difficulty is in min/max range and that the block hash is less than the +// target difficulty as claimed. +func CheckProofOfWork(block *btcutil.Block, powLimit *big.Int) error { + return checkProofOfWork(&block.MsgBlock().Header, powLimit, BFNone) +} + +// CountSigOps returns the number of signature operations for all transaction +// input and output scripts in the provided transaction. This uses the +// quicker, but imprecise, signature operation counting mechanism from +// txscript. +func CountSigOps(tx *btcutil.Tx) int { + msgTx := tx.MsgTx() + + // Accumulate the number of signature operations in all transaction + // inputs. + totalSigOps := 0 + for _, txIn := range msgTx.TxIn { + numSigOps := txscript.GetSigOpCount(txIn.SignatureScript) + totalSigOps += numSigOps + } + + // Accumulate the number of signature operations in all transaction + // outputs. + for _, txOut := range msgTx.TxOut { + numSigOps := txscript.GetSigOpCount(txOut.PkScript) + totalSigOps += numSigOps + } + + return totalSigOps +} + +// CountP2SHSigOps returns the number of signature operations for all input +// transactions which are of the pay-to-script-hash type. This uses the +// precise, signature operation counting mechanism from the script engine which +// requires access to the input transaction scripts. +func CountP2SHSigOps(tx *btcutil.Tx, isCoinBaseTx bool, utxoView *UtxoViewpoint) (int, error) { + // Coinbase transactions have no interesting inputs. + if isCoinBaseTx { + return 0, nil + } + + // Accumulate the number of signature operations in all transaction + // inputs. + msgTx := tx.MsgTx() + totalSigOps := 0 + for txInIndex, txIn := range msgTx.TxIn { + // Ensure the referenced input transaction is available. + originTxHash := &txIn.PreviousOutPoint.Hash + originTxIndex := txIn.PreviousOutPoint.Index + txEntry := utxoView.LookupEntry(originTxHash) + if txEntry == nil || txEntry.IsOutputSpent(originTxIndex) { + str := fmt.Sprintf("unable to find unspent output "+ + "%v referenced from transaction %s:%d", + txIn.PreviousOutPoint, tx.Sha(), txInIndex) + return 0, ruleError(ErrMissingTx, str) + } + + // We're only interested in pay-to-script-hash types, so skip + // this input if it's not one. + pkScript := txEntry.PkScriptByIndex(originTxIndex) + if !txscript.IsPayToScriptHash(pkScript) { + continue + } + + // Count the precise number of signature operations in the + // referenced public key script. + sigScript := txIn.SignatureScript + numSigOps := txscript.GetPreciseSigOpCount(sigScript, pkScript, + true) + + // We could potentially overflow the accumulator so check for + // overflow. + lastSigOps := totalSigOps + totalSigOps += numSigOps + if totalSigOps < lastSigOps { + str := fmt.Sprintf("the public key script from output "+ + "%v contains too many signature operations - "+ + "overflow", txIn.PreviousOutPoint) + return 0, ruleError(ErrTooManySigOps, str) + } + } + + return totalSigOps, nil +} + +// checkBlockHeaderSanity performs some preliminary checks on a block header to +// ensure it is sane before continuing with processing. These checks are +// context free. +// +// The flags do not modify the behavior of this function directly, however they +// are needed to pass along to checkProofOfWork. +func checkBlockHeaderSanity(header *wire.BlockHeader, powLimit *big.Int, timeSource MedianTimeSource, flags BehaviorFlags) error { + // Ensure the proof of work bits in the block header is in min/max range + // and the block hash is less than the target value described by the + // bits. + err := checkProofOfWork(header, powLimit, flags) + if err != nil { + return err + } + + // A block timestamp must not have a greater precision than one second. + // This check is necessary because Go time.Time values support + // nanosecond precision whereas the consensus rules only apply to + // seconds and it's much nicer to deal with standard Go time values + // instead of converting to seconds everywhere. + if !header.Timestamp.Equal(time.Unix(header.Timestamp.Unix(), 0)) { + str := fmt.Sprintf("block timestamp of %v has a higher "+ + "precision than one second", header.Timestamp) + return ruleError(ErrInvalidTime, str) + } + + // Ensure the block time is not too far in the future. + maxTimestamp := timeSource.AdjustedTime().Add(time.Second * + MaxTimeOffsetSeconds) + if header.Timestamp.After(maxTimestamp) { + str := fmt.Sprintf("block timestamp of %v is too far in the "+ + "future", header.Timestamp) + return ruleError(ErrTimeTooNew, str) + } + + return nil +} + +// checkBlockSanity performs some preliminary checks on a block to ensure it is +// sane before continuing with block processing. These checks are context free. +// +// The flags do not modify the behavior of this function directly, however they +// are needed to pass along to checkBlockHeaderSanity. +func checkBlockSanity(block *btcutil.Block, powLimit *big.Int, timeSource MedianTimeSource, flags BehaviorFlags) error { + msgBlock := block.MsgBlock() + header := &msgBlock.Header + err := checkBlockHeaderSanity(header, powLimit, timeSource, flags) + if err != nil { + return err + } + + // A block must have at least one transaction. + numTx := len(msgBlock.Transactions) + if numTx == 0 { + return ruleError(ErrNoTransactions, "block does not contain "+ + "any transactions") + } + + // A block must not have more transactions than the max block payload. + if numTx > wire.MaxBlockPayload { + str := fmt.Sprintf("block contains too many transactions - "+ + "got %d, max %d", numTx, wire.MaxBlockPayload) + return ruleError(ErrTooManyTransactions, str) + } + + // A block must not exceed the maximum allowed block payload when + // serialized. + serializedSize := msgBlock.SerializeSize() + if serializedSize > wire.MaxBlockPayload { + str := fmt.Sprintf("serialized block is too big - got %d, "+ + "max %d", serializedSize, wire.MaxBlockPayload) + return ruleError(ErrBlockTooBig, str) + } + + // The first transaction in a block must be a coinbase. + transactions := block.Transactions() + if !IsCoinBase(transactions[0]) { + return ruleError(ErrFirstTxNotCoinbase, "first transaction in "+ + "block is not a coinbase") + } + + // A block must not have more than one coinbase. + for i, tx := range transactions[1:] { + if IsCoinBase(tx) { + str := fmt.Sprintf("block contains second coinbase at "+ + "index %d", i+1) + return ruleError(ErrMultipleCoinbases, str) + } + } + + // Do some preliminary checks on each transaction to ensure they are + // sane before continuing. + for _, tx := range transactions { + err := CheckTransactionSanity(tx) + if err != nil { + return err + } + } + + // Build merkle tree and ensure the calculated merkle root matches the + // entry in the block header. This also has the effect of caching all + // of the transaction hashes in the block to speed up future hash + // checks. Bitcoind builds the tree here and checks the merkle root + // after the following checks, but there is no reason not to check the + // merkle root matches here. + merkles := BuildMerkleTreeStore(block.Transactions()) + calculatedMerkleRoot := merkles[len(merkles)-1] + if !header.MerkleRoot.IsEqual(calculatedMerkleRoot) { + str := fmt.Sprintf("block merkle root is invalid - block "+ + "header indicates %v, but calculated value is %v", + header.MerkleRoot, calculatedMerkleRoot) + return ruleError(ErrBadMerkleRoot, str) + } + + // Check for duplicate transactions. This check will be fairly quick + // since the transaction hashes are already cached due to building the + // merkle tree above. + existingTxHashes := make(map[wire.ShaHash]struct{}) + for _, tx := range transactions { + hash := tx.Sha() + if _, exists := existingTxHashes[*hash]; exists { + str := fmt.Sprintf("block contains duplicate "+ + "transaction %v", hash) + return ruleError(ErrDuplicateTx, str) + } + existingTxHashes[*hash] = struct{}{} + } + + // The number of signature operations must be less than the maximum + // allowed per block. + totalSigOps := 0 + for _, tx := range transactions { + // We could potentially overflow the accumulator so check for + // overflow. + lastSigOps := totalSigOps + totalSigOps += CountSigOps(tx) + if totalSigOps < lastSigOps || totalSigOps > MaxSigOpsPerBlock { + str := fmt.Sprintf("block contains too many signature "+ + "operations - got %v, max %v", totalSigOps, + MaxSigOpsPerBlock) + return ruleError(ErrTooManySigOps, str) + } + } + + return nil +} + +// CheckBlockSanity performs some preliminary checks on a block to ensure it is +// sane before continuing with block processing. These checks are context free. +func CheckBlockSanity(block *btcutil.Block, powLimit *big.Int, timeSource MedianTimeSource) error { + return checkBlockSanity(block, powLimit, timeSource, BFNone) +} + +// ExtractCoinbaseHeight attempts to extract the height of the block from the +// scriptSig of a coinbase transaction. Coinbase heights are only present in +// blocks of version 2 or later. This was added as part of BIP0034. +func ExtractCoinbaseHeight(coinbaseTx *btcutil.Tx) (int32, error) { + sigScript := coinbaseTx.MsgTx().TxIn[0].SignatureScript + if len(sigScript) < 1 { + str := "the coinbase signature script for blocks of " + + "version %d or greater must start with the " + + "length of the serialized block height" + str = fmt.Sprintf(str, serializedHeightVersion) + return 0, ruleError(ErrMissingCoinbaseHeight, str) + } + + serializedLen := int(sigScript[0]) + if len(sigScript[1:]) < serializedLen { + str := "the coinbase signature script for blocks of " + + "version %d or greater must start with the " + + "serialized block height" + str = fmt.Sprintf(str, serializedLen) + return 0, ruleError(ErrMissingCoinbaseHeight, str) + } + + serializedHeightBytes := make([]byte, 8, 8) + copy(serializedHeightBytes, sigScript[1:serializedLen+1]) + serializedHeight := binary.LittleEndian.Uint64(serializedHeightBytes) + + return int32(serializedHeight), nil +} + +// checkSerializedHeight checks if the signature script in the passed +// transaction starts with the serialized block height of wantHeight. +func checkSerializedHeight(coinbaseTx *btcutil.Tx, wantHeight int32) error { + serializedHeight, err := ExtractCoinbaseHeight(coinbaseTx) + if err != nil { + return err + } + + if serializedHeight != wantHeight { + str := fmt.Sprintf("the coinbase signature script serialized "+ + "block height is %d when %d was expected", + serializedHeight, wantHeight) + return ruleError(ErrBadCoinbaseHeight, str) + } + return nil +} + +// checkBlockHeaderContext peforms several validation checks on the block header +// which depend on its position within the block chain. +// +// The flags modify the behavior of this function as follows: +// - BFFastAdd: All checks except those involving comparing the header against +// the checkpoints are not performed. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode *blockNode, flags BehaviorFlags) error { + // The genesis block is valid by definition. + if prevNode == nil { + return nil + } + + fastAdd := flags&BFFastAdd == BFFastAdd + if !fastAdd { + // Ensure the difficulty specified in the block header matches + // the calculated difficulty based on the previous block and + // difficulty retarget rules. + expectedDifficulty, err := b.calcNextRequiredDifficulty(prevNode, + header.Timestamp) + if err != nil { + return err + } + blockDifficulty := header.Bits + if blockDifficulty != expectedDifficulty { + str := "block difficulty of %d is not the expected value of %d" + str = fmt.Sprintf(str, blockDifficulty, expectedDifficulty) + return ruleError(ErrUnexpectedDifficulty, str) + } + + // Ensure the timestamp for the block header is after the + // median time of the last several blocks (medianTimeBlocks). + medianTime, err := b.calcPastMedianTime(prevNode) + if err != nil { + log.Errorf("calcPastMedianTime: %v", err) + return err + } + if !header.Timestamp.After(medianTime) { + str := "block timestamp of %v is not after expected %v" + str = fmt.Sprintf(str, header.Timestamp, medianTime) + return ruleError(ErrTimeTooOld, str) + } + } + + // The height of this block is one more than the referenced previous + // block. + blockHeight := prevNode.height + 1 + + // Ensure chain matches up to predetermined checkpoints. + blockHash := header.BlockSha() + if !b.verifyCheckpoint(blockHeight, &blockHash) { + str := fmt.Sprintf("block at height %d does not match "+ + "checkpoint hash", blockHeight) + return ruleError(ErrBadCheckpoint, str) + } + + // Find the previous checkpoint and prevent blocks which fork the main + // chain before it. This prevents storage of new, otherwise valid, + // blocks which build off of old blocks that are likely at a much easier + // difficulty and therefore could be used to waste cache and disk space. + checkpointBlock, err := b.findPreviousCheckpoint() + if err != nil { + return err + } + if checkpointBlock != nil && blockHeight < checkpointBlock.Height() { + str := fmt.Sprintf("block at height %d forks the main chain "+ + "before the previous checkpoint at height %d", + blockHeight, checkpointBlock.Height()) + return ruleError(ErrForkTooOld, str) + } + + if !fastAdd { + // Reject version 3 blocks once a majority of the network has + // upgraded. This is part of BIP0065. + if header.Version < 4 && b.isMajorityVersion(4, prevNode, + b.chainParams.BlockRejectNumRequired) { + + str := "new blocks with version %d are no longer valid" + str = fmt.Sprintf(str, header.Version) + return ruleError(ErrBlockVersionTooOld, str) + } + + // Reject version 2 blocks once a majority of the network has + // upgraded. This is part of BIP0066. + if header.Version < 3 && b.isMajorityVersion(3, prevNode, + b.chainParams.BlockRejectNumRequired) { + + str := "new blocks with version %d are no longer valid" + str = fmt.Sprintf(str, header.Version) + return ruleError(ErrBlockVersionTooOld, str) + } + + // Reject version 1 blocks once a majority of the network has + // upgraded. This is part of BIP0034. + if header.Version < 2 && b.isMajorityVersion(2, prevNode, + b.chainParams.BlockRejectNumRequired) { + + str := "new blocks with version %d are no longer valid" + str = fmt.Sprintf(str, header.Version) + return ruleError(ErrBlockVersionTooOld, str) + } + } + + return nil +} + +// checkBlockContext peforms several validation checks on the block which depend +// on its position within the block chain. +// +// The flags modify the behavior of this function as follows: +// - BFFastAdd: The transaction are not checked to see if they are finalized +// and the somewhat expensive BIP0034 validation is not performed. +// +// The flags are also passed to checkBlockHeaderContext. See its documentation +// for how the flags modify its behavior. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) checkBlockContext(block *btcutil.Block, prevNode *blockNode, flags BehaviorFlags) error { + // The genesis block is valid by definition. + if prevNode == nil { + return nil + } + + // Perform all block header related validation checks. + header := &block.MsgBlock().Header + err := b.checkBlockHeaderContext(header, prevNode, flags) + if err != nil { + return err + } + + fastAdd := flags&BFFastAdd == BFFastAdd + if !fastAdd { + // The height of this block is one more than the referenced + // previous block. + blockHeight := prevNode.height + 1 + + // Ensure all transactions in the block are finalized. + for _, tx := range block.Transactions() { + if !IsFinalizedTransaction(tx, blockHeight, + header.Timestamp) { + + str := fmt.Sprintf("block contains unfinalized "+ + "transaction %v", tx.Sha()) + return ruleError(ErrUnfinalizedTx, str) + } + } + + // Ensure coinbase starts with serialized block heights for + // blocks whose version is the serializedHeightVersion or newer + // once a majority of the network has upgraded. This is part of + // BIP0034. + if ShouldHaveSerializedBlockHeight(header) && + b.isMajorityVersion(serializedHeightVersion, prevNode, + b.chainParams.BlockEnforceNumRequired) { + + coinbaseTx := block.Transactions()[0] + err := checkSerializedHeight(coinbaseTx, blockHeight) + if err != nil { + return err + } + } + } + + return nil +} + +// checkBIP0030 ensures blocks do not contain duplicate transactions which +// 'overwrite' older transactions that are not fully spent. This prevents an +// attack where a coinbase and all of its dependent transactions could be +// duplicated to effectively revert the overwritten transactions to a single +// confirmation thereby making them vulnerable to a double spend. +// +// For more details, see https://en.bitcoin.it/wiki/BIP_0030 and +// http://r6.ca/blog/20120206T005236Z.html. +// +// This function MUST be called with the chain state lock held (for reads). +func (b *BlockChain) checkBIP0030(node *blockNode, block *btcutil.Block, view *UtxoViewpoint) error { + // Fetch utxo details for all of the transactions in this block. + // Typically, there will not be any utxos for any of the transactions. + fetchSet := make(map[wire.ShaHash]struct{}) + for _, tx := range block.Transactions() { + fetchSet[*tx.Sha()] = struct{}{} + } + err := view.fetchUtxos(b.db, fetchSet) + if err != nil { + return err + } + + // Duplicate transactions are only allowed if the previous transaction + // is fully spent. + for _, tx := range block.Transactions() { + txEntry := view.LookupEntry(tx.Sha()) + if txEntry != nil && !txEntry.IsFullySpent() { + str := fmt.Sprintf("tried to overwrite transaction %v "+ + "at block height %d that is not fully spent", + tx.Sha(), txEntry.blockHeight) + return ruleError(ErrOverwriteTx, str) + } + } + + return nil +} + +// CheckTransactionInputs performs a series of checks on the inputs to a +// transaction to ensure they are valid. An example of some of the checks +// include verifying all inputs exist, ensuring the coinbase seasoning +// requirements are met, detecting double spends, validating all values and fees +// are in the legal range and the total output amount doesn't exceed the input +// amount, and verifying the signatures to prove the spender was the owner of +// the bitcoins and therefore allowed to spend them. As it checks the inputs, +// it also calculates the total fees for the transaction and returns that value. +// +// NOTE: The transaction MUST have already been sanity checked with the +// CheckTransactionSanity function prior to calling this function. +func CheckTransactionInputs(tx *btcutil.Tx, txHeight int32, utxoView *UtxoViewpoint) (int64, error) { + // Coinbase transactions have no inputs. + if IsCoinBase(tx) { + return 0, nil + } + + txHash := tx.Sha() + var totalSatoshiIn int64 + for txInIndex, txIn := range tx.MsgTx().TxIn { + // Ensure the referenced input transaction is available. + originTxHash := &txIn.PreviousOutPoint.Hash + utxoEntry := utxoView.LookupEntry(originTxHash) + if utxoEntry == nil { + str := fmt.Sprintf("unable to find unspent output "+ + "%v referenced from transaction %s:%d", + txIn.PreviousOutPoint, tx.Sha(), txInIndex) + return 0, ruleError(ErrMissingTx, str) + } + + // Ensure the transaction is not spending coins which have not + // yet reached the required coinbase maturity. + if utxoEntry.IsCoinBase() { + originHeight := int32(utxoEntry.BlockHeight()) + blocksSincePrev := txHeight - originHeight + if blocksSincePrev < coinbaseMaturity { + str := fmt.Sprintf("tried to spend coinbase "+ + "transaction %v from height %v at "+ + "height %v before required maturity "+ + "of %v blocks", originTxHash, + originHeight, txHeight, + coinbaseMaturity) + return 0, ruleError(ErrImmatureSpend, str) + } + } + + // Ensure the transaction is not double spending coins. + originTxIndex := txIn.PreviousOutPoint.Index + if utxoEntry.IsOutputSpent(originTxIndex) { + str := fmt.Sprintf("transaction %s:%d tried to double "+ + "spend output %v", txHash, txInIndex, + txIn.PreviousOutPoint) + return 0, ruleError(ErrDoubleSpend, str) + } + + // Ensure the transaction amounts are in range. Each of the + // output values of the input transactions must not be negative + // or more than the max allowed per transaction. All amounts in + // a transaction are in a unit value known as a satoshi. One + // bitcoin is a quantity of satoshi as defined by the + // SatoshiPerBitcoin constant. + originTxSatoshi := utxoEntry.AmountByIndex(originTxIndex) + if originTxSatoshi < 0 { + str := fmt.Sprintf("transaction output has negative "+ + "value of %v", btcutil.Amount(originTxSatoshi)) + return 0, ruleError(ErrBadTxOutValue, str) + } + if originTxSatoshi > btcutil.MaxSatoshi { + str := fmt.Sprintf("transaction output value of %v is "+ + "higher than max allowed value of %v", + btcutil.Amount(originTxSatoshi), + btcutil.MaxSatoshi) + return 0, ruleError(ErrBadTxOutValue, str) + } + + // The total of all outputs must not be more than the max + // allowed per transaction. Also, we could potentially overflow + // the accumulator so check for overflow. + lastSatoshiIn := totalSatoshiIn + totalSatoshiIn += originTxSatoshi + if totalSatoshiIn < lastSatoshiIn || + totalSatoshiIn > btcutil.MaxSatoshi { + str := fmt.Sprintf("total value of all transaction "+ + "inputs is %v which is higher than max "+ + "allowed value of %v", totalSatoshiIn, + btcutil.MaxSatoshi) + return 0, ruleError(ErrBadTxOutValue, str) + } + } + + // Calculate the total output amount for this transaction. It is safe + // to ignore overflow and out of range errors here because those error + // conditions would have already been caught by checkTransactionSanity. + var totalSatoshiOut int64 + for _, txOut := range tx.MsgTx().TxOut { + totalSatoshiOut += txOut.Value + } + + // Ensure the transaction does not spend more than its inputs. + if totalSatoshiIn < totalSatoshiOut { + str := fmt.Sprintf("total value of all transaction inputs for "+ + "transaction %v is %v which is less than the amount "+ + "spent of %v", txHash, totalSatoshiIn, totalSatoshiOut) + return 0, ruleError(ErrSpendTooHigh, str) + } + + // NOTE: bitcoind checks if the transaction fees are < 0 here, but that + // is an impossible condition because of the check above that ensures + // the inputs are >= the outputs. + txFeeInSatoshi := totalSatoshiIn - totalSatoshiOut + return txFeeInSatoshi, nil +} + +// checkConnectBlock performs several checks to confirm connecting the passed +// block to the chain represented by the passed view does not violate any rules. +// In addition, the passed view is updated to spend all of the referenced +// outputs and add all of the new utxos created by block. Thus, the view will +// represent the state of the chain as if the block were actually connected and +// consequently the best hash for the view is also updated to passed block. +// +// The CheckConnectBlock function makes use of this function to perform the +// bulk of its work. The only difference is this function accepts a node which +// may or may not require reorganization to connect it to the main chain whereas +// CheckConnectBlock creates a new node which specifically connects to the end +// of the current main chain and then calls this function with that node. +// +// See the comments for CheckConnectBlock for some examples of the type of +// checks performed by this function. +// +// This function MUST be called with the chain state lock held (for writes). +func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block, view *UtxoViewpoint, stxos *[]spentTxOut) error { + // If the side chain blocks end up in the database, a call to + // CheckBlockSanity should be done here in case a previous version + // allowed a block that is no longer valid. However, since the + // implementation only currently uses memory for the side chain blocks, + // it isn't currently necessary. + + // The coinbase for the Genesis block is not spendable, so just return + // an error now. + if node.hash.IsEqual(b.chainParams.GenesisHash) { + str := "the coinbase for the genesis block is not spendable" + return ruleError(ErrMissingTx, str) + } + + // Ensure the view is for the node being checked. + if !view.BestHash().IsEqual(node.parentHash) { + return AssertError(fmt.Sprintf("inconsistent view when "+ + "checking block connection: best hash is %v instead "+ + "of expected %v", view.BestHash(), node.hash)) + } + + // BIP0030 added a rule to prevent blocks which contain duplicate + // transactions that 'overwrite' older transactions which are not fully + // spent. See the documentation for checkBIP0030 for more details. + // + // There are two blocks in the chain which violate this rule, so the + // check must be skipped for those blocks. The isBIP0030Node function is + // used to determine if this block is one of the two blocks that must be + // skipped. + enforceBIP0030 := !isBIP0030Node(node) + if enforceBIP0030 { + err := b.checkBIP0030(node, block, view) + if err != nil { + return err + } + } + + // Load all of the utxos referenced by the inputs for all transactions + // in the block don't already exist in the utxo view from the database. + // + // These utxo entries are needed for verification of things such as + // transaction inputs, counting pay-to-script-hashes, and scripts. + err := view.fetchInputUtxos(b.db, block) + if err != nil { + return err + } + + // BIP0016 describes a pay-to-script-hash type that is considered a + // "standard" type. The rules for this BIP only apply to transactions + // after the timestamp defined by txscript.Bip16Activation. See + // https://en.bitcoin.it/wiki/BIP_0016 for more details. + enforceBIP0016 := node.timestamp.After(txscript.Bip16Activation) + + // The number of signature operations must be less than the maximum + // allowed per block. Note that the preliminary sanity checks on a + // block also include a check similar to this one, but this check + // expands the count to include a precise count of pay-to-script-hash + // signature operations in each of the input transaction public key + // scripts. + transactions := block.Transactions() + totalSigOps := 0 + for i, tx := range transactions { + numsigOps := CountSigOps(tx) + if enforceBIP0016 { + // Since the first (and only the first) transaction has + // already been verified to be a coinbase transaction, + // use i == 0 as an optimization for the flag to + // countP2SHSigOps for whether or not the transaction is + // a coinbase transaction rather than having to do a + // full coinbase check again. + numP2SHSigOps, err := CountP2SHSigOps(tx, i == 0, view) + if err != nil { + return err + } + numsigOps += numP2SHSigOps + } + + // Check for overflow or going over the limits. We have to do + // this on every loop iteration to avoid overflow. + lastSigops := totalSigOps + totalSigOps += numsigOps + if totalSigOps < lastSigops || totalSigOps > MaxSigOpsPerBlock { + str := fmt.Sprintf("block contains too many "+ + "signature operations - got %v, max %v", + totalSigOps, MaxSigOpsPerBlock) + return ruleError(ErrTooManySigOps, str) + } + } + + // Perform several checks on the inputs for each transaction. Also + // accumulate the total fees. This could technically be combined with + // the loop above instead of running another loop over the transactions, + // but by separating it we can avoid running the more expensive (though + // still relatively cheap as compared to running the scripts) checks + // against all the inputs when the signature operations are out of + // bounds. + var totalFees int64 + for _, tx := range transactions { + txFee, err := CheckTransactionInputs(tx, node.height, view) + if err != nil { + return err + } + + // Sum the total fees and ensure we don't overflow the + // accumulator. + lastTotalFees := totalFees + totalFees += txFee + if totalFees < lastTotalFees { + return ruleError(ErrBadFees, "total fees for block "+ + "overflows accumulator") + } + + // Add all of the outputs for this transaction which are not + // provably unspendable as available utxos. Also, the passed + // spent txos slice is updated to contain an entry for each + // spent txout in the order each transaction spends them. + err = view.connectTransaction(tx, node.height, stxos) + if err != nil { + return err + } + } + + // The total output values of the coinbase transaction must not exceed + // the expected subsidy value plus total transaction fees gained from + // mining the block. It is safe to ignore overflow and out of range + // errors here because those error conditions would have already been + // caught by checkTransactionSanity. + var totalSatoshiOut int64 + for _, txOut := range transactions[0].MsgTx().TxOut { + totalSatoshiOut += txOut.Value + } + expectedSatoshiOut := CalcBlockSubsidy(node.height, b.chainParams) + + totalFees + if totalSatoshiOut > expectedSatoshiOut { + str := fmt.Sprintf("coinbase transaction for block pays %v "+ + "which is more than expected value of %v", + totalSatoshiOut, expectedSatoshiOut) + return ruleError(ErrBadCoinbaseValue, str) + } + + // Don't run scripts if this node is before the latest known good + // checkpoint since the validity is verified via the checkpoints (all + // transactions are included in the merkle root hash and any changes + // will therefore be detected by the next checkpoint). This is a huge + // optimization because running the scripts is the most time consuming + // portion of block handling. + checkpoint := b.latestCheckpoint() + runScripts := !b.noVerify + if checkpoint != nil && node.height <= checkpoint.Height { + runScripts = false + } + + // Get the previous block node. This function is used over simply + // accessing node.parent directly as it will dynamically create previous + // block nodes as needed. This helps allow only the pieces of the chain + // that are needed to remain in memory. + prevNode, err := b.getPrevNodeFromNode(node) + if err != nil { + log.Errorf("getPrevNodeFromNode: %v", err) + return err + } + + // Blocks created after the BIP0016 activation time need to have the + // pay-to-script-hash checks enabled. + var scriptFlags txscript.ScriptFlags + if enforceBIP0016 { + scriptFlags |= txscript.ScriptBip16 + } + + // Enforce DER signatures for block versions 3+ once the majority of the + // network has upgraded to the enforcement threshold. This is part of + // BIP0066. + blockHeader := &block.MsgBlock().Header + if blockHeader.Version >= 3 && b.isMajorityVersion(3, prevNode, + b.chainParams.BlockEnforceNumRequired) { + + scriptFlags |= txscript.ScriptVerifyDERSignatures + } + + // Enforce CHECKLOCKTIMEVERIFY for block versions 4+ once the majority + // of the network has upgraded to the enforcement threshold. This is + // part of BIP0065. + if blockHeader.Version >= 4 && b.isMajorityVersion(4, prevNode, + b.chainParams.BlockEnforceNumRequired) { + + scriptFlags |= txscript.ScriptVerifyCheckLockTimeVerify + } + + // Now that the inexpensive checks are done and have passed, verify the + // transactions are actually allowed to spend the coins by running the + // expensive ECDSA signature check scripts. Doing this last helps + // prevent CPU exhaustion attacks. + if runScripts { + err := checkBlockScripts(block, view, scriptFlags, b.sigCache) + if err != nil { + return err + } + } + + // Update the best hash for view to include this block since all of its + // transactions have been connected. + view.SetBestHash(node.hash) + + return nil +} + +// CheckConnectBlock performs several checks to confirm connecting the passed +// block to the main chain does not violate any rules. An example of some of +// the checks performed are ensuring connecting the block would not cause any +// duplicate transaction hashes for old transactions that aren't already fully +// spent, double spends, exceeding the maximum allowed signature operations +// per block, invalid values in relation to the expected block subsidy, or fail +// transaction script validation. +// +// This function is safe for concurrent access. +func (b *BlockChain) CheckConnectBlock(block *btcutil.Block) error { + b.chainLock.Lock() + defer b.chainLock.Unlock() + + prevNode := b.bestNode + newNode := newBlockNode(&block.MsgBlock().Header, block.Sha(), + prevNode.height+1) + newNode.parent = prevNode + newNode.workSum.Add(prevNode.workSum, newNode.workSum) + + // Leave the spent txouts entry nil in the state since the information + // is not needed and thus extra work can be avoided. + view := NewUtxoViewpoint() + view.SetBestHash(prevNode.hash) + return b.checkConnectBlock(newNode, block, view, nil) +} diff --git a/vendor/github.com/btcsuite/btcd/blockchain/validate_test.go b/vendor/github.com/btcsuite/btcd/blockchain/validate_test.go new file mode 100644 index 0000000000000000000000000000000000000000..391449a5204f6b3424b86d3dbc891eab4f0abca2 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockchain/validate_test.go @@ -0,0 +1,375 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package blockchain_test + +import ( + "math" + "reflect" + "testing" + "time" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// TestCheckConnectBlock tests the CheckConnectBlock function to ensure it +// fails. +func TestCheckConnectBlock(t *testing.T) { + // Create a new database and chain instance to run tests against. + chain, teardownFunc, err := chainSetup("checkconnectblock") + if err != nil { + t.Errorf("Failed to setup chain instance: %v", err) + return + } + defer teardownFunc() + + // The genesis block should fail to connect since it's already inserted. + genesisBlock := chaincfg.MainNetParams.GenesisBlock + err = chain.CheckConnectBlock(btcutil.NewBlock(genesisBlock)) + if err == nil { + t.Errorf("CheckConnectBlock: Did not received expected error") + } +} + +// TestCheckBlockSanity tests the CheckBlockSanity function to ensure it works +// as expected. +func TestCheckBlockSanity(t *testing.T) { + powLimit := chaincfg.MainNetParams.PowLimit + block := btcutil.NewBlock(&Block100000) + timeSource := blockchain.NewMedianTime() + err := blockchain.CheckBlockSanity(block, powLimit, timeSource) + if err != nil { + t.Errorf("CheckBlockSanity: %v", err) + } + + // Ensure a block that has a timestamp with a precision higher than one + // second fails. + timestamp := block.MsgBlock().Header.Timestamp + block.MsgBlock().Header.Timestamp = timestamp.Add(time.Nanosecond) + err = blockchain.CheckBlockSanity(block, powLimit, timeSource) + if err == nil { + t.Errorf("CheckBlockSanity: error is nil when it shouldn't be") + } +} + +// TestCheckSerializedHeight tests the checkSerializedHeight function with +// various serialized heights and also does negative tests to ensure errors +// and handled properly. +func TestCheckSerializedHeight(t *testing.T) { + // Create an empty coinbase template to be used in the tests below. + coinbaseOutpoint := wire.NewOutPoint(&wire.ShaHash{}, math.MaxUint32) + coinbaseTx := wire.NewMsgTx() + coinbaseTx.Version = 2 + coinbaseTx.AddTxIn(wire.NewTxIn(coinbaseOutpoint, nil)) + + // Expected rule errors. + missingHeightError := blockchain.RuleError{ + ErrorCode: blockchain.ErrMissingCoinbaseHeight, + } + badHeightError := blockchain.RuleError{ + ErrorCode: blockchain.ErrBadCoinbaseHeight, + } + + tests := []struct { + sigScript []byte // Serialized data + wantHeight int32 // Expected height + err error // Expected error type + }{ + // No serialized height length. + {[]byte{}, 0, missingHeightError}, + // Serialized height length with no height bytes. + {[]byte{0x02}, 0, missingHeightError}, + // Serialized height length with too few height bytes. + {[]byte{0x02, 0x4a}, 0, missingHeightError}, + // Serialized height that needs 2 bytes to encode. + {[]byte{0x02, 0x4a, 0x52}, 21066, nil}, + // Serialized height that needs 2 bytes to encode, but backwards + // endianness. + {[]byte{0x02, 0x4a, 0x52}, 19026, badHeightError}, + // Serialized height that needs 3 bytes to encode. + {[]byte{0x03, 0x40, 0x0d, 0x03}, 200000, nil}, + // Serialized height that needs 3 bytes to encode, but backwards + // endianness. + {[]byte{0x03, 0x40, 0x0d, 0x03}, 1074594560, badHeightError}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + msgTx := coinbaseTx.Copy() + msgTx.TxIn[0].SignatureScript = test.sigScript + tx := btcutil.NewTx(msgTx) + + err := blockchain.TstCheckSerializedHeight(tx, test.wantHeight) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("checkSerializedHeight #%d wrong error type "+ + "got: %v <%T>, want: %T", i, err, err, test.err) + continue + } + + if rerr, ok := err.(blockchain.RuleError); ok { + trerr := test.err.(blockchain.RuleError) + if rerr.ErrorCode != trerr.ErrorCode { + t.Errorf("checkSerializedHeight #%d wrong "+ + "error code got: %v, want: %v", i, + rerr.ErrorCode, trerr.ErrorCode) + continue + } + } + } +} + +// Block100000 defines block 100,000 of the block chain. It is used to +// test Block operations. +var Block100000 = wire.MsgBlock{ + Header: wire.BlockHeader{ + Version: 1, + PrevBlock: wire.ShaHash([32]byte{ // Make go vet happy. + 0x50, 0x12, 0x01, 0x19, 0x17, 0x2a, 0x61, 0x04, + 0x21, 0xa6, 0xc3, 0x01, 0x1d, 0xd3, 0x30, 0xd9, + 0xdf, 0x07, 0xb6, 0x36, 0x16, 0xc2, 0xcc, 0x1f, + 0x1c, 0xd0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + }), // 000000000002d01c1fccc21636b607dfd930d31d01c3a62104612a1719011250 + MerkleRoot: wire.ShaHash([32]byte{ // Make go vet happy. + 0x66, 0x57, 0xa9, 0x25, 0x2a, 0xac, 0xd5, 0xc0, + 0xb2, 0x94, 0x09, 0x96, 0xec, 0xff, 0x95, 0x22, + 0x28, 0xc3, 0x06, 0x7c, 0xc3, 0x8d, 0x48, 0x85, + 0xef, 0xb5, 0xa4, 0xac, 0x42, 0x47, 0xe9, 0xf3, + }), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766 + Timestamp: time.Unix(1293623863, 0), // 2010-12-29 11:57:43 +0000 UTC + Bits: 0x1b04864c, // 453281356 + Nonce: 0x10572b0f, // 274148111 + }, + Transactions: []*wire.MsgTx{ + { + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{ + 0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02, + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 0x12a05f200, // 5000000000 + PkScript: []byte{ + 0x41, // OP_DATA_65 + 0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25, + 0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73, + 0xdc, 0x44, 0x44, 0xac, 0xb8, 0x3c, 0x4e, 0xc7, + 0xa0, 0xe2, 0xf9, 0x9d, 0xd7, 0x45, 0x75, 0x16, + 0xc5, 0x81, 0x72, 0x42, 0xda, 0x79, 0x69, 0x24, + 0xca, 0x4e, 0x99, 0x94, 0x7d, 0x08, 0x7f, 0xed, + 0xf9, 0xce, 0x46, 0x7c, 0xb9, 0xf7, 0xc6, 0x28, + 0x70, 0x78, 0xf8, 0x01, 0xdf, 0x27, 0x6f, 0xdf, + 0x84, // 65-byte signature + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 0, + }, + { + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash([32]byte{ // Make go vet happy. + 0x03, 0x2e, 0x38, 0xe9, 0xc0, 0xa8, 0x4c, 0x60, + 0x46, 0xd6, 0x87, 0xd1, 0x05, 0x56, 0xdc, 0xac, + 0xc4, 0x1d, 0x27, 0x5e, 0xc5, 0x5f, 0xc0, 0x07, + 0x79, 0xac, 0x88, 0xfd, 0xf3, 0x57, 0xa1, 0x87, + }), // 87a157f3fd88ac7907c05fc55e271dc4acdc5605d187d646604ca8c0e9382e03 + Index: 0, + }, + SignatureScript: []byte{ + 0x49, // OP_DATA_73 + 0x30, 0x46, 0x02, 0x21, 0x00, 0xc3, 0x52, 0xd3, + 0xdd, 0x99, 0x3a, 0x98, 0x1b, 0xeb, 0xa4, 0xa6, + 0x3a, 0xd1, 0x5c, 0x20, 0x92, 0x75, 0xca, 0x94, + 0x70, 0xab, 0xfc, 0xd5, 0x7d, 0xa9, 0x3b, 0x58, + 0xe4, 0xeb, 0x5d, 0xce, 0x82, 0x02, 0x21, 0x00, + 0x84, 0x07, 0x92, 0xbc, 0x1f, 0x45, 0x60, 0x62, + 0x81, 0x9f, 0x15, 0xd3, 0x3e, 0xe7, 0x05, 0x5c, + 0xf7, 0xb5, 0xee, 0x1a, 0xf1, 0xeb, 0xcc, 0x60, + 0x28, 0xd9, 0xcd, 0xb1, 0xc3, 0xaf, 0x77, 0x48, + 0x01, // 73-byte signature + 0x41, // OP_DATA_65 + 0x04, 0xf4, 0x6d, 0xb5, 0xe9, 0xd6, 0x1a, 0x9d, + 0xc2, 0x7b, 0x8d, 0x64, 0xad, 0x23, 0xe7, 0x38, + 0x3a, 0x4e, 0x6c, 0xa1, 0x64, 0x59, 0x3c, 0x25, + 0x27, 0xc0, 0x38, 0xc0, 0x85, 0x7e, 0xb6, 0x7e, + 0xe8, 0xe8, 0x25, 0xdc, 0xa6, 0x50, 0x46, 0xb8, + 0x2c, 0x93, 0x31, 0x58, 0x6c, 0x82, 0xe0, 0xfd, + 0x1f, 0x63, 0x3f, 0x25, 0xf8, 0x7c, 0x16, 0x1b, + 0xc6, 0xf8, 0xa6, 0x30, 0x12, 0x1d, 0xf2, 0xb3, + 0xd3, // 65-byte pubkey + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 0x2123e300, // 556000000 + PkScript: []byte{ + 0x76, // OP_DUP + 0xa9, // OP_HASH160 + 0x14, // OP_DATA_20 + 0xc3, 0x98, 0xef, 0xa9, 0xc3, 0x92, 0xba, 0x60, + 0x13, 0xc5, 0xe0, 0x4e, 0xe7, 0x29, 0x75, 0x5e, + 0xf7, 0xf5, 0x8b, 0x32, + 0x88, // OP_EQUALVERIFY + 0xac, // OP_CHECKSIG + }, + }, + { + Value: 0x108e20f00, // 4444000000 + PkScript: []byte{ + 0x76, // OP_DUP + 0xa9, // OP_HASH160 + 0x14, // OP_DATA_20 + 0x94, 0x8c, 0x76, 0x5a, 0x69, 0x14, 0xd4, 0x3f, + 0x2a, 0x7a, 0xc1, 0x77, 0xda, 0x2c, 0x2f, 0x6b, + 0x52, 0xde, 0x3d, 0x7c, + 0x88, // OP_EQUALVERIFY + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 0, + }, + { + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash([32]byte{ // Make go vet happy. + 0xc3, 0x3e, 0xbf, 0xf2, 0xa7, 0x09, 0xf1, 0x3d, + 0x9f, 0x9a, 0x75, 0x69, 0xab, 0x16, 0xa3, 0x27, + 0x86, 0xaf, 0x7d, 0x7e, 0x2d, 0xe0, 0x92, 0x65, + 0xe4, 0x1c, 0x61, 0xd0, 0x78, 0x29, 0x4e, 0xcf, + }), // cf4e2978d0611ce46592e02d7e7daf8627a316ab69759a9f3df109a7f2bf3ec3 + Index: 1, + }, + SignatureScript: []byte{ + 0x47, // OP_DATA_71 + 0x30, 0x44, 0x02, 0x20, 0x03, 0x2d, 0x30, 0xdf, + 0x5e, 0xe6, 0xf5, 0x7f, 0xa4, 0x6c, 0xdd, 0xb5, + 0xeb, 0x8d, 0x0d, 0x9f, 0xe8, 0xde, 0x6b, 0x34, + 0x2d, 0x27, 0x94, 0x2a, 0xe9, 0x0a, 0x32, 0x31, + 0xe0, 0xba, 0x33, 0x3e, 0x02, 0x20, 0x3d, 0xee, + 0xe8, 0x06, 0x0f, 0xdc, 0x70, 0x23, 0x0a, 0x7f, + 0x5b, 0x4a, 0xd7, 0xd7, 0xbc, 0x3e, 0x62, 0x8c, + 0xbe, 0x21, 0x9a, 0x88, 0x6b, 0x84, 0x26, 0x9e, + 0xae, 0xb8, 0x1e, 0x26, 0xb4, 0xfe, 0x01, + 0x41, // OP_DATA_65 + 0x04, 0xae, 0x31, 0xc3, 0x1b, 0xf9, 0x12, 0x78, + 0xd9, 0x9b, 0x83, 0x77, 0xa3, 0x5b, 0xbc, 0xe5, + 0xb2, 0x7d, 0x9f, 0xff, 0x15, 0x45, 0x68, 0x39, + 0xe9, 0x19, 0x45, 0x3f, 0xc7, 0xb3, 0xf7, 0x21, + 0xf0, 0xba, 0x40, 0x3f, 0xf9, 0x6c, 0x9d, 0xee, + 0xb6, 0x80, 0xe5, 0xfd, 0x34, 0x1c, 0x0f, 0xc3, + 0xa7, 0xb9, 0x0d, 0xa4, 0x63, 0x1e, 0xe3, 0x95, + 0x60, 0x63, 0x9d, 0xb4, 0x62, 0xe9, 0xcb, 0x85, + 0x0f, // 65-byte pubkey + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 0xf4240, // 1000000 + PkScript: []byte{ + 0x76, // OP_DUP + 0xa9, // OP_HASH160 + 0x14, // OP_DATA_20 + 0xb0, 0xdc, 0xbf, 0x97, 0xea, 0xbf, 0x44, 0x04, + 0xe3, 0x1d, 0x95, 0x24, 0x77, 0xce, 0x82, 0x2d, + 0xad, 0xbe, 0x7e, 0x10, + 0x88, // OP_EQUALVERIFY + 0xac, // OP_CHECKSIG + }, + }, + { + Value: 0x11d260c0, // 299000000 + PkScript: []byte{ + 0x76, // OP_DUP + 0xa9, // OP_HASH160 + 0x14, // OP_DATA_20 + 0x6b, 0x12, 0x81, 0xee, 0xc2, 0x5a, 0xb4, 0xe1, + 0xe0, 0x79, 0x3f, 0xf4, 0xe0, 0x8a, 0xb1, 0xab, + 0xb3, 0x40, 0x9c, 0xd9, + 0x88, // OP_EQUALVERIFY + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 0, + }, + { + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash([32]byte{ // Make go vet happy. + 0x0b, 0x60, 0x72, 0xb3, 0x86, 0xd4, 0xa7, 0x73, + 0x23, 0x52, 0x37, 0xf6, 0x4c, 0x11, 0x26, 0xac, + 0x3b, 0x24, 0x0c, 0x84, 0xb9, 0x17, 0xa3, 0x90, + 0x9b, 0xa1, 0xc4, 0x3d, 0xed, 0x5f, 0x51, 0xf4, + }), // f4515fed3dc4a19b90a317b9840c243bac26114cf637522373a7d486b372600b + Index: 0, + }, + SignatureScript: []byte{ + 0x49, // OP_DATA_73 + 0x30, 0x46, 0x02, 0x21, 0x00, 0xbb, 0x1a, 0xd2, + 0x6d, 0xf9, 0x30, 0xa5, 0x1c, 0xce, 0x11, 0x0c, + 0xf4, 0x4f, 0x7a, 0x48, 0xc3, 0xc5, 0x61, 0xfd, + 0x97, 0x75, 0x00, 0xb1, 0xae, 0x5d, 0x6b, 0x6f, + 0xd1, 0x3d, 0x0b, 0x3f, 0x4a, 0x02, 0x21, 0x00, + 0xc5, 0xb4, 0x29, 0x51, 0xac, 0xed, 0xff, 0x14, + 0xab, 0xba, 0x27, 0x36, 0xfd, 0x57, 0x4b, 0xdb, + 0x46, 0x5f, 0x3e, 0x6f, 0x8d, 0xa1, 0x2e, 0x2c, + 0x53, 0x03, 0x95, 0x4a, 0xca, 0x7f, 0x78, 0xf3, + 0x01, // 73-byte signature + 0x41, // OP_DATA_65 + 0x04, 0xa7, 0x13, 0x5b, 0xfe, 0x82, 0x4c, 0x97, + 0xec, 0xc0, 0x1e, 0xc7, 0xd7, 0xe3, 0x36, 0x18, + 0x5c, 0x81, 0xe2, 0xaa, 0x2c, 0x41, 0xab, 0x17, + 0x54, 0x07, 0xc0, 0x94, 0x84, 0xce, 0x96, 0x94, + 0xb4, 0x49, 0x53, 0xfc, 0xb7, 0x51, 0x20, 0x65, + 0x64, 0xa9, 0xc2, 0x4d, 0xd0, 0x94, 0xd4, 0x2f, + 0xdb, 0xfd, 0xd5, 0xaa, 0xd3, 0xe0, 0x63, 0xce, + 0x6a, 0xf4, 0xcf, 0xaa, 0xea, 0x4e, 0xa1, 0x4f, + 0xbb, // 65-byte pubkey + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 0xf4240, // 1000000 + PkScript: []byte{ + 0x76, // OP_DUP + 0xa9, // OP_HASH160 + 0x14, // OP_DATA_20 + 0x39, 0xaa, 0x3d, 0x56, 0x9e, 0x06, 0xa1, 0xd7, + 0x92, 0x6d, 0xc4, 0xbe, 0x11, 0x93, 0xc9, 0x9b, + 0xf2, 0xeb, 0x9e, 0xe0, + 0x88, // OP_EQUALVERIFY + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 0, + }, + }, +} diff --git a/vendor/github.com/btcsuite/btcd/blocklogger.go b/vendor/github.com/btcsuite/btcd/blocklogger.go new file mode 100644 index 0000000000000000000000000000000000000000..1901a708bc9f17571b0368059650fa2edfd31153 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blocklogger.go @@ -0,0 +1,76 @@ +package main + +import ( + "sync" + "time" + + "github.com/btcsuite/btclog" + "github.com/btcsuite/btcutil" +) + +// blockProgressLogger provides periodic logging for other services in order +// to show users progress of certain "actions" involving some or all current +// blocks. Ex: syncing to best chain, indexing all blocks, etc. +type blockProgressLogger struct { + receivedLogBlocks int64 + receivedLogTx int64 + lastBlockLogTime time.Time + + subsystemLogger btclog.Logger + progressAction string + sync.Mutex +} + +// newBlockProgressLogger returns a new block progress logger. +// The progress message is templated as follows: +// {progressAction} {numProcessed} {blocks|block} in the last {timePeriod} +// ({numTxs}, height {lastBlockHeight}, {lastBlockTimeStamp}) +func newBlockProgressLogger(progressMessage string, logger btclog.Logger) *blockProgressLogger { + return &blockProgressLogger{ + lastBlockLogTime: time.Now(), + progressAction: progressMessage, + subsystemLogger: logger, + } +} + +// LogBlockHeight logs a new block height as an information message to show +// progress to the user. In order to prevent spam, it limits logging to one +// message every 10 seconds with duration and totals included. +func (b *blockProgressLogger) LogBlockHeight(block *btcutil.Block) { + b.Lock() + defer b.Unlock() + + b.receivedLogBlocks++ + b.receivedLogTx += int64(len(block.MsgBlock().Transactions)) + + now := time.Now() + duration := now.Sub(b.lastBlockLogTime) + if duration < time.Second*10 { + return + } + + // Truncate the duration to 10s of milliseconds. + durationMillis := int64(duration / time.Millisecond) + tDuration := 10 * time.Millisecond * time.Duration(durationMillis/10) + + // Log information about new block height. + blockStr := "blocks" + if b.receivedLogBlocks == 1 { + blockStr = "block" + } + txStr := "transactions" + if b.receivedLogTx == 1 { + txStr = "transaction" + } + b.subsystemLogger.Infof("%s %d %s in the last %s (%d %s, height %d, %s)", + b.progressAction, b.receivedLogBlocks, blockStr, tDuration, b.receivedLogTx, + txStr, block.Height(), block.MsgBlock().Header.Timestamp) + + b.receivedLogBlocks = 0 + b.receivedLogTx = 0 + b.lastBlockLogTime = now +} + +func (b *blockProgressLogger) SetLastLogTime(time time.Time) { + b.lastBlockLogTime = time +} diff --git a/vendor/github.com/btcsuite/btcd/blockmanager.go b/vendor/github.com/btcsuite/btcd/blockmanager.go new file mode 100644 index 0000000000000000000000000000000000000000..b8c4a55b8b209144b8eeb0ecce1c68a82360923a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/blockmanager.go @@ -0,0 +1,1554 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "container/list" + "net" + "os" + "path/filepath" + "sync" + "sync/atomic" + "time" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +const ( + chanBufferSize = 50 + + // minInFlightBlocks is the minimum number of blocks that should be + // in the request queue for headers-first mode before requesting + // more. + minInFlightBlocks = 10 + + // blockDbNamePrefix is the prefix for the block database name. The + // database type is appended to this value to form the full block + // database name. + blockDbNamePrefix = "blocks" + + // maxRejectedTxns is the maximum number of rejected transactions + // shas to store in memory. + maxRejectedTxns = 1000 + + // maxRequestedBlocks is the maximum number of requested block + // shas to store in memory. + maxRequestedBlocks = wire.MaxInvPerMsg + + // maxRequestedTxns is the maximum number of requested transactions + // shas to store in memory. + maxRequestedTxns = wire.MaxInvPerMsg +) + +// zeroHash is the zero value hash (all zeros). It is defined as a convenience. +var zeroHash wire.ShaHash + +// newPeerMsg signifies a newly connected peer to the block handler. +type newPeerMsg struct { + peer *serverPeer +} + +// blockMsg packages a bitcoin block message and the peer it came from together +// so the block handler has access to that information. +type blockMsg struct { + block *btcutil.Block + peer *serverPeer +} + +// invMsg packages a bitcoin inv message and the peer it came from together +// so the block handler has access to that information. +type invMsg struct { + inv *wire.MsgInv + peer *serverPeer +} + +// headersMsg packages a bitcoin headers message and the peer it came from +// together so the block handler has access to that information. +type headersMsg struct { + headers *wire.MsgHeaders + peer *serverPeer +} + +// donePeerMsg signifies a newly disconnected peer to the block handler. +type donePeerMsg struct { + peer *serverPeer +} + +// txMsg packages a bitcoin tx message and the peer it came from together +// so the block handler has access to that information. +type txMsg struct { + tx *btcutil.Tx + peer *serverPeer +} + +// getSyncPeerMsg is a message type to be sent across the message channel for +// retrieving the current sync peer. +type getSyncPeerMsg struct { + reply chan *serverPeer +} + +// processBlockResponse is a response sent to the reply channel of a +// processBlockMsg. +type processBlockResponse struct { + isOrphan bool + err error +} + +// processBlockMsg is a message type to be sent across the message channel +// for requested a block is processed. Note this call differs from blockMsg +// above in that blockMsg is intended for blocks that came from peers and have +// extra handling whereas this message essentially is just a concurrent safe +// way to call ProcessBlock on the internal block chain instance. +type processBlockMsg struct { + block *btcutil.Block + flags blockchain.BehaviorFlags + reply chan processBlockResponse +} + +// isCurrentMsg is a message type to be sent across the message channel for +// requesting whether or not the block manager believes it is synced with +// the currently connected peers. +type isCurrentMsg struct { + reply chan bool +} + +// pauseMsg is a message type to be sent across the message channel for +// pausing the block manager. This effectively provides the caller with +// exclusive access over the manager until a receive is performed on the +// unpause channel. +type pauseMsg struct { + unpause <-chan struct{} +} + +// headerNode is used as a node in a list of headers that are linked together +// between checkpoints. +type headerNode struct { + height int32 + sha *wire.ShaHash +} + +// chainState tracks the state of the best chain as blocks are inserted. This +// is done because btcchain is currently not safe for concurrent access and the +// block manager is typically quite busy processing block and inventory. +// Therefore, requesting this information from chain through the block manager +// would not be anywhere near as efficient as simply updating it as each block +// is inserted and protecting it with a mutex. +type chainState struct { + sync.Mutex + newestHash *wire.ShaHash + newestHeight int32 + pastMedianTime time.Time + pastMedianTimeErr error +} + +// Best returns the block hash and height known for the tip of the best known +// chain. +// +// This function is safe for concurrent access. +func (c *chainState) Best() (*wire.ShaHash, int32) { + c.Lock() + defer c.Unlock() + + return c.newestHash, c.newestHeight +} + +// blockManager provides a concurrency safe block manager for handling all +// incoming blocks. +type blockManager struct { + server *server + started int32 + shutdown int32 + chain *blockchain.BlockChain + rejectedTxns map[wire.ShaHash]struct{} + requestedTxns map[wire.ShaHash]struct{} + requestedBlocks map[wire.ShaHash]struct{} + progressLogger *blockProgressLogger + receivedLogBlocks int64 + receivedLogTx int64 + processingReqs bool + syncPeer *serverPeer + msgChan chan interface{} + chainState chainState + wg sync.WaitGroup + quit chan struct{} + + // The following fields are used for headers-first mode. + headersFirstMode bool + headerList *list.List + startHeader *list.Element + nextCheckpoint *chaincfg.Checkpoint +} + +// resetHeaderState sets the headers-first mode state to values appropriate for +// syncing from a new peer. +func (b *blockManager) resetHeaderState(newestHash *wire.ShaHash, newestHeight int32) { + b.headersFirstMode = false + b.headerList.Init() + b.startHeader = nil + + // When there is a next checkpoint, add an entry for the latest known + // block into the header pool. This allows the next downloaded header + // to prove it links to the chain properly. + if b.nextCheckpoint != nil { + node := headerNode{height: newestHeight, sha: newestHash} + b.headerList.PushBack(&node) + } +} + +// updateChainState updates the chain state associated with the block manager. +// This allows fast access to chain information since btcchain is currently not +// safe for concurrent access and the block manager is typically quite busy +// processing block and inventory. +func (b *blockManager) updateChainState(newestHash *wire.ShaHash, newestHeight int32) { + b.chainState.Lock() + defer b.chainState.Unlock() + + b.chainState.newestHash = newestHash + b.chainState.newestHeight = newestHeight + medianTime, err := b.chain.CalcPastMedianTime() + if err != nil { + b.chainState.pastMedianTimeErr = err + } else { + b.chainState.pastMedianTime = medianTime + } +} + +// findNextHeaderCheckpoint returns the next checkpoint after the passed height. +// It returns nil when there is not one either because the height is already +// later than the final checkpoint or some other reason such as disabled +// checkpoints. +func (b *blockManager) findNextHeaderCheckpoint(height int32) *chaincfg.Checkpoint { + // There is no next checkpoint if checkpoints are disabled or there are + // none for this current network. + if cfg.DisableCheckpoints { + return nil + } + checkpoints := b.server.chainParams.Checkpoints + if len(checkpoints) == 0 { + return nil + } + + // There is no next checkpoint if the height is already after the final + // checkpoint. + finalCheckpoint := &checkpoints[len(checkpoints)-1] + if height >= finalCheckpoint.Height { + return nil + } + + // Find the next checkpoint. + nextCheckpoint := finalCheckpoint + for i := len(checkpoints) - 2; i >= 0; i-- { + if height >= checkpoints[i].Height { + break + } + nextCheckpoint = &checkpoints[i] + } + return nextCheckpoint +} + +// startSync will choose the best peer among the available candidate peers to +// download/sync the blockchain from. When syncing is already running, it +// simply returns. It also examines the candidates for any which are no longer +// candidates and removes them as needed. +func (b *blockManager) startSync(peers *list.List) { + // Return now if we're already syncing. + if b.syncPeer != nil { + return + } + + best := b.chain.BestSnapshot() + var bestPeer *serverPeer + var enext *list.Element + for e := peers.Front(); e != nil; e = enext { + enext = e.Next() + sp := e.Value.(*serverPeer) + + // Remove sync candidate peers that are no longer candidates due + // to passing their latest known block. NOTE: The < is + // intentional as opposed to <=. While techcnically the peer + // doesn't have a later block when it's equal, it will likely + // have one soon so it is a reasonable choice. It also allows + // the case where both are at 0 such as during regression test. + if sp.LastBlock() < best.Height { + peers.Remove(e) + continue + } + + // TODO(davec): Use a better algorithm to choose the best peer. + // For now, just pick the first available candidate. + bestPeer = sp + } + + // Start syncing from the best peer if one was selected. + if bestPeer != nil { + // Clear the requestedBlocks if the sync peer changes, otherwise + // we may ignore blocks we need that the last sync peer failed + // to send. + b.requestedBlocks = make(map[wire.ShaHash]struct{}) + + locator, err := b.chain.LatestBlockLocator() + if err != nil { + bmgrLog.Errorf("Failed to get block locator for the "+ + "latest block: %v", err) + return + } + + bmgrLog.Infof("Syncing to block height %d from peer %v", + bestPeer.LastBlock(), bestPeer.Addr()) + + // When the current height is less than a known checkpoint we + // can use block headers to learn about which blocks comprise + // the chain up to the checkpoint and perform less validation + // for them. This is possible since each header contains the + // hash of the previous header and a merkle root. Therefore if + // we validate all of the received headers link together + // properly and the checkpoint hashes match, we can be sure the + // hashes for the blocks in between are accurate. Further, once + // the full blocks are downloaded, the merkle root is computed + // and compared against the value in the header which proves the + // full block hasn't been tampered with. + // + // Once we have passed the final checkpoint, or checkpoints are + // disabled, use standard inv messages learn about the blocks + // and fully validate them. Finally, regression test mode does + // not support the headers-first approach so do normal block + // downloads when in regression test mode. + if b.nextCheckpoint != nil && + best.Height < b.nextCheckpoint.Height && + !cfg.RegressionTest && !cfg.DisableCheckpoints { + + bestPeer.PushGetHeadersMsg(locator, b.nextCheckpoint.Hash) + b.headersFirstMode = true + bmgrLog.Infof("Downloading headers for blocks %d to "+ + "%d from peer %s", best.Height+1, + b.nextCheckpoint.Height, bestPeer.Addr()) + } else { + bestPeer.PushGetBlocksMsg(locator, &zeroHash) + } + b.syncPeer = bestPeer + } else { + bmgrLog.Warnf("No sync peer candidates available") + } +} + +// isSyncCandidate returns whether or not the peer is a candidate to consider +// syncing from. +func (b *blockManager) isSyncCandidate(sp *serverPeer) bool { + // Typically a peer is not a candidate for sync if it's not a full node, + // however regression test is special in that the regression tool is + // not a full node and still needs to be considered a sync candidate. + if cfg.RegressionTest { + // The peer is not a candidate if it's not coming from localhost + // or the hostname can't be determined for some reason. + host, _, err := net.SplitHostPort(sp.Addr()) + if err != nil { + return false + } + + if host != "127.0.0.1" && host != "localhost" { + return false + } + } else { + // The peer is not a candidate for sync if it's not a full node. + if sp.Services()&wire.SFNodeNetwork != wire.SFNodeNetwork { + return false + } + } + + // Candidate if all checks passed. + return true +} + +// handleNewPeerMsg deals with new peers that have signalled they may +// be considered as a sync peer (they have already successfully negotiated). It +// also starts syncing if needed. It is invoked from the syncHandler goroutine. +func (b *blockManager) handleNewPeerMsg(peers *list.List, sp *serverPeer) { + // Ignore if in the process of shutting down. + if atomic.LoadInt32(&b.shutdown) != 0 { + return + } + + bmgrLog.Infof("New valid peer %s (%s)", sp, sp.UserAgent()) + + // Ignore the peer if it's not a sync candidate. + if !b.isSyncCandidate(sp) { + return + } + + // Add the peer as a candidate to sync from. + peers.PushBack(sp) + + // Start syncing by choosing the best candidate if needed. + b.startSync(peers) +} + +// handleDonePeerMsg deals with peers that have signalled they are done. It +// removes the peer as a candidate for syncing and in the case where it was +// the current sync peer, attempts to select a new best peer to sync from. It +// is invoked from the syncHandler goroutine. +func (b *blockManager) handleDonePeerMsg(peers *list.List, sp *serverPeer) { + // Remove the peer from the list of candidate peers. + for e := peers.Front(); e != nil; e = e.Next() { + if e.Value == sp { + peers.Remove(e) + break + } + } + + bmgrLog.Infof("Lost peer %s", sp) + + // Remove requested transactions from the global map so that they will + // be fetched from elsewhere next time we get an inv. + for k := range sp.requestedTxns { + delete(b.requestedTxns, k) + } + + // Remove requested blocks from the global map so that they will be + // fetched from elsewhere next time we get an inv. + // TODO(oga) we could possibly here check which peers have these blocks + // and request them now to speed things up a little. + for k := range sp.requestedBlocks { + delete(b.requestedBlocks, k) + } + + // Attempt to find a new peer to sync from if the quitting peer is the + // sync peer. Also, reset the headers-first state if in headers-first + // mode so + if b.syncPeer != nil && b.syncPeer == sp { + b.syncPeer = nil + if b.headersFirstMode { + best := b.chain.BestSnapshot() + b.resetHeaderState(best.Hash, best.Height) + } + b.startSync(peers) + } +} + +// handleTxMsg handles transaction messages from all peers. +func (b *blockManager) handleTxMsg(tmsg *txMsg) { + // NOTE: BitcoinJ, and possibly other wallets, don't follow the spec of + // sending an inventory message and allowing the remote peer to decide + // whether or not they want to request the transaction via a getdata + // message. Unfortunately, the reference implementation permits + // unrequested data, so it has allowed wallets that don't follow the + // spec to proliferate. While this is not ideal, there is no check here + // to disconnect peers for sending unsolicited transactions to provide + // interoperability. + txHash := tmsg.tx.Sha() + + // Ignore transactions that we have already rejected. Do not + // send a reject message here because if the transaction was already + // rejected, the transaction was unsolicited. + if _, exists := b.rejectedTxns[*txHash]; exists { + bmgrLog.Debugf("Ignoring unsolicited previously rejected "+ + "transaction %v from %s", txHash, tmsg.peer) + return + } + + // Process the transaction to include validation, insertion in the + // memory pool, orphan handling, etc. + allowOrphans := cfg.MaxOrphanTxs > 0 + acceptedTxs, err := b.server.txMemPool.ProcessTransaction(tmsg.tx, + allowOrphans, true) + + // Remove transaction from request maps. Either the mempool/chain + // already knows about it and as such we shouldn't have any more + // instances of trying to fetch it, or we failed to insert and thus + // we'll retry next time we get an inv. + delete(tmsg.peer.requestedTxns, *txHash) + delete(b.requestedTxns, *txHash) + + if err != nil { + // Do not request this transaction again until a new block + // has been processed. + b.rejectedTxns[*txHash] = struct{}{} + b.limitMap(b.rejectedTxns, maxRejectedTxns) + + // When the error is a rule error, it means the transaction was + // simply rejected as opposed to something actually going wrong, + // so log it as such. Otherwise, something really did go wrong, + // so log it as an actual error. + if _, ok := err.(RuleError); ok { + bmgrLog.Debugf("Rejected transaction %v from %s: %v", + txHash, tmsg.peer, err) + } else { + bmgrLog.Errorf("Failed to process transaction %v: %v", + txHash, err) + } + + // Convert the error into an appropriate reject message and + // send it. + code, reason := errToRejectErr(err) + tmsg.peer.PushRejectMsg(wire.CmdTx, code, reason, txHash, + false) + return + } + + b.server.AnnounceNewTransactions(acceptedTxs) +} + +// current returns true if we believe we are synced with our peers, false if we +// still have blocks to check +func (b *blockManager) current() bool { + if !b.chain.IsCurrent(b.server.timeSource) { + return false + } + + // if blockChain thinks we are current and we have no syncPeer it + // is probably right. + if b.syncPeer == nil { + return true + } + + // No matter what chain thinks, if we are below the block we are syncing + // to we are not current. + if b.chain.BestSnapshot().Height < b.syncPeer.LastBlock() { + return false + } + return true +} + +// handleBlockMsg handles block messages from all peers. +func (b *blockManager) handleBlockMsg(bmsg *blockMsg) { + // If we didn't ask for this block then the peer is misbehaving. + blockSha := bmsg.block.Sha() + if _, exists := bmsg.peer.requestedBlocks[*blockSha]; !exists { + // The regression test intentionally sends some blocks twice + // to test duplicate block insertion fails. Don't disconnect + // the peer or ignore the block when we're in regression test + // mode in this case so the chain code is actually fed the + // duplicate blocks. + if !cfg.RegressionTest { + bmgrLog.Warnf("Got unrequested block %v from %s -- "+ + "disconnecting", blockSha, bmsg.peer.Addr()) + bmsg.peer.Disconnect() + return + } + } + + // When in headers-first mode, if the block matches the hash of the + // first header in the list of headers that are being fetched, it's + // eligible for less validation since the headers have already been + // verified to link together and are valid up to the next checkpoint. + // Also, remove the list entry for all blocks except the checkpoint + // since it is needed to verify the next round of headers links + // properly. + isCheckpointBlock := false + behaviorFlags := blockchain.BFNone + if b.headersFirstMode { + firstNodeEl := b.headerList.Front() + if firstNodeEl != nil { + firstNode := firstNodeEl.Value.(*headerNode) + if blockSha.IsEqual(firstNode.sha) { + behaviorFlags |= blockchain.BFFastAdd + if firstNode.sha.IsEqual(b.nextCheckpoint.Hash) { + isCheckpointBlock = true + } else { + b.headerList.Remove(firstNodeEl) + } + } + } + } + + // Remove block from request maps. Either chain will know about it and + // so we shouldn't have any more instances of trying to fetch it, or we + // will fail the insert and thus we'll retry next time we get an inv. + delete(bmsg.peer.requestedBlocks, *blockSha) + delete(b.requestedBlocks, *blockSha) + + // Process the block to include validation, best chain selection, orphan + // handling, etc. + isOrphan, err := b.chain.ProcessBlock(bmsg.block, + b.server.timeSource, behaviorFlags) + if err != nil { + // When the error is a rule error, it means the block was simply + // rejected as opposed to something actually going wrong, so log + // it as such. Otherwise, something really did go wrong, so log + // it as an actual error. + if _, ok := err.(blockchain.RuleError); ok { + bmgrLog.Infof("Rejected block %v from %s: %v", blockSha, + bmsg.peer, err) + } else { + bmgrLog.Errorf("Failed to process block %v: %v", + blockSha, err) + } + if dbErr, ok := err.(database.Error); ok && dbErr.ErrorCode == + database.ErrCorruption { + panic(dbErr) + } + + // Convert the error into an appropriate reject message and + // send it. + code, reason := errToRejectErr(err) + bmsg.peer.PushRejectMsg(wire.CmdBlock, code, reason, + blockSha, false) + return + } + + // Meta-data about the new block this peer is reporting. We use this + // below to update this peer's lastest block height and the heights of + // other peers based on their last announced block sha. This allows us + // to dynamically update the block heights of peers, avoiding stale heights + // when looking for a new sync peer. Upon acceptance of a block or + // recognition of an orphan, we also use this information to update + // the block heights over other peers who's invs may have been ignored + // if we are actively syncing while the chain is not yet current or + // who may have lost the lock announcment race. + var heightUpdate int32 + var blkShaUpdate *wire.ShaHash + + // Request the parents for the orphan block from the peer that sent it. + if isOrphan { + // We've just received an orphan block from a peer. In order + // to update the height of the peer, we try to extract the + // block height from the scriptSig of the coinbase transaction. + // Extraction is only attempted if the block's version is + // high enough (ver 2+). + header := &bmsg.block.MsgBlock().Header + if blockchain.ShouldHaveSerializedBlockHeight(header) { + coinbaseTx := bmsg.block.Transactions()[0] + cbHeight, err := blockchain.ExtractCoinbaseHeight(coinbaseTx) + if err != nil { + bmgrLog.Warnf("Unable to extract height from "+ + "coinbase tx: %v", err) + } else { + bmgrLog.Debugf("Extracted height of %v from "+ + "orphan block", cbHeight) + heightUpdate = int32(cbHeight) + blkShaUpdate = blockSha + } + } + + orphanRoot := b.chain.GetOrphanRoot(blockSha) + locator, err := b.chain.LatestBlockLocator() + if err != nil { + bmgrLog.Warnf("Failed to get block locator for the "+ + "latest block: %v", err) + } else { + bmsg.peer.PushGetBlocksMsg(locator, orphanRoot) + } + } else { + // When the block is not an orphan, log information about it and + // update the chain state. + b.progressLogger.LogBlockHeight(bmsg.block) + + // Query the chain for the latest best block since the block + // that was processed could be on a side chain or have caused + // a reorg. + best := b.chain.BestSnapshot() + b.updateChainState(best.Hash, best.Height) + + // Update this peer's latest block height, for future + // potential sync node candidacy. + heightUpdate = best.Height + blkShaUpdate = best.Hash + + // Clear the rejected transactions. + b.rejectedTxns = make(map[wire.ShaHash]struct{}) + + // Allow any clients performing long polling via the + // getblocktemplate RPC to be notified when the new block causes + // their old block template to become stale. + rpcServer := b.server.rpcServer + if rpcServer != nil { + rpcServer.gbtWorkState.NotifyBlockConnected(blockSha) + } + } + + // Update the block height for this peer. But only send a message to + // the server for updating peer heights if this is an orphan or our + // chain is "current". This avoids sending a spammy amount of messages + // if we're syncing the chain from scratch. + if blkShaUpdate != nil && heightUpdate != 0 { + bmsg.peer.UpdateLastBlockHeight(heightUpdate) + if isOrphan || b.current() { + go b.server.UpdatePeerHeights(blkShaUpdate, int32(heightUpdate), bmsg.peer) + } + } + + // Nothing more to do if we aren't in headers-first mode. + if !b.headersFirstMode { + return + } + + // This is headers-first mode, so if the block is not a checkpoint + // request more blocks using the header list when the request queue is + // getting short. + if !isCheckpointBlock { + if b.startHeader != nil && + len(bmsg.peer.requestedBlocks) < minInFlightBlocks { + b.fetchHeaderBlocks() + } + return + } + + // This is headers-first mode and the block is a checkpoint. When + // there is a next checkpoint, get the next round of headers by asking + // for headers starting from the block after this one up to the next + // checkpoint. + prevHeight := b.nextCheckpoint.Height + prevHash := b.nextCheckpoint.Hash + b.nextCheckpoint = b.findNextHeaderCheckpoint(prevHeight) + if b.nextCheckpoint != nil { + locator := blockchain.BlockLocator([]*wire.ShaHash{prevHash}) + err := bmsg.peer.PushGetHeadersMsg(locator, b.nextCheckpoint.Hash) + if err != nil { + bmgrLog.Warnf("Failed to send getheaders message to "+ + "peer %s: %v", bmsg.peer.Addr(), err) + return + } + bmgrLog.Infof("Downloading headers for blocks %d to %d from "+ + "peer %s", prevHeight+1, b.nextCheckpoint.Height, + b.syncPeer.Addr()) + return + } + + // This is headers-first mode, the block is a checkpoint, and there are + // no more checkpoints, so switch to normal mode by requesting blocks + // from the block after this one up to the end of the chain (zero hash). + b.headersFirstMode = false + b.headerList.Init() + bmgrLog.Infof("Reached the final checkpoint -- switching to normal mode") + locator := blockchain.BlockLocator([]*wire.ShaHash{blockSha}) + err = bmsg.peer.PushGetBlocksMsg(locator, &zeroHash) + if err != nil { + bmgrLog.Warnf("Failed to send getblocks message to peer %s: %v", + bmsg.peer.Addr(), err) + return + } +} + +// fetchHeaderBlocks creates and sends a request to the syncPeer for the next +// list of blocks to be downloaded based on the current list of headers. +func (b *blockManager) fetchHeaderBlocks() { + // Nothing to do if there is no start header. + if b.startHeader == nil { + bmgrLog.Warnf("fetchHeaderBlocks called with no start header") + return + } + + // Build up a getdata request for the list of blocks the headers + // describe. The size hint will be limited to wire.MaxInvPerMsg by + // the function, so no need to double check it here. + gdmsg := wire.NewMsgGetDataSizeHint(uint(b.headerList.Len())) + numRequested := 0 + for e := b.startHeader; e != nil; e = e.Next() { + node, ok := e.Value.(*headerNode) + if !ok { + bmgrLog.Warn("Header list node type is not a headerNode") + continue + } + + iv := wire.NewInvVect(wire.InvTypeBlock, node.sha) + haveInv, err := b.haveInventory(iv) + if err != nil { + bmgrLog.Warnf("Unexpected failure when checking for "+ + "existing inventory during header block "+ + "fetch: %v", err) + } + if !haveInv { + b.requestedBlocks[*node.sha] = struct{}{} + b.syncPeer.requestedBlocks[*node.sha] = struct{}{} + gdmsg.AddInvVect(iv) + numRequested++ + } + b.startHeader = e.Next() + if numRequested >= wire.MaxInvPerMsg { + break + } + } + if len(gdmsg.InvList) > 0 { + b.syncPeer.QueueMessage(gdmsg, nil) + } +} + +// handleHeadersMsghandles headers messages from all peers. +func (b *blockManager) handleHeadersMsg(hmsg *headersMsg) { + // The remote peer is misbehaving if we didn't request headers. + msg := hmsg.headers + numHeaders := len(msg.Headers) + if !b.headersFirstMode { + bmgrLog.Warnf("Got %d unrequested headers from %s -- "+ + "disconnecting", numHeaders, hmsg.peer.Addr()) + hmsg.peer.Disconnect() + return + } + + // Nothing to do for an empty headers message. + if numHeaders == 0 { + return + } + + // Process all of the received headers ensuring each one connects to the + // previous and that checkpoints match. + receivedCheckpoint := false + var finalHash *wire.ShaHash + for _, blockHeader := range msg.Headers { + blockHash := blockHeader.BlockSha() + finalHash = &blockHash + + // Ensure there is a previous header to compare against. + prevNodeEl := b.headerList.Back() + if prevNodeEl == nil { + bmgrLog.Warnf("Header list does not contain a previous" + + "element as expected -- disconnecting peer") + hmsg.peer.Disconnect() + return + } + + // Ensure the header properly connects to the previous one and + // add it to the list of headers. + node := headerNode{sha: &blockHash} + prevNode := prevNodeEl.Value.(*headerNode) + if prevNode.sha.IsEqual(&blockHeader.PrevBlock) { + node.height = prevNode.height + 1 + e := b.headerList.PushBack(&node) + if b.startHeader == nil { + b.startHeader = e + } + } else { + bmgrLog.Warnf("Received block header that does not "+ + "properly connect to the chain from peer %s "+ + "-- disconnecting", hmsg.peer.Addr()) + hmsg.peer.Disconnect() + return + } + + // Verify the header at the next checkpoint height matches. + if node.height == b.nextCheckpoint.Height { + if node.sha.IsEqual(b.nextCheckpoint.Hash) { + receivedCheckpoint = true + bmgrLog.Infof("Verified downloaded block "+ + "header against checkpoint at height "+ + "%d/hash %s", node.height, node.sha) + } else { + bmgrLog.Warnf("Block header at height %d/hash "+ + "%s from peer %s does NOT match "+ + "expected checkpoint hash of %s -- "+ + "disconnecting", node.height, + node.sha, hmsg.peer.Addr(), + b.nextCheckpoint.Hash) + hmsg.peer.Disconnect() + return + } + break + } + } + + // When this header is a checkpoint, switch to fetching the blocks for + // all of the headers since the last checkpoint. + if receivedCheckpoint { + // Since the first entry of the list is always the final block + // that is already in the database and is only used to ensure + // the next header links properly, it must be removed before + // fetching the blocks. + b.headerList.Remove(b.headerList.Front()) + bmgrLog.Infof("Received %v block headers: Fetching blocks", + b.headerList.Len()) + b.progressLogger.SetLastLogTime(time.Now()) + b.fetchHeaderBlocks() + return + } + + // This header is not a checkpoint, so request the next batch of + // headers starting from the latest known header and ending with the + // next checkpoint. + locator := blockchain.BlockLocator([]*wire.ShaHash{finalHash}) + err := hmsg.peer.PushGetHeadersMsg(locator, b.nextCheckpoint.Hash) + if err != nil { + bmgrLog.Warnf("Failed to send getheaders message to "+ + "peer %s: %v", hmsg.peer.Addr(), err) + return + } +} + +// haveInventory returns whether or not the inventory represented by the passed +// inventory vector is known. This includes checking all of the various places +// inventory can be when it is in different states such as blocks that are part +// of the main chain, on a side chain, in the orphan pool, and transactions that +// are in the memory pool (either the main pool or orphan pool). +func (b *blockManager) haveInventory(invVect *wire.InvVect) (bool, error) { + switch invVect.Type { + case wire.InvTypeBlock: + // Ask chain if the block is known to it in any form (main + // chain, side chain, or orphan). + return b.chain.HaveBlock(&invVect.Hash) + + case wire.InvTypeTx: + // Ask the transaction memory pool if the transaction is known + // to it in any form (main pool or orphan). + if b.server.txMemPool.HaveTransaction(&invVect.Hash) { + return true, nil + } + + // Check if the transaction exists from the point of view of the + // end of the main chain. + entry, err := b.chain.FetchUtxoEntry(&invVect.Hash) + if err != nil { + return false, err + } + return entry != nil && !entry.IsFullySpent(), nil + } + + // The requested inventory is is an unsupported type, so just claim + // it is known to avoid requesting it. + return true, nil +} + +// handleInvMsg handles inv messages from all peers. +// We examine the inventory advertised by the remote peer and act accordingly. +func (b *blockManager) handleInvMsg(imsg *invMsg) { + // Attempt to find the final block in the inventory list. There may + // not be one. + lastBlock := -1 + invVects := imsg.inv.InvList + for i := len(invVects) - 1; i >= 0; i-- { + if invVects[i].Type == wire.InvTypeBlock { + lastBlock = i + break + } + } + + // If this inv contains a block announcement, and this isn't coming from + // our current sync peer or we're current, then update the last + // announced block for this peer. We'll use this information later to + // update the heights of peers based on blocks we've accepted that they + // previously announced. + if lastBlock != -1 && (imsg.peer != b.syncPeer || b.current()) { + imsg.peer.UpdateLastAnnouncedBlock(&invVects[lastBlock].Hash) + } + + // Ignore invs from peers that aren't the sync if we are not current. + // Helps prevent fetching a mass of orphans. + if imsg.peer != b.syncPeer && !b.current() { + return + } + + // If our chain is current and a peer announces a block we already + // know of, then update their current block height. + if lastBlock != -1 && b.current() { + blkHeight, err := b.chain.BlockHeightByHash(&invVects[lastBlock].Hash) + if err == nil { + imsg.peer.UpdateLastBlockHeight(int32(blkHeight)) + } + } + + // Request the advertised inventory if we don't already have it. Also, + // request parent blocks of orphans if we receive one we already have. + // Finally, attempt to detect potential stalls due to long side chains + // we already have and request more blocks to prevent them. + for i, iv := range invVects { + // Ignore unsupported inventory types. + if iv.Type != wire.InvTypeBlock && iv.Type != wire.InvTypeTx { + continue + } + + // Add the inventory to the cache of known inventory + // for the peer. + imsg.peer.AddKnownInventory(iv) + + // Ignore inventory when we're in headers-first mode. + if b.headersFirstMode { + continue + } + + // Request the inventory if we don't already have it. + haveInv, err := b.haveInventory(iv) + if err != nil { + bmgrLog.Warnf("Unexpected failure when checking for "+ + "existing inventory during inv message "+ + "processing: %v", err) + continue + } + if !haveInv { + if iv.Type == wire.InvTypeTx { + // Skip the transaction if it has already been + // rejected. + if _, exists := b.rejectedTxns[iv.Hash]; exists { + continue + } + } + + // Add it to the request queue. + imsg.peer.requestQueue = append(imsg.peer.requestQueue, iv) + continue + } + + if iv.Type == wire.InvTypeBlock { + // The block is an orphan block that we already have. + // When the existing orphan was processed, it requested + // the missing parent blocks. When this scenario + // happens, it means there were more blocks missing + // than are allowed into a single inventory message. As + // a result, once this peer requested the final + // advertised block, the remote peer noticed and is now + // resending the orphan block as an available block + // to signal there are more missing blocks that need to + // be requested. + if b.chain.IsKnownOrphan(&iv.Hash) { + // Request blocks starting at the latest known + // up to the root of the orphan that just came + // in. + orphanRoot := b.chain.GetOrphanRoot(&iv.Hash) + locator, err := b.chain.LatestBlockLocator() + if err != nil { + bmgrLog.Errorf("PEER: Failed to get block "+ + "locator for the latest block: "+ + "%v", err) + continue + } + imsg.peer.PushGetBlocksMsg(locator, orphanRoot) + continue + } + + // We already have the final block advertised by this + // inventory message, so force a request for more. This + // should only happen if we're on a really long side + // chain. + if i == lastBlock { + // Request blocks after this one up to the + // final one the remote peer knows about (zero + // stop hash). + locator := b.chain.BlockLocatorFromHash(&iv.Hash) + imsg.peer.PushGetBlocksMsg(locator, &zeroHash) + } + } + } + + // Request as much as possible at once. Anything that won't fit into + // the request will be requested on the next inv message. + numRequested := 0 + gdmsg := wire.NewMsgGetData() + requestQueue := imsg.peer.requestQueue + for len(requestQueue) != 0 { + iv := requestQueue[0] + requestQueue[0] = nil + requestQueue = requestQueue[1:] + + switch iv.Type { + case wire.InvTypeBlock: + // Request the block if there is not already a pending + // request. + if _, exists := b.requestedBlocks[iv.Hash]; !exists { + b.requestedBlocks[iv.Hash] = struct{}{} + b.limitMap(b.requestedBlocks, maxRequestedBlocks) + imsg.peer.requestedBlocks[iv.Hash] = struct{}{} + gdmsg.AddInvVect(iv) + numRequested++ + } + + case wire.InvTypeTx: + // Request the transaction if there is not already a + // pending request. + if _, exists := b.requestedTxns[iv.Hash]; !exists { + b.requestedTxns[iv.Hash] = struct{}{} + b.limitMap(b.requestedTxns, maxRequestedTxns) + imsg.peer.requestedTxns[iv.Hash] = struct{}{} + gdmsg.AddInvVect(iv) + numRequested++ + } + } + + if numRequested >= wire.MaxInvPerMsg { + break + } + } + imsg.peer.requestQueue = requestQueue + if len(gdmsg.InvList) > 0 { + imsg.peer.QueueMessage(gdmsg, nil) + } +} + +// limitMap is a helper function for maps that require a maximum limit by +// evicting a random transaction if adding a new value would cause it to +// overflow the maximum allowed. +func (b *blockManager) limitMap(m map[wire.ShaHash]struct{}, limit int) { + if len(m)+1 > limit { + // Remove a random entry from the map. For most compilers, Go's + // range statement iterates starting at a random item although + // that is not 100% guaranteed by the spec. The iteration order + // is not important here because an adversary would have to be + // able to pull off preimage attacks on the hashing function in + // order to target eviction of specific entries anyways. + for txHash := range m { + delete(m, txHash) + return + } + } +} + +// blockHandler is the main handler for the block manager. It must be run +// as a goroutine. It processes block and inv messages in a separate goroutine +// from the peer handlers so the block (MsgBlock) messages are handled by a +// single thread without needing to lock memory data structures. This is +// important because the block manager controls which blocks are needed and how +// the fetching should proceed. +func (b *blockManager) blockHandler() { + candidatePeers := list.New() +out: + for { + select { + case m := <-b.msgChan: + switch msg := m.(type) { + case *newPeerMsg: + b.handleNewPeerMsg(candidatePeers, msg.peer) + + case *txMsg: + b.handleTxMsg(msg) + msg.peer.txProcessed <- struct{}{} + + case *blockMsg: + b.handleBlockMsg(msg) + msg.peer.blockProcessed <- struct{}{} + + case *invMsg: + b.handleInvMsg(msg) + + case *headersMsg: + b.handleHeadersMsg(msg) + + case *donePeerMsg: + b.handleDonePeerMsg(candidatePeers, msg.peer) + + case getSyncPeerMsg: + msg.reply <- b.syncPeer + + case processBlockMsg: + isOrphan, err := b.chain.ProcessBlock(msg.block, + b.server.timeSource, msg.flags) + if err != nil { + msg.reply <- processBlockResponse{ + isOrphan: false, + err: err, + } + } + + // Query the chain for the latest best block + // since the block that was processed could be + // on a side chain or have caused a reorg. + best := b.chain.BestSnapshot() + b.updateChainState(best.Hash, best.Height) + + // Allow any clients performing long polling via the + // getblocktemplate RPC to be notified when the new block causes + // their old block template to become stale. + rpcServer := b.server.rpcServer + if rpcServer != nil { + rpcServer.gbtWorkState.NotifyBlockConnected(msg.block.Sha()) + } + + msg.reply <- processBlockResponse{ + isOrphan: isOrphan, + err: nil, + } + + case isCurrentMsg: + msg.reply <- b.current() + + case pauseMsg: + // Wait until the sender unpauses the manager. + <-msg.unpause + + default: + bmgrLog.Warnf("Invalid message type in block "+ + "handler: %T", msg) + } + + case <-b.quit: + break out + } + } + + b.wg.Done() + bmgrLog.Trace("Block handler done") +} + +// handleNotifyMsg handles notifications from blockchain. It does things such +// as request orphan block parents and relay accepted blocks to connected peers. +func (b *blockManager) handleNotifyMsg(notification *blockchain.Notification) { + switch notification.Type { + // A block has been accepted into the block chain. Relay it to other + // peers. + case blockchain.NTBlockAccepted: + // Don't relay if we are not current. Other peers that are + // current should already know about it. + if !b.current() { + return + } + + block, ok := notification.Data.(*btcutil.Block) + if !ok { + bmgrLog.Warnf("Chain accepted notification is not a block.") + break + } + + // Generate the inventory vector and relay it. + iv := wire.NewInvVect(wire.InvTypeBlock, block.Sha()) + b.server.RelayInventory(iv, nil) + + // A block has been connected to the main block chain. + case blockchain.NTBlockConnected: + block, ok := notification.Data.(*btcutil.Block) + if !ok { + bmgrLog.Warnf("Chain connected notification is not a block.") + break + } + + // Remove all of the transactions (except the coinbase) in the + // connected block from the transaction pool. Secondly, remove any + // transactions which are now double spends as a result of these + // new transactions. Finally, remove any transaction that is + // no longer an orphan. Transactions which depend on a confirmed + // transaction are NOT removed recursively because they are still + // valid. + for _, tx := range block.Transactions()[1:] { + b.server.txMemPool.RemoveTransaction(tx, false) + b.server.txMemPool.RemoveDoubleSpends(tx) + b.server.txMemPool.RemoveOrphan(tx.Sha()) + acceptedTxs := b.server.txMemPool.ProcessOrphans(tx.Sha()) + b.server.AnnounceNewTransactions(acceptedTxs) + } + + if r := b.server.rpcServer; r != nil { + // Now that this block is in the blockchain we can mark + // all the transactions (except the coinbase) as no + // longer needing rebroadcasting. + for _, tx := range block.Transactions()[1:] { + iv := wire.NewInvVect(wire.InvTypeTx, tx.Sha()) + b.server.RemoveRebroadcastInventory(iv) + } + + // Notify registered websocket clients of incoming block. + r.ntfnMgr.NotifyBlockConnected(block) + } + + // A block has been disconnected from the main block chain. + case blockchain.NTBlockDisconnected: + block, ok := notification.Data.(*btcutil.Block) + if !ok { + bmgrLog.Warnf("Chain disconnected notification is not a block.") + break + } + + // Reinsert all of the transactions (except the coinbase) into + // the transaction pool. + for _, tx := range block.Transactions()[1:] { + _, err := b.server.txMemPool.MaybeAcceptTransaction(tx, + false, false) + if err != nil { + // Remove the transaction and all transactions + // that depend on it if it wasn't accepted into + // the transaction pool. + b.server.txMemPool.RemoveTransaction(tx, true) + } + } + + // Notify registered websocket clients. + if r := b.server.rpcServer; r != nil { + r.ntfnMgr.NotifyBlockDisconnected(block) + } + } +} + +// NewPeer informs the block manager of a newly active peer. +func (b *blockManager) NewPeer(sp *serverPeer) { + // Ignore if we are shutting down. + if atomic.LoadInt32(&b.shutdown) != 0 { + return + } + b.msgChan <- &newPeerMsg{peer: sp} +} + +// QueueTx adds the passed transaction message and peer to the block handling +// queue. +func (b *blockManager) QueueTx(tx *btcutil.Tx, sp *serverPeer) { + // Don't accept more transactions if we're shutting down. + if atomic.LoadInt32(&b.shutdown) != 0 { + sp.txProcessed <- struct{}{} + return + } + + b.msgChan <- &txMsg{tx: tx, peer: sp} +} + +// QueueBlock adds the passed block message and peer to the block handling queue. +func (b *blockManager) QueueBlock(block *btcutil.Block, sp *serverPeer) { + // Don't accept more blocks if we're shutting down. + if atomic.LoadInt32(&b.shutdown) != 0 { + sp.blockProcessed <- struct{}{} + return + } + + b.msgChan <- &blockMsg{block: block, peer: sp} +} + +// QueueInv adds the passed inv message and peer to the block handling queue. +func (b *blockManager) QueueInv(inv *wire.MsgInv, sp *serverPeer) { + // No channel handling here because peers do not need to block on inv + // messages. + if atomic.LoadInt32(&b.shutdown) != 0 { + return + } + + b.msgChan <- &invMsg{inv: inv, peer: sp} +} + +// QueueHeaders adds the passed headers message and peer to the block handling +// queue. +func (b *blockManager) QueueHeaders(headers *wire.MsgHeaders, sp *serverPeer) { + // No channel handling here because peers do not need to block on + // headers messages. + if atomic.LoadInt32(&b.shutdown) != 0 { + return + } + + b.msgChan <- &headersMsg{headers: headers, peer: sp} +} + +// DonePeer informs the blockmanager that a peer has disconnected. +func (b *blockManager) DonePeer(sp *serverPeer) { + // Ignore if we are shutting down. + if atomic.LoadInt32(&b.shutdown) != 0 { + return + } + + b.msgChan <- &donePeerMsg{peer: sp} +} + +// Start begins the core block handler which processes block and inv messages. +func (b *blockManager) Start() { + // Already started? + if atomic.AddInt32(&b.started, 1) != 1 { + return + } + + bmgrLog.Trace("Starting block manager") + b.wg.Add(1) + go b.blockHandler() +} + +// Stop gracefully shuts down the block manager by stopping all asynchronous +// handlers and waiting for them to finish. +func (b *blockManager) Stop() error { + if atomic.AddInt32(&b.shutdown, 1) != 1 { + bmgrLog.Warnf("Block manager is already in the process of " + + "shutting down") + return nil + } + + bmgrLog.Infof("Block manager shutting down") + close(b.quit) + b.wg.Wait() + return nil +} + +// SyncPeer returns the current sync peer. +func (b *blockManager) SyncPeer() *serverPeer { + reply := make(chan *serverPeer) + b.msgChan <- getSyncPeerMsg{reply: reply} + return <-reply +} + +// ProcessBlock makes use of ProcessBlock on an internal instance of a block +// chain. It is funneled through the block manager since btcchain is not safe +// for concurrent access. +func (b *blockManager) ProcessBlock(block *btcutil.Block, flags blockchain.BehaviorFlags) (bool, error) { + reply := make(chan processBlockResponse, 1) + b.msgChan <- processBlockMsg{block: block, flags: flags, reply: reply} + response := <-reply + return response.isOrphan, response.err +} + +// IsCurrent returns whether or not the block manager believes it is synced with +// the connected peers. +func (b *blockManager) IsCurrent() bool { + reply := make(chan bool) + b.msgChan <- isCurrentMsg{reply: reply} + return <-reply +} + +// Pause pauses the block manager until the returned channel is closed. +// +// Note that while paused, all peer and block processing is halted. The +// message sender should avoid pausing the block manager for long durations. +func (b *blockManager) Pause() chan<- struct{} { + c := make(chan struct{}) + b.msgChan <- pauseMsg{c} + return c +} + +// newBlockManager returns a new bitcoin block manager. +// Use Start to begin processing asynchronous block and inv updates. +func newBlockManager(s *server, indexManager blockchain.IndexManager) (*blockManager, error) { + bm := blockManager{ + server: s, + rejectedTxns: make(map[wire.ShaHash]struct{}), + requestedTxns: make(map[wire.ShaHash]struct{}), + requestedBlocks: make(map[wire.ShaHash]struct{}), + progressLogger: newBlockProgressLogger("Processed", bmgrLog), + msgChan: make(chan interface{}, cfg.MaxPeers*3), + headerList: list.New(), + quit: make(chan struct{}), + } + + // Create a new block chain instance with the appropriate configuration. + var err error + bm.chain, err = blockchain.New(&blockchain.Config{ + DB: s.db, + ChainParams: s.chainParams, + Notifications: bm.handleNotifyMsg, + SigCache: s.sigCache, + IndexManager: indexManager, + }) + if err != nil { + return nil, err + } + best := bm.chain.BestSnapshot() + bm.chain.DisableCheckpoints(cfg.DisableCheckpoints) + if !cfg.DisableCheckpoints { + // Initialize the next checkpoint based on the current height. + bm.nextCheckpoint = bm.findNextHeaderCheckpoint(best.Height) + if bm.nextCheckpoint != nil { + bm.resetHeaderState(best.Hash, best.Height) + } + } else { + bmgrLog.Info("Checkpoints are disabled") + } + + // Initialize the chain state now that the initial block node index has + // been generated. + bm.updateChainState(best.Hash, best.Height) + + return &bm, nil +} + +// removeRegressionDB removes the existing regression test database if running +// in regression test mode and it already exists. +func removeRegressionDB(dbPath string) error { + // Don't do anything if not in regression test mode. + if !cfg.RegressionTest { + return nil + } + + // Remove the old regression test database if it already exists. + fi, err := os.Stat(dbPath) + if err == nil { + btcdLog.Infof("Removing regression test database from '%s'", dbPath) + if fi.IsDir() { + err := os.RemoveAll(dbPath) + if err != nil { + return err + } + } else { + err := os.Remove(dbPath) + if err != nil { + return err + } + } + } + + return nil +} + +// dbPath returns the path to the block database given a database type. +func blockDbPath(dbType string) string { + // The database name is based on the database type. + dbName := blockDbNamePrefix + "_" + dbType + if dbType == "sqlite" { + dbName = dbName + ".db" + } + dbPath := filepath.Join(cfg.DataDir, dbName) + return dbPath +} + +// warnMultipeDBs shows a warning if multiple block database types are detected. +// This is not a situation most users want. It is handy for development however +// to support multiple side-by-side databases. +func warnMultipeDBs() { + // This is intentionally not using the known db types which depend + // on the database types compiled into the binary since we want to + // detect legacy db types as well. + dbTypes := []string{"ffldb", "leveldb", "sqlite"} + duplicateDbPaths := make([]string, 0, len(dbTypes)-1) + for _, dbType := range dbTypes { + if dbType == cfg.DbType { + continue + } + + // Store db path as a duplicate db if it exists. + dbPath := blockDbPath(dbType) + if fileExists(dbPath) { + duplicateDbPaths = append(duplicateDbPaths, dbPath) + } + } + + // Warn if there are extra databases. + if len(duplicateDbPaths) > 0 { + selectedDbPath := blockDbPath(cfg.DbType) + btcdLog.Warnf("WARNING: There are multiple block chain databases "+ + "using different database types.\nYou probably don't "+ + "want to waste disk space by having more than one.\n"+ + "Your current database is located at [%v].\nThe "+ + "additional database is located at %v", selectedDbPath, + duplicateDbPaths) + } +} + +// loadBlockDB loads (or creates when needed) the block database taking into +// account the selected database backend and returns a handle to it. It also +// contains additional logic such warning the user if there are multiple +// databases which consume space on the file system and ensuring the regression +// test database is clean when in regression test mode. +func loadBlockDB() (database.DB, error) { + // The memdb backend does not have a file path associated with it, so + // handle it uniquely. We also don't want to worry about the multiple + // database type warnings when running with the memory database. + if cfg.DbType == "memdb" { + btcdLog.Infof("Creating block database in memory.") + db, err := database.Create(cfg.DbType) + if err != nil { + return nil, err + } + return db, nil + } + + warnMultipeDBs() + + // The database name is based on the database type. + dbPath := blockDbPath(cfg.DbType) + + // The regression test is special in that it needs a clean database for + // each run, so remove it now if it already exists. + removeRegressionDB(dbPath) + + btcdLog.Infof("Loading block database from '%s'", dbPath) + db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net) + if err != nil { + // Return the error if it's not because the database doesn't + // exist. + if dbErr, ok := err.(database.Error); !ok || dbErr.ErrorCode != + database.ErrDbDoesNotExist { + + return nil, err + } + + // Create the db if it does not exist. + err = os.MkdirAll(cfg.DataDir, 0700) + if err != nil { + return nil, err + } + db, err = database.Create(cfg.DbType, dbPath, activeNetParams.Net) + if err != nil { + return nil, err + } + } + + btcdLog.Info("Block database loaded") + return db, nil +} diff --git a/vendor/github.com/btcsuite/btcd/btcd.go b/vendor/github.com/btcsuite/btcd/btcd.go new file mode 100644 index 0000000000000000000000000000000000000000..e1fdf04bcd2b9cddafb83f8cb3af1577192300b0 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcd.go @@ -0,0 +1,177 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "net" + "net/http" + _ "net/http/pprof" + "os" + "runtime" + "runtime/pprof" + + "github.com/btcsuite/btcd/blockchain/indexers" + "github.com/btcsuite/btcd/limits" +) + +var ( + cfg *config + shutdownChannel = make(chan struct{}) +) + +// winServiceMain is only invoked on Windows. It detects when btcd is running +// as a service and reacts accordingly. +var winServiceMain func() (bool, error) + +// btcdMain is the real main function for btcd. It is necessary to work around +// the fact that deferred functions do not run when os.Exit() is called. The +// optional serverChan parameter is mainly used by the service code to be +// notified with the server once it is setup so it can gracefully stop it when +// requested from the service control manager. +func btcdMain(serverChan chan<- *server) error { + // Load configuration and parse command line. This function also + // initializes logging and configures it accordingly. + tcfg, _, err := loadConfig() + if err != nil { + return err + } + cfg = tcfg + defer backendLog.Flush() + + // Show version at startup. + btcdLog.Infof("Version %s", version()) + + // Enable http profiling server if requested. + if cfg.Profile != "" { + go func() { + listenAddr := net.JoinHostPort("", cfg.Profile) + btcdLog.Infof("Profile server listening on %s", listenAddr) + profileRedirect := http.RedirectHandler("/debug/pprof", + http.StatusSeeOther) + http.Handle("/", profileRedirect) + btcdLog.Errorf("%v", http.ListenAndServe(listenAddr, nil)) + }() + } + + // Write cpu profile if requested. + if cfg.CPUProfile != "" { + f, err := os.Create(cfg.CPUProfile) + if err != nil { + btcdLog.Errorf("Unable to create cpu profile: %v", err) + return err + } + pprof.StartCPUProfile(f) + defer f.Close() + defer pprof.StopCPUProfile() + } + + // Perform upgrades to btcd as new versions require it. + if err := doUpgrades(); err != nil { + btcdLog.Errorf("%v", err) + return err + } + + // Load the block database. + db, err := loadBlockDB() + if err != nil { + btcdLog.Errorf("%v", err) + return err + } + defer db.Close() + + // Ensure the database is sync'd and closed on Ctrl+C. + addInterruptHandler(func() { + btcdLog.Infof("Gracefully shutting down the database...") + db.Close() + }) + + // Drop indexes and exit if requested. + // + // NOTE: The order is important here because dropping the tx index also + // drops the address index since it relies on it. + if cfg.DropAddrIndex { + if err := indexers.DropAddrIndex(db); err != nil { + btcdLog.Errorf("%v", err) + return err + } + + return nil + } + if cfg.DropTxIndex { + if err := indexers.DropTxIndex(db); err != nil { + btcdLog.Errorf("%v", err) + return err + } + + return nil + } + + // Create server and start it. + server, err := newServer(cfg.Listeners, db, activeNetParams.Params) + if err != nil { + // TODO(oga) this logging could do with some beautifying. + btcdLog.Errorf("Unable to start server on %v: %v", + cfg.Listeners, err) + return err + } + addInterruptHandler(func() { + btcdLog.Infof("Gracefully shutting down the server...") + server.Stop() + server.WaitForShutdown() + }) + server.Start() + if serverChan != nil { + serverChan <- server + } + + // Monitor for graceful server shutdown and signal the main goroutine + // when done. This is done in a separate goroutine rather than waiting + // directly so the main goroutine can be signaled for shutdown by either + // a graceful shutdown or from the main interrupt handler. This is + // necessary since the main goroutine must be kept running long enough + // for the interrupt handler goroutine to finish. + go func() { + server.WaitForShutdown() + srvrLog.Infof("Server shutdown complete") + shutdownChannel <- struct{}{} + }() + + // Wait for shutdown signal from either a graceful server stop or from + // the interrupt handler. + <-shutdownChannel + btcdLog.Info("Shutdown complete") + return nil +} + +func main() { + // Use all processor cores. + runtime.GOMAXPROCS(runtime.NumCPU()) + + // Up some limits. + if err := limits.SetLimits(); err != nil { + fmt.Fprintf(os.Stderr, "failed to set limits: %v\n", err) + os.Exit(1) + } + + // Call serviceMain on Windows to handle running as a service. When + // the return isService flag is true, exit now since we ran as a + // service. Otherwise, just fall through to normal operation. + if runtime.GOOS == "windows" { + isService, err := winServiceMain() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + if isService { + os.Exit(0) + } + } + + // Work around defer not working after os.Exit() + if err := btcdMain(nil); err != nil { + os.Exit(1) + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/README.md b/vendor/github.com/btcsuite/btcd/btcec/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5875dfc91b7a5a59789a2643197bf75b4b23c85d --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/README.md @@ -0,0 +1,74 @@ +btcec +===== + +[] +(https://travis-ci.org/btcsuite/btcec) [![ISC License] +(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[] +(http://godoc.org/github.com/btcsuite/btcd/btcec) + +Package btcec implements elliptic curve cryptography needed for working with +Bitcoin (secp256k1 only for now). It is designed so that it may be used with the +standard crypto/ecdsa packages provided with go. A comprehensive suite of test +is provided to ensure proper functionality. Package btcec was originally based +on work from ThePiachu which is licensed under the same terms as Go, but it has +signficantly diverged since then. The btcsuite developers original is licensed +under the liberal ISC license. + +Although this package was primarily written for btcd, it has intentionally been +designed so it can be used as a standalone package for any projects needing to +use secp256k1 elliptic curve cryptography. + +## Installation and Updating + +```bash +$ go get -u github.com/btcsuite/btcd/btcec +``` + +## Examples + +* [Sign Message] + (http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--SignMessage) + Demonstrates signing a message with a secp256k1 private key that is first + parsed form raw bytes and serializing the generated signature. + +* [Verify Signature] + (http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--VerifySignature) + Demonstrates verifying a secp256k1 signature against a public key that is + first parsed from raw bytes. The signature is also parsed from raw bytes. + +* [Encryption] + (http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--EncryptMessage) + Demonstrates encrypting a message for a public key that is first parsed from + raw bytes, then decrypting it using the corresponding private key. + +* [Decryption] + (http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--DecryptMessage) + Demonstrates decrypting a message using a private key that is first parsed + from raw bytes. + +## GPG Verification Key + +All official release tags are signed by Conformal so users can ensure the code +has not been tampered with and is coming from the btcsuite developers. To +verify the signature perform the following: + +- Download the public key from the Conformal website at + https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt + +- Import the public key into your GPG keyring: + ```bash + gpg --import GIT-GPG-KEY-conformal.txt + ``` + +- Verify the release tag with the following command where `TAG_NAME` is a + placeholder for the specific tag: + ```bash + git tag -v TAG_NAME + ``` + +## License + +Package btcec is licensed under the [copyfree](http://copyfree.org) ISC License +except for btcec.go and btcec_test.go which is under the same license as Go. + diff --git a/vendor/github.com/btcsuite/btcd/btcec/bench_test.go b/vendor/github.com/btcsuite/btcd/btcec/bench_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ccdac1442aa28d0101cbbd2caef634002043dfad --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/bench_test.go @@ -0,0 +1,113 @@ +// Copyright 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import "testing" + +// BenchmarkAddJacobian benchmarks the secp256k1 curve addJacobian function with +// Z values of 1 so that the associated optimizations are used. +func BenchmarkAddJacobian(b *testing.B) { + b.StopTimer() + x1 := new(fieldVal).SetHex("34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6") + y1 := new(fieldVal).SetHex("0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232") + z1 := new(fieldVal).SetHex("1") + x2 := new(fieldVal).SetHex("34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6") + y2 := new(fieldVal).SetHex("0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232") + z2 := new(fieldVal).SetHex("1") + x3, y3, z3 := new(fieldVal), new(fieldVal), new(fieldVal) + curve := S256() + b.StartTimer() + for i := 0; i < b.N; i++ { + curve.TstAddJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3) + } +} + +// BenchmarkAddJacobianNotZOne benchmarks the secp256k1 curve addJacobian +// function with Z values other than one so the optimizations associated with +// Z=1 aren't used. +func BenchmarkAddJacobianNotZOne(b *testing.B) { + b.StopTimer() + x1 := new(fieldVal).SetHex("d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718") + y1 := new(fieldVal).SetHex("5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190") + z1 := new(fieldVal).SetHex("2") + x2 := new(fieldVal).SetHex("91abba6a34b7481d922a4bd6a04899d5a686f6cf6da4e66a0cb427fb25c04bd4") + y2 := new(fieldVal).SetHex("03fede65e30b4e7576a2abefc963ddbf9fdccbf791b77c29beadefe49951f7d1") + z2 := new(fieldVal).SetHex("3") + x3, y3, z3 := new(fieldVal), new(fieldVal), new(fieldVal) + curve := S256() + b.StartTimer() + for i := 0; i < b.N; i++ { + curve.TstAddJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3) + } +} + +// BenchmarkScalarBaseMult benchmarks the secp256k1 curve ScalarBaseMult +// function. +func BenchmarkScalarBaseMult(b *testing.B) { + k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + curve := S256() + for i := 0; i < b.N; i++ { + curve.ScalarBaseMult(k.Bytes()) + } +} + +// BenchmarkScalarBaseMultLarge benchmarks the secp256k1 curve ScalarBaseMult +// function with abnormally large k values. +func BenchmarkScalarBaseMultLarge(b *testing.B) { + k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c005751111111011111110") + curve := S256() + for i := 0; i < b.N; i++ { + curve.ScalarBaseMult(k.Bytes()) + } +} + +// BenchmarkScalarMult benchmarks the secp256k1 curve ScalarMult function. +func BenchmarkScalarMult(b *testing.B) { + x := fromHex("34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6") + y := fromHex("0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232") + k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + curve := S256() + for i := 0; i < b.N; i++ { + curve.ScalarMult(x, y, k.Bytes()) + } +} + +// BenchmarkNAF benchmarks the NAF function. +func BenchmarkNAF(b *testing.B) { + k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") + for i := 0; i < b.N; i++ { + NAF(k.Bytes()) + } +} + +// BenchmarkSigVerify benchmarks how long it takes the secp256k1 curve to +// verify signatures. +func BenchmarkSigVerify(b *testing.B) { + b.StopTimer() + // Randomly generated keypair. + // Private key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d + pubKey := PublicKey{ + Curve: S256(), + X: fromHex("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), + Y: fromHex("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), + } + + // Double sha256 of []byte{0x01, 0x02, 0x03, 0x04} + msgHash := fromHex("8de472e2399610baaa7f84840547cd409434e31f5d3bd71e4d947f283874f9c0") + sig := Signature{ + R: fromHex("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"), + S: fromHex("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"), + } + + if !sig.Verify(msgHash.Bytes(), &pubKey) { + b.Errorf("Signature failed to verify") + return + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + sig.Verify(msgHash.Bytes(), &pubKey) + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/btcec.go b/vendor/github.com/btcsuite/btcd/btcec/btcec.go new file mode 100644 index 0000000000000000000000000000000000000000..98d7b14329de60ba227226b3a4fc6a94acf8400c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/btcec.go @@ -0,0 +1,956 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2011 ThePiachu. All rights reserved. +// Copyright 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +// References: +// [SECG]: Recommended Elliptic Curve Domain Parameters +// http://www.secg.org/sec2-v2.pdf +// +// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone) + +// This package operates, internally, on Jacobian coordinates. For a given +// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1) +// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole +// calculation can be performed within the transform (as in ScalarMult and +// ScalarBaseMult). But even for Add and Double, it's faster to apply and +// reverse the transform than to operate in affine coordinates. + +import ( + "crypto/elliptic" + "math/big" + "sync" +) + +var ( + // fieldOne is simply the integer 1 in field representation. It is + // used to avoid needing to create it multiple times during the internal + // arithmetic. + fieldOne = new(fieldVal).SetInt(1) +) + +// KoblitzCurve supports a koblitz curve implementation that fits the ECC Curve +// interface from crypto/elliptic. +type KoblitzCurve struct { + *elliptic.CurveParams + q *big.Int + H int // cofactor of the curve. + + // byteSize is simply the bit size / 8 and is provided for convenience + // since it is calculated repeatedly. + byteSize int + + // bytePoints + bytePoints *[32][256][3]fieldVal + + // The next 6 values are used specifically for endomorphism + // optimizations in ScalarMult. + + // lambda must fulfill lambda^3 = 1 mod N where N is the order of G. + lambda *big.Int + + // beta must fulfill beta^3 = 1 mod P where P is the prime field of the + // curve. + beta *fieldVal + + // See the EndomorphismVectors in gensecp256k1.go to see how these are + // derived. + a1 *big.Int + b1 *big.Int + a2 *big.Int + b2 *big.Int +} + +// Params returns the parameters for the curve. +func (curve *KoblitzCurve) Params() *elliptic.CurveParams { + return curve.CurveParams +} + +// bigAffineToField takes an affine point (x, y) as big integers and converts +// it to an affine point as field values. +func (curve *KoblitzCurve) bigAffineToField(x, y *big.Int) (*fieldVal, *fieldVal) { + x3, y3 := new(fieldVal), new(fieldVal) + x3.SetByteSlice(x.Bytes()) + y3.SetByteSlice(y.Bytes()) + + return x3, y3 +} + +// fieldJacobianToBigAffine takes a Jacobian point (x, y, z) as field values and +// converts it to an affine point as big integers. +func (curve *KoblitzCurve) fieldJacobianToBigAffine(x, y, z *fieldVal) (*big.Int, *big.Int) { + // Inversions are expensive and both point addition and point doubling + // are faster when working with points that have a z value of one. So, + // if the point needs to be converted to affine, go ahead and normalize + // the point itself at the same time as the calculation is the same. + var zInv, tempZ fieldVal + zInv.Set(z).Inverse() // zInv = Z^-1 + tempZ.SquareVal(&zInv) // tempZ = Z^-2 + x.Mul(&tempZ) // X = X/Z^2 (mag: 1) + y.Mul(tempZ.Mul(&zInv)) // Y = Y/Z^3 (mag: 1) + z.SetInt(1) // Z = 1 (mag: 1) + + // Normalize the x and y values. + x.Normalize() + y.Normalize() + + // Convert the field values for the now affine point to big.Ints. + x3, y3 := new(big.Int), new(big.Int) + x3.SetBytes(x.Bytes()[:]) + y3.SetBytes(y.Bytes()[:]) + return x3, y3 +} + +// IsOnCurve returns boolean if the point (x,y) is on the curve. +// Part of the elliptic.Curve interface. This function differs from the +// crypto/elliptic algorithm since a = 0 not -3. +func (curve *KoblitzCurve) IsOnCurve(x, y *big.Int) bool { + // Convert big ints to field values for faster arithmetic. + fx, fy := curve.bigAffineToField(x, y) + + // Elliptic curve equation for secp256k1 is: y^2 = x^3 + 7 + y2 := new(fieldVal).SquareVal(fy).Normalize() + result := new(fieldVal).SquareVal(fx).Mul(fx).AddInt(7).Normalize() + return y2.Equals(result) +} + +// addZ1AndZ2EqualsOne adds two Jacobian points that are already known to have +// z values of 1 and stores the result in (x3, y3, z3). That is to say +// (x1, y1, 1) + (x2, y2, 1) = (x3, y3, z3). It performs faster addition than +// the generic add routine since less arithmetic is needed due to the ability to +// avoid the z value multiplications. +func (curve *KoblitzCurve) addZ1AndZ2EqualsOne(x1, y1, z1, x2, y2, x3, y3, z3 *fieldVal) { + // To compute the point addition efficiently, this implementation splits + // the equation into intermediate elements which are used to minimize + // the number of field multiplications using the method shown at: + // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-mmadd-2007-bl + // + // In particular it performs the calculations using the following: + // H = X2-X1, HH = H^2, I = 4*HH, J = H*I, r = 2*(Y2-Y1), V = X1*I + // X3 = r^2-J-2*V, Y3 = r*(V-X3)-2*Y1*J, Z3 = 2*H + // + // This results in a cost of 4 field multiplications, 2 field squarings, + // 6 field additions, and 5 integer multiplications. + + // When the x coordinates are the same for two points on the curve, the + // y coordinates either must be the same, in which case it is point + // doubling, or they are opposite and the result is the point at + // infinity per the group law for elliptic curve cryptography. + x1.Normalize() + y1.Normalize() + x2.Normalize() + y2.Normalize() + if x1.Equals(x2) { + if y1.Equals(y2) { + // Since x1 == x2 and y1 == y2, point doubling must be + // done, otherwise the addition would end up dividing + // by zero. + curve.doubleJacobian(x1, y1, z1, x3, y3, z3) + return + } + + // Since x1 == x2 and y1 == -y2, the sum is the point at + // infinity per the group law. + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + + // Calculate X3, Y3, and Z3 according to the intermediate elements + // breakdown above. + var h, i, j, r, v fieldVal + var negJ, neg2V, negX3 fieldVal + h.Set(x1).Negate(1).Add(x2) // H = X2-X1 (mag: 3) + i.SquareVal(&h).MulInt(4) // I = 4*H^2 (mag: 4) + j.Mul2(&h, &i) // J = H*I (mag: 1) + r.Set(y1).Negate(1).Add(y2).MulInt(2) // r = 2*(Y2-Y1) (mag: 6) + v.Mul2(x1, &i) // V = X1*I (mag: 1) + negJ.Set(&j).Negate(1) // negJ = -J (mag: 2) + neg2V.Set(&v).MulInt(2).Negate(2) // neg2V = -(2*V) (mag: 3) + x3.Set(&r).Square().Add(&negJ).Add(&neg2V) // X3 = r^2-J-2*V (mag: 6) + negX3.Set(x3).Negate(6) // negX3 = -X3 (mag: 7) + j.Mul(y1).MulInt(2).Negate(2) // J = -(2*Y1*J) (mag: 3) + y3.Set(&v).Add(&negX3).Mul(&r).Add(&j) // Y3 = r*(V-X3)-2*Y1*J (mag: 4) + z3.Set(&h).MulInt(2) // Z3 = 2*H (mag: 6) + + // Normalize the resulting field values to a magnitude of 1 as needed. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// addZ1EqualsZ2 adds two Jacobian points that are already known to have the +// same z value and stores the result in (x3, y3, z3). That is to say +// (x1, y1, z1) + (x2, y2, z1) = (x3, y3, z3). It performs faster addition than +// the generic add routine since less arithmetic is needed due to the known +// equivalence. +func (curve *KoblitzCurve) addZ1EqualsZ2(x1, y1, z1, x2, y2, x3, y3, z3 *fieldVal) { + // To compute the point addition efficiently, this implementation splits + // the equation into intermediate elements which are used to minimize + // the number of field multiplications using a slightly modified version + // of the method shown at: + // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-mmadd-2007-bl + // + // In particular it performs the calculations using the following: + // A = X2-X1, B = A^2, C=Y2-Y1, D = C^2, E = X1*B, F = X2*B + // X3 = D-E-F, Y3 = C*(E-X3)-Y1*(F-E), Z3 = Z1*A + // + // This results in a cost of 5 field multiplications, 2 field squarings, + // 9 field additions, and 0 integer multiplications. + + // When the x coordinates are the same for two points on the curve, the + // y coordinates either must be the same, in which case it is point + // doubling, or they are opposite and the result is the point at + // infinity per the group law for elliptic curve cryptography. + x1.Normalize() + y1.Normalize() + x2.Normalize() + y2.Normalize() + if x1.Equals(x2) { + if y1.Equals(y2) { + // Since x1 == x2 and y1 == y2, point doubling must be + // done, otherwise the addition would end up dividing + // by zero. + curve.doubleJacobian(x1, y1, z1, x3, y3, z3) + return + } + + // Since x1 == x2 and y1 == -y2, the sum is the point at + // infinity per the group law. + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + + // Calculate X3, Y3, and Z3 according to the intermediate elements + // breakdown above. + var a, b, c, d, e, f fieldVal + var negX1, negY1, negE, negX3 fieldVal + negX1.Set(x1).Negate(1) // negX1 = -X1 (mag: 2) + negY1.Set(y1).Negate(1) // negY1 = -Y1 (mag: 2) + a.Set(&negX1).Add(x2) // A = X2-X1 (mag: 3) + b.SquareVal(&a) // B = A^2 (mag: 1) + c.Set(&negY1).Add(y2) // C = Y2-Y1 (mag: 3) + d.SquareVal(&c) // D = C^2 (mag: 1) + e.Mul2(x1, &b) // E = X1*B (mag: 1) + negE.Set(&e).Negate(1) // negE = -E (mag: 2) + f.Mul2(x2, &b) // F = X2*B (mag: 1) + x3.Add2(&e, &f).Negate(3).Add(&d) // X3 = D-E-F (mag: 5) + negX3.Set(x3).Negate(5).Normalize() // negX3 = -X3 (mag: 1) + y3.Set(y1).Mul(f.Add(&negE)).Negate(3) // Y3 = -(Y1*(F-E)) (mag: 4) + y3.Add(e.Add(&negX3).Mul(&c)) // Y3 = C*(E-X3)+Y3 (mag: 5) + z3.Mul2(z1, &a) // Z3 = Z1*A (mag: 1) + + // Normalize the resulting field values to a magnitude of 1 as needed. + x3.Normalize() + y3.Normalize() +} + +// addZ2EqualsOne adds two Jacobian points when the second point is already +// known to have a z value of 1 (and the z value for the first point is not 1) +// and stores the result in (x3, y3, z3). That is to say (x1, y1, z1) + +// (x2, y2, 1) = (x3, y3, z3). It performs faster addition than the generic +// add routine since less arithmetic is needed due to the ability to avoid +// multiplications by the second point's z value. +func (curve *KoblitzCurve) addZ2EqualsOne(x1, y1, z1, x2, y2, x3, y3, z3 *fieldVal) { + // To compute the point addition efficiently, this implementation splits + // the equation into intermediate elements which are used to minimize + // the number of field multiplications using the method shown at: + // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl + // + // In particular it performs the calculations using the following: + // Z1Z1 = Z1^2, U2 = X2*Z1Z1, S2 = Y2*Z1*Z1Z1, H = U2-X1, HH = H^2, + // I = 4*HH, J = H*I, r = 2*(S2-Y1), V = X1*I + // X3 = r^2-J-2*V, Y3 = r*(V-X3)-2*Y1*J, Z3 = (Z1+H)^2-Z1Z1-HH + // + // This results in a cost of 7 field multiplications, 4 field squarings, + // 9 field additions, and 4 integer multiplications. + + // When the x coordinates are the same for two points on the curve, the + // y coordinates either must be the same, in which case it is point + // doubling, or they are opposite and the result is the point at + // infinity per the group law for elliptic curve cryptography. Since + // any number of Jacobian coordinates can represent the same affine + // point, the x and y values need to be converted to like terms. Due to + // the assumption made for this function that the second point has a z + // value of 1 (z2=1), the first point is already "converted". + var z1z1, u2, s2 fieldVal + x1.Normalize() + y1.Normalize() + z1z1.SquareVal(z1) // Z1Z1 = Z1^2 (mag: 1) + u2.Set(x2).Mul(&z1z1).Normalize() // U2 = X2*Z1Z1 (mag: 1) + s2.Set(y2).Mul(&z1z1).Mul(z1).Normalize() // S2 = Y2*Z1*Z1Z1 (mag: 1) + if x1.Equals(&u2) { + if y1.Equals(&s2) { + // Since x1 == x2 and y1 == y2, point doubling must be + // done, otherwise the addition would end up dividing + // by zero. + curve.doubleJacobian(x1, y1, z1, x3, y3, z3) + return + } + + // Since x1 == x2 and y1 == -y2, the sum is the point at + // infinity per the group law. + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + + // Calculate X3, Y3, and Z3 according to the intermediate elements + // breakdown above. + var h, hh, i, j, r, rr, v fieldVal + var negX1, negY1, negX3 fieldVal + negX1.Set(x1).Negate(1) // negX1 = -X1 (mag: 2) + h.Add2(&u2, &negX1) // H = U2-X1 (mag: 3) + hh.SquareVal(&h) // HH = H^2 (mag: 1) + i.Set(&hh).MulInt(4) // I = 4 * HH (mag: 4) + j.Mul2(&h, &i) // J = H*I (mag: 1) + negY1.Set(y1).Negate(1) // negY1 = -Y1 (mag: 2) + r.Set(&s2).Add(&negY1).MulInt(2) // r = 2*(S2-Y1) (mag: 6) + rr.SquareVal(&r) // rr = r^2 (mag: 1) + v.Mul2(x1, &i) // V = X1*I (mag: 1) + x3.Set(&v).MulInt(2).Add(&j).Negate(3) // X3 = -(J+2*V) (mag: 4) + x3.Add(&rr) // X3 = r^2+X3 (mag: 5) + negX3.Set(x3).Negate(5) // negX3 = -X3 (mag: 6) + y3.Set(y1).Mul(&j).MulInt(2).Negate(2) // Y3 = -(2*Y1*J) (mag: 3) + y3.Add(v.Add(&negX3).Mul(&r)) // Y3 = r*(V-X3)+Y3 (mag: 4) + z3.Add2(z1, &h).Square() // Z3 = (Z1+H)^2 (mag: 1) + z3.Add(z1z1.Add(&hh).Negate(2)) // Z3 = Z3-(Z1Z1+HH) (mag: 4) + + // Normalize the resulting field values to a magnitude of 1 as needed. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// addGeneric adds two Jacobian points (x1, y1, z1) and (x2, y2, z2) without any +// assumptions about the z values of the two points and stores the result in +// (x3, y3, z3). That is to say (x1, y1, z1) + (x2, y2, z2) = (x3, y3, z3). It +// is the slowest of the add routines due to requiring the most arithmetic. +func (curve *KoblitzCurve) addGeneric(x1, y1, z1, x2, y2, z2, x3, y3, z3 *fieldVal) { + // To compute the point addition efficiently, this implementation splits + // the equation into intermediate elements which are used to minimize + // the number of field multiplications using the method shown at: + // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + // + // In particular it performs the calculations using the following: + // Z1Z1 = Z1^2, Z2Z2 = Z2^2, U1 = X1*Z2Z2, U2 = X2*Z1Z1, S1 = Y1*Z2*Z2Z2 + // S2 = Y2*Z1*Z1Z1, H = U2-U1, I = (2*H)^2, J = H*I, r = 2*(S2-S1) + // V = U1*I + // X3 = r^2-J-2*V, Y3 = r*(V-X3)-2*S1*J, Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2)*H + // + // This results in a cost of 11 field multiplications, 5 field squarings, + // 9 field additions, and 4 integer multiplications. + + // When the x coordinates are the same for two points on the curve, the + // y coordinates either must be the same, in which case it is point + // doubling, or they are opposite and the result is the point at + // infinity. Since any number of Jacobian coordinates can represent the + // same affine point, the x and y values need to be converted to like + // terms. + var z1z1, z2z2, u1, u2, s1, s2 fieldVal + z1z1.SquareVal(z1) // Z1Z1 = Z1^2 (mag: 1) + z2z2.SquareVal(z2) // Z2Z2 = Z2^2 (mag: 1) + u1.Set(x1).Mul(&z2z2).Normalize() // U1 = X1*Z2Z2 (mag: 1) + u2.Set(x2).Mul(&z1z1).Normalize() // U2 = X2*Z1Z1 (mag: 1) + s1.Set(y1).Mul(&z2z2).Mul(z2).Normalize() // S1 = Y1*Z2*Z2Z2 (mag: 1) + s2.Set(y2).Mul(&z1z1).Mul(z1).Normalize() // S2 = Y2*Z1*Z1Z1 (mag: 1) + if u1.Equals(&u2) { + if s1.Equals(&s2) { + // Since x1 == x2 and y1 == y2, point doubling must be + // done, otherwise the addition would end up dividing + // by zero. + curve.doubleJacobian(x1, y1, z1, x3, y3, z3) + return + } + + // Since x1 == x2 and y1 == -y2, the sum is the point at + // infinity per the group law. + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + + // Calculate X3, Y3, and Z3 according to the intermediate elements + // breakdown above. + var h, i, j, r, rr, v fieldVal + var negU1, negS1, negX3 fieldVal + negU1.Set(&u1).Negate(1) // negU1 = -U1 (mag: 2) + h.Add2(&u2, &negU1) // H = U2-U1 (mag: 3) + i.Set(&h).MulInt(2).Square() // I = (2*H)^2 (mag: 2) + j.Mul2(&h, &i) // J = H*I (mag: 1) + negS1.Set(&s1).Negate(1) // negS1 = -S1 (mag: 2) + r.Set(&s2).Add(&negS1).MulInt(2) // r = 2*(S2-S1) (mag: 6) + rr.SquareVal(&r) // rr = r^2 (mag: 1) + v.Mul2(&u1, &i) // V = U1*I (mag: 1) + x3.Set(&v).MulInt(2).Add(&j).Negate(3) // X3 = -(J+2*V) (mag: 4) + x3.Add(&rr) // X3 = r^2+X3 (mag: 5) + negX3.Set(x3).Negate(5) // negX3 = -X3 (mag: 6) + y3.Mul2(&s1, &j).MulInt(2).Negate(2) // Y3 = -(2*S1*J) (mag: 3) + y3.Add(v.Add(&negX3).Mul(&r)) // Y3 = r*(V-X3)+Y3 (mag: 4) + z3.Add2(z1, z2).Square() // Z3 = (Z1+Z2)^2 (mag: 1) + z3.Add(z1z1.Add(&z2z2).Negate(2)) // Z3 = Z3-(Z1Z1+Z2Z2) (mag: 4) + z3.Mul(&h) // Z3 = Z3*H (mag: 1) + + // Normalize the resulting field values to a magnitude of 1 as needed. + x3.Normalize() + y3.Normalize() +} + +// addJacobian adds the passed Jacobian points (x1, y1, z1) and (x2, y2, z2) +// together and stores the result in (x3, y3, z3). +func (curve *KoblitzCurve) addJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3 *fieldVal) { + // A point at infinity is the identity according to the group law for + // elliptic curve cryptography. Thus, ∞ + P = P and P + ∞ = P. + if (x1.IsZero() && y1.IsZero()) || z1.IsZero() { + x3.Set(x2) + y3.Set(y2) + z3.Set(z2) + return + } + if (x2.IsZero() && y2.IsZero()) || z2.IsZero() { + x3.Set(x1) + y3.Set(y1) + z3.Set(z1) + return + } + + // Faster point addition can be achieved when certain assumptions are + // met. For example, when both points have the same z value, arithmetic + // on the z values can be avoided. This section thus checks for these + // conditions and calls an appropriate add function which is accelerated + // by using those assumptions. + z1.Normalize() + z2.Normalize() + isZ1One := z1.Equals(fieldOne) + isZ2One := z2.Equals(fieldOne) + switch { + case isZ1One && isZ2One: + curve.addZ1AndZ2EqualsOne(x1, y1, z1, x2, y2, x3, y3, z3) + return + case z1.Equals(z2): + curve.addZ1EqualsZ2(x1, y1, z1, x2, y2, x3, y3, z3) + return + case isZ2One: + curve.addZ2EqualsOne(x1, y1, z1, x2, y2, x3, y3, z3) + return + } + + // None of the above assumptions are true, so fall back to generic + // point addition. + curve.addGeneric(x1, y1, z1, x2, y2, z2, x3, y3, z3) +} + +// Add returns the sum of (x1,y1) and (x2,y2). Part of the elliptic.Curve +// interface. +func (curve *KoblitzCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { + // A point at infinity is the identity according to the group law for + // elliptic curve cryptography. Thus, ∞ + P = P and P + ∞ = P. + if x1.Sign() == 0 && y1.Sign() == 0 { + return x2, y2 + } + if x2.Sign() == 0 && y2.Sign() == 0 { + return x1, y1 + } + + // Convert the affine coordinates from big integers to field values + // and do the point addition in Jacobian projective space. + fx1, fy1 := curve.bigAffineToField(x1, y1) + fx2, fy2 := curve.bigAffineToField(x2, y2) + fx3, fy3, fz3 := new(fieldVal), new(fieldVal), new(fieldVal) + fOne := new(fieldVal).SetInt(1) + curve.addJacobian(fx1, fy1, fOne, fx2, fy2, fOne, fx3, fy3, fz3) + + // Convert the Jacobian coordinate field values back to affine big + // integers. + return curve.fieldJacobianToBigAffine(fx3, fy3, fz3) +} + +// doubleZ1EqualsOne performs point doubling on the passed Jacobian point +// when the point is already known to have a z value of 1 and stores +// the result in (x3, y3, z3). That is to say (x3, y3, z3) = 2*(x1, y1, 1). It +// performs faster point doubling than the generic routine since less arithmetic +// is needed due to the ability to avoid multiplication by the z value. +func (curve *KoblitzCurve) doubleZ1EqualsOne(x1, y1, x3, y3, z3 *fieldVal) { + // This function uses the assumptions that z1 is 1, thus the point + // doubling formulas reduce to: + // + // X3 = (3*X1^2)^2 - 8*X1*Y1^2 + // Y3 = (3*X1^2)*(4*X1*Y1^2 - X3) - 8*Y1^4 + // Z3 = 2*Y1 + // + // To compute the above efficiently, this implementation splits the + // equation into intermediate elements which are used to minimize the + // number of field multiplications in favor of field squarings which + // are roughly 35% faster than field multiplications with the current + // implementation at the time this was written. + // + // This uses a slightly modified version of the method shown at: + // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl + // + // In particular it performs the calculations using the following: + // A = X1^2, B = Y1^2, C = B^2, D = 2*((X1+B)^2-A-C) + // E = 3*A, F = E^2, X3 = F-2*D, Y3 = E*(D-X3)-8*C + // Z3 = 2*Y1 + // + // This results in a cost of 1 field multiplication, 5 field squarings, + // 6 field additions, and 5 integer multiplications. + var a, b, c, d, e, f fieldVal + z3.Set(y1).MulInt(2) // Z3 = 2*Y1 (mag: 2) + a.SquareVal(x1) // A = X1^2 (mag: 1) + b.SquareVal(y1) // B = Y1^2 (mag: 1) + c.SquareVal(&b) // C = B^2 (mag: 1) + b.Add(x1).Square() // B = (X1+B)^2 (mag: 1) + d.Set(&a).Add(&c).Negate(2) // D = -(A+C) (mag: 3) + d.Add(&b).MulInt(2) // D = 2*(B+D)(mag: 8) + e.Set(&a).MulInt(3) // E = 3*A (mag: 3) + f.SquareVal(&e) // F = E^2 (mag: 1) + x3.Set(&d).MulInt(2).Negate(16) // X3 = -(2*D) (mag: 17) + x3.Add(&f) // X3 = F+X3 (mag: 18) + f.Set(x3).Negate(18).Add(&d).Normalize() // F = D-X3 (mag: 1) + y3.Set(&c).MulInt(8).Negate(8) // Y3 = -(8*C) (mag: 9) + y3.Add(f.Mul(&e)) // Y3 = E*F+Y3 (mag: 10) + + // Normalize the field values back to a magnitude of 1. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// doubleGeneric performs point doubling on the passed Jacobian point without +// any assumptions about the z value and stores the result in (x3, y3, z3). +// That is to say (x3, y3, z3) = 2*(x1, y1, z1). It is the slowest of the point +// doubling routines due to requiring the most arithmetic. +func (curve *KoblitzCurve) doubleGeneric(x1, y1, z1, x3, y3, z3 *fieldVal) { + // Point doubling formula for Jacobian coordinates for the secp256k1 + // curve: + // X3 = (3*X1^2)^2 - 8*X1*Y1^2 + // Y3 = (3*X1^2)*(4*X1*Y1^2 - X3) - 8*Y1^4 + // Z3 = 2*Y1*Z1 + // + // To compute the above efficiently, this implementation splits the + // equation into intermediate elements which are used to minimize the + // number of field multiplications in favor of field squarings which + // are roughly 35% faster than field multiplications with the current + // implementation at the time this was written. + // + // This uses a slightly modified version of the method shown at: + // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + // + // In particular it performs the calculations using the following: + // A = X1^2, B = Y1^2, C = B^2, D = 2*((X1+B)^2-A-C) + // E = 3*A, F = E^2, X3 = F-2*D, Y3 = E*(D-X3)-8*C + // Z3 = 2*Y1*Z1 + // + // This results in a cost of 1 field multiplication, 5 field squarings, + // 6 field additions, and 5 integer multiplications. + var a, b, c, d, e, f fieldVal + z3.Mul2(y1, z1).MulInt(2) // Z3 = 2*Y1*Z1 (mag: 2) + a.SquareVal(x1) // A = X1^2 (mag: 1) + b.SquareVal(y1) // B = Y1^2 (mag: 1) + c.SquareVal(&b) // C = B^2 (mag: 1) + b.Add(x1).Square() // B = (X1+B)^2 (mag: 1) + d.Set(&a).Add(&c).Negate(2) // D = -(A+C) (mag: 3) + d.Add(&b).MulInt(2) // D = 2*(B+D)(mag: 8) + e.Set(&a).MulInt(3) // E = 3*A (mag: 3) + f.SquareVal(&e) // F = E^2 (mag: 1) + x3.Set(&d).MulInt(2).Negate(16) // X3 = -(2*D) (mag: 17) + x3.Add(&f) // X3 = F+X3 (mag: 18) + f.Set(x3).Negate(18).Add(&d).Normalize() // F = D-X3 (mag: 1) + y3.Set(&c).MulInt(8).Negate(8) // Y3 = -(8*C) (mag: 9) + y3.Add(f.Mul(&e)) // Y3 = E*F+Y3 (mag: 10) + + // Normalize the field values back to a magnitude of 1. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// doubleJacobian doubles the passed Jacobian point (x1, y1, z1) and stores the +// result in (x3, y3, z3). +func (curve *KoblitzCurve) doubleJacobian(x1, y1, z1, x3, y3, z3 *fieldVal) { + // Doubling a point at infinity is still infinity. + if y1.IsZero() || z1.IsZero() { + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + + // Slightly faster point doubling can be achieved when the z value is 1 + // by avoiding the multiplication on the z value. This section calls + // a point doubling function which is accelerated by using that + // assumption when possible. + if z1.Normalize().Equals(fieldOne) { + curve.doubleZ1EqualsOne(x1, y1, x3, y3, z3) + return + } + + // Fall back to generic point doubling which works with arbitrary z + // values. + curve.doubleGeneric(x1, y1, z1, x3, y3, z3) +} + +// Double returns 2*(x1,y1). Part of the elliptic.Curve interface. +func (curve *KoblitzCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { + if y1.Sign() == 0 { + return new(big.Int), new(big.Int) + } + + // Convert the affine coordinates from big integers to field values + // and do the point doubling in Jacobian projective space. + fx1, fy1 := curve.bigAffineToField(x1, y1) + fx3, fy3, fz3 := new(fieldVal), new(fieldVal), new(fieldVal) + fOne := new(fieldVal).SetInt(1) + curve.doubleJacobian(fx1, fy1, fOne, fx3, fy3, fz3) + + // Convert the Jacobian coordinate field values back to affine big + // integers. + return curve.fieldJacobianToBigAffine(fx3, fy3, fz3) +} + +// splitK returns a balanced length-two representation of k and their signs. +// This is algorithm 3.74 from [GECC]. +// +// One thing of note about this algorithm is that no matter what c1 and c2 are, +// the final equation of k = k1 + k2 * lambda (mod n) will hold. This is +// provable mathematically due to how a1/b1/a2/b2 are computed. +// +// c1 and c2 are chosen to minimize the max(k1,k2). +func (curve *KoblitzCurve) splitK(k []byte) ([]byte, []byte, int, int) { + // All math here is done with big.Int, which is slow. + // At some point, it might be useful to write something similar to + // fieldVal but for N instead of P as the prime field if this ends up + // being a bottleneck. + bigIntK := new(big.Int) + c1, c2 := new(big.Int), new(big.Int) + tmp1, tmp2 := new(big.Int), new(big.Int) + k1, k2 := new(big.Int), new(big.Int) + + bigIntK.SetBytes(k) + // c1 = round(b2 * k / n) from step 4. + // Rounding isn't really necessary and costs too much, hence skipped + c1.Mul(curve.b2, bigIntK) + c1.Div(c1, curve.N) + // c2 = round(b1 * k / n) from step 4 (sign reversed to optimize one step) + // Rounding isn't really necessary and costs too much, hence skipped + c2.Mul(curve.b1, bigIntK) + c2.Div(c2, curve.N) + // k1 = k - c1 * a1 - c2 * a2 from step 5 (note c2's sign is reversed) + tmp1.Mul(c1, curve.a1) + tmp2.Mul(c2, curve.a2) + k1.Sub(bigIntK, tmp1) + k1.Add(k1, tmp2) + // k2 = - c1 * b1 - c2 * b2 from step 5 (note c2's sign is reversed) + tmp1.Mul(c1, curve.b1) + tmp2.Mul(c2, curve.b2) + k2.Sub(tmp2, tmp1) + + // Note Bytes() throws out the sign of k1 and k2. This matters + // since k1 and/or k2 can be negative. Hence, we pass that + // back separately. + return k1.Bytes(), k2.Bytes(), k1.Sign(), k2.Sign() +} + +// moduloReduce reduces k from more than 32 bytes to 32 bytes and under. This +// is done by doing a simple modulo curve.N. We can do this since G^N = 1 and +// thus any other valid point on the elliptic curve has the same order. +func (curve *KoblitzCurve) moduloReduce(k []byte) []byte { + // Since the order of G is curve.N, we can use a much smaller number + // by doing modulo curve.N + if len(k) > curve.byteSize { + // Reduce k by performing modulo curve.N. + tmpK := new(big.Int).SetBytes(k) + tmpK.Mod(tmpK, curve.N) + return tmpK.Bytes() + } + + return k +} + +// NAF takes a positive integer k and returns the Non-Adjacent Form (NAF) as two +// byte slices. The first is where 1s will be. The second is where -1s will +// be. NAF is convenient in that on average, only 1/3rd of its values are +// non-zero. This is algorithm 3.30 from [GECC]. +// +// Essentially, this makes it possible to minimize the number of operations +// since the resulting ints returned will be at least 50% 0s. +func NAF(k []byte) ([]byte, []byte) { + // The essence of this algorithm is that whenever we have consecutive 1s + // in the binary, we want to put a -1 in the lowest bit and get a bunch + // of 0s up to the highest bit of consecutive 1s. This is due to this + // identity: + // 2^n + 2^(n-1) + 2^(n-2) + ... + 2^(n-k) = 2^(n+1) - 2^(n-k) + // + // The algorithm thus may need to go 1 more bit than the length of the + // bits we actually have, hence bits being 1 bit longer than was + // necessary. Since we need to know whether adding will cause a carry, + // we go from right-to-left in this addition. + var carry, curIsOne, nextIsOne bool + // these default to zero + retPos := make([]byte, len(k)+1) + retNeg := make([]byte, len(k)+1) + for i := len(k) - 1; i >= 0; i-- { + curByte := k[i] + for j := uint(0); j < 8; j++ { + curIsOne = curByte&1 == 1 + if j == 7 { + if i == 0 { + nextIsOne = false + } else { + nextIsOne = k[i-1]&1 == 1 + } + } else { + nextIsOne = curByte&2 == 2 + } + if carry { + if curIsOne { + // This bit is 1, so continue to carry + // and don't need to do anything. + } else { + // We've hit a 0 after some number of + // 1s. + if nextIsOne { + // Start carrying again since + // a new sequence of 1s is + // starting. + retNeg[i+1] += 1 << j + } else { + // Stop carrying since 1s have + // stopped. + carry = false + retPos[i+1] += 1 << j + } + } + } else if curIsOne { + if nextIsOne { + // If this is the start of at least 2 + // consecutive 1s, set the current one + // to -1 and start carrying. + retNeg[i+1] += 1 << j + carry = true + } else { + // This is a singleton, not consecutive + // 1s. + retPos[i+1] += 1 << j + } + } + curByte >>= 1 + } + } + if carry { + retPos[0] = 1 + } + + return retPos, retNeg +} + +// ScalarMult returns k*(Bx, By) where k is a big endian integer. +// Part of the elliptic.Curve interface. +func (curve *KoblitzCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { + // Point Q = ∞ (point at infinity). + qx, qy, qz := new(fieldVal), new(fieldVal), new(fieldVal) + + // Decompose K into k1 and k2 in order to halve the number of EC ops. + // See Algorithm 3.74 in [GECC]. + k1, k2, signK1, signK2 := curve.splitK(curve.moduloReduce(k)) + + // The main equation here to remember is: + // k * P = k1 * P + k2 * Ï•(P) + // + // P1 below is P in the equation, P2 below is Ï•(P) in the equation + p1x, p1y := curve.bigAffineToField(Bx, By) + p1yNeg := new(fieldVal).NegateVal(p1y, 1) + p1z := new(fieldVal).SetInt(1) + + // NOTE: Ï•(x,y) = (βx,y). The Jacobian z coordinate is 1, so this math + // goes through. + p2x := new(fieldVal).Mul2(p1x, curve.beta) + p2y := new(fieldVal).Set(p1y) + p2yNeg := new(fieldVal).NegateVal(p2y, 1) + p2z := new(fieldVal).SetInt(1) + + // Flip the positive and negative values of the points as needed + // depending on the signs of k1 and k2. As mentioned in the equation + // above, each of k1 and k2 are multiplied by the respective point. + // Since -k * P is the same thing as k * -P, and the group law for + // elliptic curves states that P(x, y) = -P(x, -y), it's faster and + // simplifies the code to just make the point negative. + if signK1 == -1 { + p1y, p1yNeg = p1yNeg, p1y + } + if signK2 == -1 { + p2y, p2yNeg = p2yNeg, p2y + } + + // NAF versions of k1 and k2 should have a lot more zeros. + // + // The Pos version of the bytes contain the +1s and the Neg versions + // contain the -1s. + k1PosNAF, k1NegNAF := NAF(k1) + k2PosNAF, k2NegNAF := NAF(k2) + k1Len := len(k1PosNAF) + k2Len := len(k2PosNAF) + + m := k1Len + if m < k2Len { + m = k2Len + } + + // Add left-to-right using the NAF optimization. See algorithm 3.77 + // from [GECC]. This should be faster overall since there will be a lot + // more instances of 0, hence reducing the number of Jacobian additions + // at the cost of 1 possible extra doubling. + var k1BytePos, k1ByteNeg, k2BytePos, k2ByteNeg byte + for i := 0; i < m; i++ { + // Since we're going left-to-right, pad the front with 0s. + if i < m-k1Len { + k1BytePos = 0 + k1ByteNeg = 0 + } else { + k1BytePos = k1PosNAF[i-(m-k1Len)] + k1ByteNeg = k1NegNAF[i-(m-k1Len)] + } + if i < m-k2Len { + k2BytePos = 0 + k2ByteNeg = 0 + } else { + k2BytePos = k2PosNAF[i-(m-k2Len)] + k2ByteNeg = k2NegNAF[i-(m-k2Len)] + } + + for j := 7; j >= 0; j-- { + // Q = 2 * Q + curve.doubleJacobian(qx, qy, qz, qx, qy, qz) + + if k1BytePos&0x80 == 0x80 { + curve.addJacobian(qx, qy, qz, p1x, p1y, p1z, + qx, qy, qz) + } else if k1ByteNeg&0x80 == 0x80 { + curve.addJacobian(qx, qy, qz, p1x, p1yNeg, p1z, + qx, qy, qz) + } + + if k2BytePos&0x80 == 0x80 { + curve.addJacobian(qx, qy, qz, p2x, p2y, p2z, + qx, qy, qz) + } else if k2ByteNeg&0x80 == 0x80 { + curve.addJacobian(qx, qy, qz, p2x, p2yNeg, p2z, + qx, qy, qz) + } + k1BytePos <<= 1 + k1ByteNeg <<= 1 + k2BytePos <<= 1 + k2ByteNeg <<= 1 + } + } + + // Convert the Jacobian coordinate field values back to affine big.Ints. + return curve.fieldJacobianToBigAffine(qx, qy, qz) +} + +// ScalarBaseMult returns k*G where G is the base point of the group and k is a +// big endian integer. +// Part of the elliptic.Curve interface. +func (curve *KoblitzCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { + newK := curve.moduloReduce(k) + diff := len(curve.bytePoints) - len(newK) + + // Point Q = ∞ (point at infinity). + qx, qy, qz := new(fieldVal), new(fieldVal), new(fieldVal) + + // curve.bytePoints has all 256 byte points for each 8-bit window. The + // strategy is to add up the byte points. This is best understood by + // expressing k in base-256 which it already sort of is. + // Each "digit" in the 8-bit window can be looked up using bytePoints + // and added together. + for i, byteVal := range newK { + p := curve.bytePoints[diff+i][byteVal] + curve.addJacobian(qx, qy, qz, &p[0], &p[1], &p[2], qx, qy, qz) + } + return curve.fieldJacobianToBigAffine(qx, qy, qz) +} + +// QPlus1Div4 returns the Q+1/4 constant for the curve for use in calculating +// square roots via exponention. +func (curve *KoblitzCurve) QPlus1Div4() *big.Int { + return curve.q +} + +var initonce sync.Once +var secp256k1 KoblitzCurve + +func initAll() { + initS256() +} + +// fromHex converts the passed hex string into a big integer pointer and will +// panic is there is an error. This is only provided for the hard-coded +// constants so errors in the source code can bet detected. It will only (and +// must only) be called for initialization purposes. +func fromHex(s string) *big.Int { + r, ok := new(big.Int).SetString(s, 16) + if !ok { + panic("invalid hex in source file: " + s) + } + return r +} + +func initS256() { + // Curve parameters taken from [SECG] section 2.4.1. + secp256k1.CurveParams = new(elliptic.CurveParams) + secp256k1.P = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F") + secp256k1.N = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141") + secp256k1.B = fromHex("0000000000000000000000000000000000000000000000000000000000000007") + secp256k1.Gx = fromHex("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798") + secp256k1.Gy = fromHex("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8") + secp256k1.BitSize = 256 + secp256k1.H = 1 + secp256k1.q = new(big.Int).Div(new(big.Int).Add(secp256k1.P, + big.NewInt(1)), big.NewInt(4)) + + // Provided for convenience since this gets computed repeatedly. + secp256k1.byteSize = secp256k1.BitSize / 8 + + // Deserialize and set the pre-computed table used to accelerate scalar + // base multiplication. This is hard-coded data, so any errors are + // panics because it means something is wrong in the source code. + if err := loadS256BytePoints(); err != nil { + panic(err) + } + + // Next 6 constants are from Hal Finney's bitcointalk.org post: + // https://bitcointalk.org/index.php?topic=3238.msg45565#msg45565 + // May he rest in peace. + // + // They have also been independently derived from the code in the + // EndomorphismVectors function in gensecp256k1.go. + secp256k1.lambda = fromHex("5363AD4CC05C30E0A5261C028812645A122E22EA20816678DF02967C1B23BD72") + secp256k1.beta = new(fieldVal).SetHex("7AE96A2B657C07106E64479EAC3434E99CF0497512F58995C1396C28719501EE") + secp256k1.a1 = fromHex("3086D221A7D46BCDE86C90E49284EB15") + secp256k1.b1 = fromHex("-E4437ED6010E88286F547FA90ABFE4C3") + secp256k1.a2 = fromHex("114CA50F7A8E2F3F657C1108D9D44CFD8") + secp256k1.b2 = fromHex("3086D221A7D46BCDE86C90E49284EB15") + + // Alternatively, we can use the parameters below, however, they seem + // to be about 8% slower. + // secp256k1.lambda = fromHex("AC9C52B33FA3CF1F5AD9E3FD77ED9BA4A880B9FC8EC739C2E0CFC810B51283CE") + // secp256k1.beta = new(fieldVal).SetHex("851695D49A83F8EF919BB86153CBCB16630FB68AED0A766A3EC693D68E6AFA40") + // secp256k1.a1 = fromHex("E4437ED6010E88286F547FA90ABFE4C3") + // secp256k1.b1 = fromHex("-3086D221A7D46BCDE86C90E49284EB15") + // secp256k1.a2 = fromHex("3086D221A7D46BCDE86C90E49284EB15") + // secp256k1.b2 = fromHex("114CA50F7A8E2F3F657C1108D9D44CFD8") +} + +// S256 returns a Curve which implements secp256k1. +func S256() *KoblitzCurve { + initonce.Do(initAll) + return &secp256k1 +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/btcec_test.go b/vendor/github.com/btcsuite/btcd/btcec/btcec_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3a3599c3608ea98ceb403e27de62527d5eb1b9f5 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/btcec_test.go @@ -0,0 +1,849 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Copyright 2011 ThePiachu. All rights reserved. +// Copyright 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec_test + +import ( + "crypto/rand" + "crypto/sha1" + "encoding/hex" + "fmt" + "math/big" + "testing" + + "github.com/btcsuite/btcd/btcec" +) + +// TestAddJacobian tests addition of points projected in Jacobian coordinates. +func TestAddJacobian(t *testing.T) { + tests := []struct { + x1, y1, z1 string // Coordinates (in hex) of first point to add + x2, y2, z2 string // Coordinates (in hex) of second point to add + x3, y3, z3 string // Coordinates (in hex) of expected point + }{ + // Addition with a point at infinity (left hand side). + // ∞ + P = P + { + "0", + "0", + "0", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "1", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "1", + }, + // Addition with a point at infinity (right hand side). + // P + ∞ = P + { + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "1", + "0", + "0", + "0", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "1", + }, + + // Addition with z1=z2=1 different x values. + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "1", + "0cfbc7da1e569b334460788faae0286e68b3af7379d5504efc25e4dba16e46a6", + "e205f79361bbe0346b037b4010985dbf4f9e1e955e7d0d14aca876bfa79aad87", + "44a5646b446e3877a648d6d381370d9ef55a83b666ebce9df1b1d7d65b817b2f", + }, + // Addition with z1=z2=1 same x opposite y. + // P(x, y, z) + P(x, -y, z) = infinity + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd", + "1", + "0", + "0", + "0", + }, + // Addition with z1=z2=1 same point. + // P(x, y, z) + P(x, y, z) = 2P + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + "ec9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee64f87c50c27", + "b082b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd0755c8f2a", + "16e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c1e594464", + }, + + // Addition with z1=z2 (!=1) different x values. + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "5d2fe112c21891d440f65a98473cb626111f8a234d2cd82f22172e369f002147", + "98e3386a0a622a35c4561ffb32308d8e1c6758e10ebb1b4ebd3d04b4eb0ecbe8", + "2", + "cfbc7da1e569b334460788faae0286e68b3af7379d5504efc25e4dba16e46a60", + "817de4d86ef80d1ac0ded00426176fd3e787a5579f43452b2a1db021e6ac3778", + "129591ad11b8e1de99235b4e04dc367bd56a0ed99baf3a77c6c75f5a6e05f08d", + }, + // Addition with z1=z2 (!=1) same x opposite y. + // P(x, y, z) + P(x, -y, z) = infinity + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "a470ab21467813b6e0496d2c2b70c11446bab4fcbc9a52b7f225f30e869aea9f", + "2", + "0", + "0", + "0", + }, + // Addition with z1=z2 (!=1) same point. + // P(x, y, z) + P(x, y, z) = 2P + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", + "2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", + "6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", + }, + + // Addition with z1!=z2 and z2=1 different x values. + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "1", + "3ef1f68795a6ccd1181e23eab80a1b9a2cebdcde755413bf097936eb5b91b4f3", + "0bef26c377c068d606f6802130bb7e9f3c3d2abcfa1a295950ed81133561cb04", + "252b235a2371c3bd3246b69c09b86cf7aad41db3375e74ef8d8ebeb4dc0be11a", + }, + // Addition with z1!=z2 and z2=1 same x opposite y. + // P(x, y, z) + P(x, -y, z) = infinity + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd", + "1", + "0", + "0", + "0", + }, + // Addition with z1!=z2 and z2=1 same point. + // P(x, y, z) + P(x, y, z) = 2P + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + "9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", + "2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", + "6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", + }, + + // Addition with z1!=z2 and z2!=1 different x values. + // P(x, y, z) + P(x, y, z) = 2P + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "91abba6a34b7481d922a4bd6a04899d5a686f6cf6da4e66a0cb427fb25c04bd4", + "03fede65e30b4e7576a2abefc963ddbf9fdccbf791b77c29beadefe49951f7d1", + "3", + "3f07081927fd3f6dadd4476614c89a09eba7f57c1c6c3b01fa2d64eac1eef31e", + "949166e04ebc7fd95a9d77e5dfd88d1492ecffd189792e3944eb2b765e09e031", + "eb8cba81bcffa4f44d75427506737e1f045f21e6d6f65543ee0e1d163540c931", + }, // Addition with z1!=z2 and z2!=1 same x opposite y. + // P(x, y, z) + P(x, -y, z) = infinity + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "dcc3768780c74a0325e2851edad0dc8a566fa61a9e7fc4a34d13dcb509f99bc7", + "cafc41904dd5428934f7d075129c8ba46eb622d4fc88d72cd1401452664add18", + "3", + "0", + "0", + "0", + }, + // Addition with z1!=z2 and z2!=1 same point. + // P(x, y, z) + P(x, y, z) = 2P + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "dcc3768780c74a0325e2851edad0dc8a566fa61a9e7fc4a34d13dcb509f99bc7", + "3503be6fb22abd76cb082f8aed63745b9149dd2b037728d32ebfebac99b51f17", + "3", + "9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", + "2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", + "6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Convert hex to field values. + x1 := btcec.NewFieldVal().SetHex(test.x1) + y1 := btcec.NewFieldVal().SetHex(test.y1) + z1 := btcec.NewFieldVal().SetHex(test.z1) + x2 := btcec.NewFieldVal().SetHex(test.x2) + y2 := btcec.NewFieldVal().SetHex(test.y2) + z2 := btcec.NewFieldVal().SetHex(test.z2) + x3 := btcec.NewFieldVal().SetHex(test.x3) + y3 := btcec.NewFieldVal().SetHex(test.y3) + z3 := btcec.NewFieldVal().SetHex(test.z3) + + // Ensure the test data is using points that are actually on + // the curve (or the point at infinity). + if !z1.IsZero() && !btcec.S256().TstIsJacobianOnCurve(x1, y1, z1) { + t.Errorf("#%d first point is not on the curve -- "+ + "invalid test data", i) + continue + } + if !z2.IsZero() && !btcec.S256().TstIsJacobianOnCurve(x2, y2, z2) { + t.Errorf("#%d second point is not on the curve -- "+ + "invalid test data", i) + continue + } + if !z3.IsZero() && !btcec.S256().TstIsJacobianOnCurve(x3, y3, z3) { + t.Errorf("#%d expected point is not on the curve -- "+ + "invalid test data", i) + continue + } + + // Add the two points. + rx, ry, rz := btcec.NewFieldVal(), btcec.NewFieldVal(), btcec.NewFieldVal() + btcec.S256().TstAddJacobian(x1, y1, z1, x2, y2, z2, rx, ry, rz) + + // Ensure result matches expected. + if !rx.Equals(x3) || !ry.Equals(y3) || !rz.Equals(z3) { + t.Errorf("#%d wrong result\ngot: (%v, %v, %v)\n"+ + "want: (%v, %v, %v)", i, rx, ry, rz, x3, y3, z3) + continue + } + } +} + +// TestAddAffine tests addition of points in affine coordinates. +func TestAddAffine(t *testing.T) { + tests := []struct { + x1, y1 string // Coordinates (in hex) of first point to add + x2, y2 string // Coordinates (in hex) of second point to add + x3, y3 string // Coordinates (in hex) of expected point + }{ + // Addition with a point at infinity (left hand side). + // ∞ + P = P + { + "0", + "0", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + }, + // Addition with a point at infinity (right hand side). + // P + ∞ = P + { + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "0", + "0", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + }, + + // Addition with different x values. + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", + "131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", + "fd5b88c21d3143518d522cd2796f3d726793c88b3e05636bc829448e053fed69", + "21cf4f6a5be5ff6380234c50424a970b1f7e718f5eb58f68198c108d642a137f", + }, + // Addition with same x opposite y. + // P(x, y) + P(x, -y) = infinity + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd", + "0", + "0", + }, + // Addition with same point. + // P(x, y) + P(x, y) = 2P + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "59477d88ae64a104dbb8d31ec4ce2d91b2fe50fa628fb6a064e22582196b365b", + "938dc8c0f13d1e75c987cb1a220501bd614b0d3dd9eb5c639847e1240216e3b6", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Convert hex to field values. + x1, y1 := fromHex(test.x1), fromHex(test.y1) + x2, y2 := fromHex(test.x2), fromHex(test.y2) + x3, y3 := fromHex(test.x3), fromHex(test.y3) + + // Ensure the test data is using points that are actually on + // the curve (or the point at infinity). + if !(x1.Sign() == 0 && y1.Sign() == 0) && !btcec.S256().IsOnCurve(x1, y1) { + t.Errorf("#%d first point is not on the curve -- "+ + "invalid test data", i) + continue + } + if !(x2.Sign() == 0 && y2.Sign() == 0) && !btcec.S256().IsOnCurve(x2, y2) { + t.Errorf("#%d second point is not on the curve -- "+ + "invalid test data", i) + continue + } + if !(x3.Sign() == 0 && y3.Sign() == 0) && !btcec.S256().IsOnCurve(x3, y3) { + t.Errorf("#%d expected point is not on the curve -- "+ + "invalid test data", i) + continue + } + + // Add the two points. + rx, ry := btcec.S256().Add(x1, y1, x2, y2) + + // Ensure result matches expected. + if rx.Cmp(x3) != 00 || ry.Cmp(y3) != 0 { + t.Errorf("#%d wrong result\ngot: (%x, %x)\n"+ + "want: (%x, %x)", i, rx, ry, x3, y3) + continue + } + } +} + +// TestDoubleJacobian tests doubling of points projected in Jacobian +// coordinates. +func TestDoubleJacobian(t *testing.T) { + tests := []struct { + x1, y1, z1 string // Coordinates (in hex) of point to double + x3, y3, z3 string // Coordinates (in hex) of expected point + }{ + // Doubling a point at infinity is still infinity. + { + "0", + "0", + "0", + "0", + "0", + "0", + }, + // Doubling with z1=1. + { + "34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", + "0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", + "1", + "ec9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee64f87c50c27", + "b082b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd0755c8f2a", + "16e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c1e594464", + }, + // Doubling with z1!=1. + { + "d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", + "5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", + "2", + "9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", + "2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", + "6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Convert hex to field values. + x1 := btcec.NewFieldVal().SetHex(test.x1) + y1 := btcec.NewFieldVal().SetHex(test.y1) + z1 := btcec.NewFieldVal().SetHex(test.z1) + x3 := btcec.NewFieldVal().SetHex(test.x3) + y3 := btcec.NewFieldVal().SetHex(test.y3) + z3 := btcec.NewFieldVal().SetHex(test.z3) + + // Ensure the test data is using points that are actually on + // the curve (or the point at infinity). + if !z1.IsZero() && !btcec.S256().TstIsJacobianOnCurve(x1, y1, z1) { + t.Errorf("#%d first point is not on the curve -- "+ + "invalid test data", i) + continue + } + if !z3.IsZero() && !btcec.S256().TstIsJacobianOnCurve(x3, y3, z3) { + t.Errorf("#%d expected point is not on the curve -- "+ + "invalid test data", i) + continue + } + + // Double the point. + rx, ry, rz := btcec.NewFieldVal(), btcec.NewFieldVal(), btcec.NewFieldVal() + btcec.S256().TstDoubleJacobian(x1, y1, z1, rx, ry, rz) + + // Ensure result matches expected. + if !rx.Equals(x3) || !ry.Equals(y3) || !rz.Equals(z3) { + t.Errorf("#%d wrong result\ngot: (%v, %v, %v)\n"+ + "want: (%v, %v, %v)", i, rx, ry, rz, x3, y3, z3) + continue + } + } +} + +// TestDoubleAffine tests doubling of points in affine coordinates. +func TestDoubleAffine(t *testing.T) { + tests := []struct { + x1, y1 string // Coordinates (in hex) of point to double + x3, y3 string // Coordinates (in hex) of expected point + }{ + // Doubling a point at infinity is still infinity. + // 2*∞ = ∞ (point at infinity) + + { + "0", + "0", + "0", + "0", + }, + + // Random points. + { + "e41387ffd8baaeeb43c2faa44e141b19790e8ac1f7ff43d480dc132230536f86", + "1b88191d430f559896149c86cbcb703193105e3cf3213c0c3556399836a2b899", + "88da47a089d333371bd798c548ef7caae76e737c1980b452d367b3cfe3082c19", + "3b6f659b09a362821dfcfefdbfbc2e59b935ba081b6c249eb147b3c2100b1bc1", + }, + { + "b3589b5d984f03ef7c80aeae444f919374799edf18d375cab10489a3009cff0c", + "c26cf343875b3630e15bccc61202815b5d8f1fd11308934a584a5babe69db36a", + "e193860172998751e527bb12563855602a227fc1f612523394da53b746bb2fb1", + "2bfcf13d2f5ab8bb5c611fab5ebbed3dc2f057062b39a335224c22f090c04789", + }, + { + "2b31a40fbebe3440d43ac28dba23eee71c62762c3fe3dbd88b4ab82dc6a82340", + "9ba7deb02f5c010e217607fd49d58db78ec273371ea828b49891ce2fd74959a1", + "2c8d5ef0d343b1a1a48aa336078eadda8481cb048d9305dc4fdf7ee5f65973a2", + "bb4914ac729e26d3cd8f8dc8f702f3f4bb7e0e9c5ae43335f6e94c2de6c3dc95", + }, + { + "61c64b760b51981fab54716d5078ab7dffc93730b1d1823477e27c51f6904c7a", + "ef6eb16ea1a36af69d7f66524c75a3a5e84c13be8fbc2e811e0563c5405e49bd", + "5f0dcdd2595f5ad83318a0f9da481039e36f135005420393e72dfca985b482f4", + "a01c849b0837065c1cb481b0932c441f49d1cab1b4b9f355c35173d93f110ae0", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Convert hex to field values. + x1, y1 := fromHex(test.x1), fromHex(test.y1) + x3, y3 := fromHex(test.x3), fromHex(test.y3) + + // Ensure the test data is using points that are actually on + // the curve (or the point at infinity). + if !(x1.Sign() == 0 && y1.Sign() == 0) && !btcec.S256().IsOnCurve(x1, y1) { + t.Errorf("#%d first point is not on the curve -- "+ + "invalid test data", i) + continue + } + if !(x3.Sign() == 0 && y3.Sign() == 0) && !btcec.S256().IsOnCurve(x3, y3) { + t.Errorf("#%d expected point is not on the curve -- "+ + "invalid test data", i) + continue + } + + // Double the point. + rx, ry := btcec.S256().Double(x1, y1) + + // Ensure result matches expected. + if rx.Cmp(x3) != 00 || ry.Cmp(y3) != 0 { + t.Errorf("#%d wrong result\ngot: (%x, %x)\n"+ + "want: (%x, %x)", i, rx, ry, x3, y3) + continue + } + } +} + +func TestOnCurve(t *testing.T) { + s256 := btcec.S256() + if !s256.IsOnCurve(s256.Params().Gx, s256.Params().Gy) { + t.Errorf("FAIL S256") + } +} + +type baseMultTest struct { + k string + x, y string +} + +//TODO: add more test vectors +var s256BaseMultTests = []baseMultTest{ + { + "AA5E28D6A97A2479A65527F7290311A3624D4CC0FA1578598EE3C2613BF99522", + "34F9460F0E4F08393D192B3C5133A6BA099AA0AD9FD54EBCCFACDFA239FF49C6", + "B71EA9BD730FD8923F6D25A7A91E7DD7728A960686CB5A901BB419E0F2CA232", + }, + { + "7E2B897B8CEBC6361663AD410835639826D590F393D90A9538881735256DFAE3", + "D74BF844B0862475103D96A611CF2D898447E288D34B360BC885CB8CE7C00575", + "131C670D414C4546B88AC3FF664611B1C38CEB1C21D76369D7A7A0969D61D97D", + }, + { + "6461E6DF0FE7DFD05329F41BF771B86578143D4DD1F7866FB4CA7E97C5FA945D", + "E8AECC370AEDD953483719A116711963CE201AC3EB21D3F3257BB48668C6A72F", + "C25CAF2F0EBA1DDB2F0F3F47866299EF907867B7D27E95B3873BF98397B24EE1", + }, + { + "376A3A2CDCD12581EFFF13EE4AD44C4044B8A0524C42422A7E1E181E4DEECCEC", + "14890E61FCD4B0BD92E5B36C81372CA6FED471EF3AA60A3E415EE4FE987DABA1", + "297B858D9F752AB42D3BCA67EE0EB6DCD1C2B7B0DBE23397E66ADC272263F982", + }, + { + "1B22644A7BE026548810C378D0B2994EEFA6D2B9881803CB02CEFF865287D1B9", + "F73C65EAD01C5126F28F442D087689BFA08E12763E0CEC1D35B01751FD735ED3", + "F449A8376906482A84ED01479BD18882B919C140D638307F0C0934BA12590BDE", + }, +} + +//TODO: test different curves as well? +func TestBaseMult(t *testing.T) { + s256 := btcec.S256() + for i, e := range s256BaseMultTests { + k, ok := new(big.Int).SetString(e.k, 16) + if !ok { + t.Errorf("%d: bad value for k: %s", i, e.k) + } + x, y := s256.ScalarBaseMult(k.Bytes()) + if fmt.Sprintf("%X", x) != e.x || fmt.Sprintf("%X", y) != e.y { + t.Errorf("%d: bad output for k=%s: got (%X, %X), want (%s, %s)", i, e.k, x, y, e.x, e.y) + } + if testing.Short() && i > 5 { + break + } + } +} + +func TestBaseMultVerify(t *testing.T) { + s256 := btcec.S256() + for bytes := 1; bytes < 40; bytes++ { + for i := 0; i < 30; i++ { + data := make([]byte, bytes) + _, err := rand.Read(data) + if err != nil { + t.Errorf("failed to read random data for %d", i) + continue + } + x, y := s256.ScalarBaseMult(data) + xWant, yWant := s256.ScalarMult(s256.Gx, s256.Gy, data) + if x.Cmp(xWant) != 0 || y.Cmp(yWant) != 0 { + t.Errorf("%d: bad output for %X: got (%X, %X), want (%X, %X)", i, data, x, y, xWant, yWant) + } + if testing.Short() && i > 2 { + break + } + } + } +} + +func TestScalarMult(t *testing.T) { + // Strategy for this test: + // Get a random exponent from the generator point at first + // This creates a new point which is used in the next iteration + // Use another random exponent on the new point. + // We use BaseMult to verify by multiplying the previous exponent + // and the new random exponent together (mod N) + s256 := btcec.S256() + x, y := s256.Gx, s256.Gy + exponent := big.NewInt(1) + for i := 0; i < 1024; i++ { + data := make([]byte, 32) + _, err := rand.Read(data) + if err != nil { + t.Fatalf("failed to read random data at %d", i) + break + } + x, y = s256.ScalarMult(x, y, data) + exponent.Mul(exponent, new(big.Int).SetBytes(data)) + xWant, yWant := s256.ScalarBaseMult(exponent.Bytes()) + if x.Cmp(xWant) != 0 || y.Cmp(yWant) != 0 { + t.Fatalf("%d: bad output for %X: got (%X, %X), want (%X, %X)", i, data, x, y, xWant, yWant) + break + } + } +} + +// Test this curve's usage with the ecdsa package. + +func testKeyGeneration(t *testing.T, c *btcec.KoblitzCurve, tag string) { + priv, err := btcec.NewPrivateKey(c) + if err != nil { + t.Errorf("%s: error: %s", tag, err) + return + } + if !c.IsOnCurve(priv.PublicKey.X, priv.PublicKey.Y) { + t.Errorf("%s: public key invalid: %s", tag, err) + } +} + +func TestKeyGeneration(t *testing.T) { + testKeyGeneration(t, btcec.S256(), "S256") +} + +func testSignAndVerify(t *testing.T, c *btcec.KoblitzCurve, tag string) { + priv, _ := btcec.NewPrivateKey(c) + pub := priv.PubKey() + + hashed := []byte("testing") + sig, err := priv.Sign(hashed) + if err != nil { + t.Errorf("%s: error signing: %s", tag, err) + return + } + + if !sig.Verify(hashed, pub) { + t.Errorf("%s: Verify failed", tag) + } + + hashed[0] ^= 0xff + if sig.Verify(hashed, pub) { + t.Errorf("%s: Verify always works!", tag) + } +} + +func TestSignAndVerify(t *testing.T) { + testSignAndVerify(t, btcec.S256(), "S256") +} + +func TestNAF(t *testing.T) { + negOne := big.NewInt(-1) + one := big.NewInt(1) + two := big.NewInt(2) + for i := 0; i < 1024; i++ { + data := make([]byte, 32) + _, err := rand.Read(data) + if err != nil { + t.Fatalf("failed to read random data at %d", i) + break + } + nafPos, nafNeg := btcec.NAF(data) + want := new(big.Int).SetBytes(data) + got := big.NewInt(0) + // Check that the NAF representation comes up with the right number + for i := 0; i < len(nafPos); i++ { + bytePos := nafPos[i] + byteNeg := nafNeg[i] + for j := 7; j >= 0; j-- { + got.Mul(got, two) + if bytePos&0x80 == 0x80 { + got.Add(got, one) + } else if byteNeg&0x80 == 0x80 { + got.Add(got, negOne) + } + bytePos <<= 1 + byteNeg <<= 1 + } + } + if got.Cmp(want) != 0 { + t.Errorf("%d: Failed NAF got %X want %X", i, got, want) + } + } +} + +func fromHex(s string) *big.Int { + r, ok := new(big.Int).SetString(s, 16) + if !ok { + panic("bad hex") + } + return r +} + +// These test vectors were taken from +// http://csrc.nist.gov/groups/STM/cavp/documents/dss/ecdsatestvectors.zip +var testVectors = []struct { + msg string + Qx, Qy string + r, s string + ok bool +}{ +/* + * All of these tests are disabled since they are for P224, not sec256k1. + * they are left here as an example of test vectors for when some *real* + * vectors may be found. + * - oga@conformal.com + { + "09626b45493672e48f3d1226a3aff3201960e577d33a7f72c7eb055302db8fe8ed61685dd036b554942a5737cd1512cdf811ee0c00e6dd2f08c69f08643be396e85dafda664801e772cdb7396868ac47b172245b41986aa2648cb77fbbfa562581be06651355a0c4b090f9d17d8f0ab6cced4e0c9d386cf465a516630f0231bd", + "9504b5b82d97a264d8b3735e0568decabc4b6ca275bc53cbadfc1c40", + "03426f80e477603b10dee670939623e3da91a94267fc4e51726009ed", + "81d3ac609f9575d742028dd496450a58a60eea2dcf8b9842994916e1", + "96a8c5f382c992e8f30ccce9af120b067ec1d74678fa8445232f75a5", + false, + }, + { + "96b2b6536f6df29be8567a72528aceeaccbaa66c66c534f3868ca9778b02faadb182e4ed34662e73b9d52ecbe9dc8e875fc05033c493108b380689ebf47e5b062e6a0cdb3dd34ce5fe347d92768d72f7b9b377c20aea927043b509c078ed2467d7113405d2ddd458811e6faf41c403a2a239240180f1430a6f4330df5d77de37", + "851e3100368a22478a0029353045ae40d1d8202ef4d6533cfdddafd8", + "205302ac69457dd345e86465afa72ee8c74ca97e2b0b999aec1f10c2", + "4450c2d38b697e990721aa2dbb56578d32b4f5aeb3b9072baa955ee0", + "e26d4b589166f7b4ba4b1c8fce823fa47aad22f8c9c396b8c6526e12", + false, + }, + { + "86778dbb4a068a01047a8d245d632f636c11d2ad350740b36fad90428b454ad0f120cb558d12ea5c8a23db595d87543d06d1ef489263d01ee529871eb68737efdb8ff85bc7787b61514bed85b7e01d6be209e0a4eb0db5c8df58a5c5bf706d76cb2bdf7800208639e05b89517155d11688236e6a47ed37d8e5a2b1e0adea338e", + "ad5bda09d319a717c1721acd6688d17020b31b47eef1edea57ceeffc", + "c8ce98e181770a7c9418c73c63d01494b8b80a41098c5ea50692c984", + "de5558c257ab4134e52c19d8db3b224a1899cbd08cc508ce8721d5e9", + "745db7af5a477e5046705c0a5eff1f52cb94a79d481f0c5a5e108ecd", + true, + }, + { + "4bc6ef1958556686dab1e39c3700054a304cbd8f5928603dcd97fafd1f29e69394679b638f71c9344ce6a535d104803d22119f57b5f9477e253817a52afa9bfbc9811d6cc8c8be6b6566c6ef48b439bbb532abe30627548c598867f3861ba0b154dc1c3deca06eb28df8efd28258554b5179883a36fbb1eecf4f93ee19d41e3d", + "cc5eea2edf964018bdc0504a3793e4d2145142caa09a72ac5fb8d3e8", + "a48d78ae5d08aa725342773975a00d4219cf7a8029bb8cf3c17c374a", + "67b861344b4e416d4094472faf4272f6d54a497177fbc5f9ef292836", + "1d54f3fcdad795bf3b23408ecbac3e1321d1d66f2e4e3d05f41f7020", + false, + }, + { + "bb658732acbf3147729959eb7318a2058308b2739ec58907dd5b11cfa3ecf69a1752b7b7d806fe00ec402d18f96039f0b78dbb90a59c4414fb33f1f4e02e4089de4122cd93df5263a95be4d7084e2126493892816e6a5b4ed123cb705bf930c8f67af0fb4514d5769232a9b008a803af225160ce63f675bd4872c4c97b146e5e", + "6234c936e27bf141fc7534bfc0a7eedc657f91308203f1dcbd642855", + "27983d87ca785ef4892c3591ef4a944b1deb125dd58bd351034a6f84", + "e94e05b42d01d0b965ffdd6c3a97a36a771e8ea71003de76c4ecb13f", + "1dc6464ffeefbd7872a081a5926e9fc3e66d123f1784340ba17737e9", + false, + }, + { + "7c00be9123bfa2c4290be1d8bc2942c7f897d9a5b7917e3aabd97ef1aab890f148400a89abd554d19bec9d8ed911ce57b22fbcf6d30ca2115f13ce0a3f569a23bad39ee645f624c49c60dcfc11e7d2be24de9c905596d8f23624d63dc46591d1f740e46f982bfae453f107e80db23545782be23ce43708245896fc54e1ee5c43", + "9f3f037282aaf14d4772edffff331bbdda845c3f65780498cde334f1", + "8308ee5a16e3bcb721b6bc30000a0419bc1aaedd761be7f658334066", + "6381d7804a8808e3c17901e4d283b89449096a8fba993388fa11dc54", + "8e858f6b5b253686a86b757bad23658cda53115ac565abca4e3d9f57", + false, + }, + { + "cffc122a44840dc705bb37130069921be313d8bde0b66201aebc48add028ca131914ef2e705d6bedd19dc6cf9459bbb0f27cdfe3c50483808ffcdaffbeaa5f062e097180f07a40ef4ab6ed03fe07ed6bcfb8afeb42c97eafa2e8a8df469de07317c5e1494c41547478eff4d8c7d9f0f484ad90fedf6e1c35ee68fa73f1691601", + "a03b88a10d930002c7b17ca6af2fd3e88fa000edf787dc594f8d4fd4", + "e0cf7acd6ddc758e64847fe4df9915ebda2f67cdd5ec979aa57421f5", + "387b84dcf37dc343c7d2c5beb82f0bf8bd894b395a7b894565d296c1", + "4adc12ce7d20a89ce3925e10491c731b15ddb3f339610857a21b53b4", + false, + }, + { + "26e0e0cafd85b43d16255908ccfd1f061c680df75aba3081246b337495783052ba06c60f4a486c1591a4048bae11b4d7fec4f161d80bdc9a7b79d23e44433ed625eab280521a37f23dd3e1bdc5c6a6cfaa026f3c45cf703e76dab57add93fe844dd4cda67dc3bddd01f9152579e49df60969b10f09ce9372fdd806b0c7301866", + "9a8983c42f2b5a87c37a00458b5970320d247f0c8a88536440173f7d", + "15e489ec6355351361900299088cfe8359f04fe0cab78dde952be80c", + "929a21baa173d438ec9f28d6a585a2f9abcfc0a4300898668e476dc0", + "59a853f046da8318de77ff43f26fe95a92ee296fa3f7e56ce086c872", + true, + }, + { + "1078eac124f48ae4f807e946971d0de3db3748dd349b14cca5c942560fb25401b2252744f18ad5e455d2d97ed5ae745f55ff509c6c8e64606afe17809affa855c4c4cdcaf6b69ab4846aa5624ed0687541aee6f2224d929685736c6a23906d974d3c257abce1a3fb8db5951b89ecb0cda92b5207d93f6618fd0f893c32cf6a6e", + "d6e55820bb62c2be97650302d59d667a411956138306bd566e5c3c2b", + "631ab0d64eaf28a71b9cbd27a7a88682a2167cee6251c44e3810894f", + "65af72bc7721eb71c2298a0eb4eed3cec96a737cc49125706308b129", + "bd5a987c78e2d51598dbd9c34a9035b0069c580edefdacee17ad892a", + false, + }, + { + "919deb1fdd831c23481dfdb2475dcbe325b04c34f82561ced3d2df0b3d749b36e255c4928973769d46de8b95f162b53cd666cad9ae145e7fcfba97919f703d864efc11eac5f260a5d920d780c52899e5d76f8fe66936ff82130761231f536e6a3d59792f784902c469aa897aabf9a0678f93446610d56d5e0981e4c8a563556b", + "269b455b1024eb92d860a420f143ac1286b8cce43031562ae7664574", + "baeb6ca274a77c44a0247e5eb12ca72bdd9a698b3f3ae69c9f1aaa57", + "cb4ec2160f04613eb0dfe4608486091a25eb12aa4dec1afe91cfb008", + "40b01d8cd06589481574f958b98ca08ade9d2a8fe31024375c01bb40", + false, + }, + { + "6e012361250dacf6166d2dd1aa7be544c3206a9d43464b3fcd90f3f8cf48d08ec099b59ba6fe7d9bdcfaf244120aed1695d8be32d1b1cd6f143982ab945d635fb48a7c76831c0460851a3d62b7209c30cd9c2abdbe3d2a5282a9fcde1a6f418dd23c409bc351896b9b34d7d3a1a63bbaf3d677e612d4a80fa14829386a64b33f", + "6d2d695efc6b43b13c14111f2109608f1020e3e03b5e21cfdbc82fcd", + "26a4859296b7e360b69cf40be7bd97ceaffa3d07743c8489fc47ca1b", + "9a8cb5f2fdc288b7183c5b32d8e546fc2ed1ca4285eeae00c8b572ad", + "8c623f357b5d0057b10cdb1a1593dab57cda7bdec9cf868157a79b97", + true, + }, + { + "bf6bd7356a52b234fe24d25557200971fc803836f6fec3cade9642b13a8e7af10ab48b749de76aada9d8927f9b12f75a2c383ca7358e2566c4bb4f156fce1fd4e87ef8c8d2b6b1bdd351460feb22cdca0437ac10ca5e0abbbce9834483af20e4835386f8b1c96daaa41554ceee56730aac04f23a5c765812efa746051f396566", + "14250131b2599939cf2d6bc491be80ddfe7ad9de644387ee67de2d40", + "b5dc473b5d014cd504022043c475d3f93c319a8bdcb7262d9e741803", + "4f21642f2201278a95339a80f75cc91f8321fcb3c9462562f6cbf145", + "452a5f816ea1f75dee4fd514fa91a0d6a43622981966c59a1b371ff8", + false, + }, + { + "0eb7f4032f90f0bd3cf9473d6d9525d264d14c031a10acd31a053443ed5fe919d5ac35e0be77813071b4062f0b5fdf58ad5f637b76b0b305aec18f82441b6e607b44cdf6e0e3c7c57f24e6fd565e39430af4a6b1d979821ed0175fa03e3125506847654d7e1ae904ce1190ae38dc5919e257bdac2db142a6e7cd4da6c2e83770", + "d1f342b7790a1667370a1840255ac5bbbdc66f0bc00ae977d99260ac", + "76416cabae2de9a1000b4646338b774baabfa3db4673790771220cdb", + "bc85e3fc143d19a7271b2f9e1c04b86146073f3fab4dda1c3b1f35ca", + "9a5c70ede3c48d5f43307a0c2a4871934424a3303b815df4bb0f128e", + false, + }, + { + "5cc25348a05d85e56d4b03cec450128727bc537c66ec3a9fb613c151033b5e86878632249cba83adcefc6c1e35dcd31702929c3b57871cda5c18d1cf8f9650a25b917efaed56032e43b6fc398509f0d2997306d8f26675f3a8683b79ce17128e006aa0903b39eeb2f1001be65de0520115e6f919de902b32c38d691a69c58c92", + "7e49a7abf16a792e4c7bbc4d251820a2abd22d9f2fc252a7bf59c9a6", + "44236a8fb4791c228c26637c28ae59503a2f450d4cfb0dc42aa843b9", + "084461b4050285a1a85b2113be76a17878d849e6bc489f4d84f15cd8", + "079b5bddcc4d45de8dbdfd39f69817c7e5afa454a894d03ee1eaaac3", + false, + }, + { + "1951533ce33afb58935e39e363d8497a8dd0442018fd96dff167b3b23d7206a3ee182a3194765df4768a3284e23b8696c199b4686e670d60c9d782f08794a4bccc05cffffbd1a12acd9eb1cfa01f7ebe124da66ecff4599ea7720c3be4bb7285daa1a86ebf53b042bd23208d468c1b3aa87381f8e1ad63e2b4c2ba5efcf05845", + "31945d12ebaf4d81f02be2b1768ed80784bf35cf5e2ff53438c11493", + "a62bebffac987e3b9d3ec451eb64c462cdf7b4aa0b1bbb131ceaa0a4", + "bc3c32b19e42b710bca5c6aaa128564da3ddb2726b25f33603d2af3c", + "ed1a719cc0c507edc5239d76fe50e2306c145ad252bd481da04180c0", + false, + }, +*/ +} + +func TestVectors(t *testing.T) { + sha := sha1.New() + + for i, test := range testVectors { + pub := btcec.PublicKey{ + Curve: btcec.S256(), + X: fromHex(test.Qx), + Y: fromHex(test.Qy), + } + msg, _ := hex.DecodeString(test.msg) + sha.Reset() + sha.Write(msg) + hashed := sha.Sum(nil) + sig := btcec.Signature{R: fromHex(test.r), S: fromHex(test.s)} + if fuck := sig.Verify(hashed, &pub); fuck != test.ok { + //t.Errorf("%d: bad result %v %v", i, pub, hashed) + t.Errorf("%d: bad result %v instead of %v", i, fuck, + test.ok) + } + if testing.Short() { + break + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/ciphering.go b/vendor/github.com/btcsuite/btcd/btcec/ciphering.go new file mode 100644 index 0000000000000000000000000000000000000000..e4f3abeb61ee110937ba6d90d3a6294d46fd3b0d --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/ciphering.go @@ -0,0 +1,216 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "crypto/sha512" + "errors" + "io" +) + +var ( + // ErrInvalidMAC occurs when Message Authentication Check (MAC) fails + // during decryption. This happens because of either invalid private key or + // corrupt ciphertext. + ErrInvalidMAC = errors.New("invalid mac hash") + + // errInputTooShort occurs when the input ciphertext to the Decrypt + // function is less than 134 bytes long. + errInputTooShort = errors.New("ciphertext too short") + + // errUnsupportedCurve occurs when the first two bytes of the encrypted + // text aren't 0x02CA (= 712 = secp256k1, from OpenSSL). + errUnsupportedCurve = errors.New("unsupported curve") + + errInvalidXLength = errors.New("invalid X length, must be 32") + errInvalidYLength = errors.New("invalid Y length, must be 32") + errInvalidPadding = errors.New("invalid PKCS#7 padding") + + // 0x02CA = 714 + ciphCurveBytes = [2]byte{0x02, 0xCA} + // 0x20 = 32 + ciphCoordLength = [2]byte{0x00, 0x20} +) + +// GenerateSharedSecret generates a shared secret based on a private key and a +// private key using Diffie-Hellman key exchange (ECDH) (RFC 4753). +// RFC5903 Section 9 states we should only return x. +func GenerateSharedSecret(privkey *PrivateKey, pubkey *PublicKey) []byte { + x, _ := pubkey.Curve.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes()) + return x.Bytes() +} + +// Encrypt encrypts data for the target public key using AES-256-CBC. It also +// generates a private key (the pubkey of which is also in the output). The only +// supported curve is secp256k1. The `structure' that it encodes everything into +// is: +// +// struct { +// // Initialization Vector used for AES-256-CBC +// IV [16]byte +// // Public Key: curve(2) + len_of_pubkeyX(2) + pubkeyX + +// // len_of_pubkeyY(2) + pubkeyY (curve = 714) +// PublicKey [70]byte +// // Cipher text +// Data []byte +// // HMAC-SHA-256 Message Authentication Code +// HMAC [32]byte +// } +// +// The primary aim is to ensure byte compatibility with Pyelliptic. Also, refer +// to section 5.8.1 of ANSI X9.63 for rationale on this format. +func Encrypt(pubkey *PublicKey, in []byte) ([]byte, error) { + ephemeral, err := NewPrivateKey(S256()) + if err != nil { + return nil, err + } + ecdhKey := GenerateSharedSecret(ephemeral, pubkey) + derivedKey := sha512.Sum512(ecdhKey) + keyE := derivedKey[:32] + keyM := derivedKey[32:] + + paddedIn := addPKCSPadding(in) + // IV + Curve params/X/Y + padded plaintext/ciphertext + HMAC-256 + out := make([]byte, aes.BlockSize+70+len(paddedIn)+sha256.Size) + iv := out[:aes.BlockSize] + if _, err = io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + // start writing public key + pb := ephemeral.PubKey().SerializeUncompressed() + offset := aes.BlockSize + + // curve and X length + copy(out[offset:offset+4], append(ciphCurveBytes[:], ciphCoordLength[:]...)) + offset += 4 + // X + copy(out[offset:offset+32], pb[1:33]) + offset += 32 + // Y length + copy(out[offset:offset+2], ciphCoordLength[:]) + offset += 2 + // Y + copy(out[offset:offset+32], pb[33:]) + offset += 32 + + // start encryption + block, err := aes.NewCipher(keyE) + if err != nil { + return nil, err + } + mode := cipher.NewCBCEncrypter(block, iv) + mode.CryptBlocks(out[offset:len(out)-sha256.Size], paddedIn) + + // start HMAC-SHA-256 + hm := hmac.New(sha256.New, keyM) + hm.Write(out[:len(out)-sha256.Size]) // everything is hashed + copy(out[len(out)-sha256.Size:], hm.Sum(nil)) // write checksum + + return out, nil +} + +// Decrypt decrypts data that was encrypted using the Encrypt function. +func Decrypt(priv *PrivateKey, in []byte) ([]byte, error) { + // IV + Curve params/X/Y + 1 block + HMAC-256 + if len(in) < aes.BlockSize+70+aes.BlockSize+sha256.Size { + return nil, errInputTooShort + } + + // read iv + iv := in[:aes.BlockSize] + offset := aes.BlockSize + + // start reading pubkey + if !bytes.Equal(in[offset:offset+2], ciphCurveBytes[:]) { + return nil, errUnsupportedCurve + } + offset += 2 + + if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) { + return nil, errInvalidXLength + } + offset += 2 + + xBytes := in[offset : offset+32] + offset += 32 + + if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) { + return nil, errInvalidYLength + } + offset += 2 + + yBytes := in[offset : offset+32] + offset += 32 + + pb := make([]byte, 65) + pb[0] = byte(0x04) // uncompressed + copy(pb[1:33], xBytes) + copy(pb[33:], yBytes) + // check if (X, Y) lies on the curve and create a Pubkey if it does + pubkey, err := ParsePubKey(pb, S256()) + if err != nil { + return nil, err + } + + // check for cipher text length + if (len(in)-aes.BlockSize-offset-sha256.Size)%aes.BlockSize != 0 { + return nil, errInvalidPadding // not padded to 16 bytes + } + + // read hmac + messageMAC := in[len(in)-sha256.Size:] + + // generate shared secret + ecdhKey := GenerateSharedSecret(priv, pubkey) + derivedKey := sha512.Sum512(ecdhKey) + keyE := derivedKey[:32] + keyM := derivedKey[32:] + + // verify mac + hm := hmac.New(sha256.New, keyM) + hm.Write(in[:len(in)-sha256.Size]) // everything is hashed + expectedMAC := hm.Sum(nil) + if !hmac.Equal(messageMAC, expectedMAC) { + return nil, ErrInvalidMAC + } + + // start decryption + block, err := aes.NewCipher(keyE) + if err != nil { + return nil, err + } + mode := cipher.NewCBCDecrypter(block, iv) + // same length as ciphertext + plaintext := make([]byte, len(in)-offset-sha256.Size) + mode.CryptBlocks(plaintext, in[offset:len(in)-sha256.Size]) + + return removePKCSPadding(plaintext) +} + +// Implement PKCS#7 padding with block size of 16 (AES block size). + +// addPKCSPadding adds padding to a block of data +func addPKCSPadding(src []byte) []byte { + padding := aes.BlockSize - len(src)%aes.BlockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(src, padtext...) +} + +// removePKCSPadding removes padding from data that was added with addPKCSPadding +func removePKCSPadding(src []byte) ([]byte, error) { + length := len(src) + padLength := int(src[length-1]) + if padLength > aes.BlockSize || length < aes.BlockSize { + return nil, errInvalidPadding + } + + return src[:length-padLength], nil +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/ciphering_test.go b/vendor/github.com/btcsuite/btcd/btcec/ciphering_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0160f0621bd0fffa4f419f19cc8aeddfa57cb294 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/ciphering_test.go @@ -0,0 +1,176 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec_test + +import ( + "bytes" + "encoding/hex" + "testing" + + "github.com/btcsuite/btcd/btcec" +) + +func TestGenerateSharedSecret(t *testing.T) { + privKey1, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("private key generation error: %s", err) + return + } + privKey2, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("private key generation error: %s", err) + return + } + + secret1 := btcec.GenerateSharedSecret(privKey1, privKey2.PubKey()) + secret2 := btcec.GenerateSharedSecret(privKey2, privKey1.PubKey()) + + if !bytes.Equal(secret1, secret2) { + t.Errorf("ECDH failed, secrets mismatch - first: %x, second: %x", + secret1, secret2) + } +} + +// Test 1: Encryption and decryption +func TestCipheringBasic(t *testing.T) { + privkey, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Fatal("failed to generate private key") + } + + in := []byte("Hey there dude. How are you doing? This is a test.") + + out, err := btcec.Encrypt(privkey.PubKey(), in) + if err != nil { + t.Fatal("failed to encrypt:", err) + } + + dec, err := btcec.Decrypt(privkey, out) + if err != nil { + t.Fatal("failed to decrypt:", err) + } + + if !bytes.Equal(in, dec) { + t.Error("decrypted data doesn't match original") + } +} + +// Test 2: Byte compatibility with Pyelliptic +func TestCiphering(t *testing.T) { + pb, _ := hex.DecodeString("fe38240982f313ae5afb3e904fb8215fb11af1200592b" + + "fca26c96c4738e4bf8f") + privkey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pb) + + in := []byte("This is just a test.") + out, _ := hex.DecodeString("b0d66e5adaa5ed4e2f0ca68e17b8f2fc02ca002009e3" + + "3487e7fa4ab505cf34d98f131be7bd258391588ca7804acb30251e71a04e0020ecf" + + "df0f84608f8add82d7353af780fbb28868c713b7813eb4d4e61f7b75d7534dd9856" + + "9b0ba77cf14348fcff80fee10e11981f1b4be372d93923e9178972f69937ec850ed" + + "6c3f11ff572ddd5b2bedf9f9c0b327c54da02a28fcdce1f8369ffec") + + dec, err := btcec.Decrypt(privkey, out) + if err != nil { + t.Fatal("failed to decrypt:", err) + } + + if !bytes.Equal(in, dec) { + t.Error("decrypted data doesn't match original") + } +} + +func TestCipheringErrors(t *testing.T) { + privkey, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Fatal("failed to generate private key") + } + + tests1 := []struct { + ciphertext []byte // input ciphertext + }{ + {bytes.Repeat([]byte{0x00}, 133)}, // errInputTooShort + {bytes.Repeat([]byte{0x00}, 134)}, // errUnsupportedCurve + {bytes.Repeat([]byte{0x02, 0xCA}, 134)}, // errInvalidXLength + {bytes.Repeat([]byte{0x02, 0xCA, 0x00, 0x20}, 134)}, // errInvalidYLength + {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IV + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0xCA, 0x00, 0x20, // curve and X length + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // X + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, // Y length + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Y + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ciphertext + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MAC + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }}, // invalid pubkey + {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IV + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0xCA, 0x00, 0x20, // curve and X length + 0x11, 0x5C, 0x42, 0xE7, 0x57, 0xB2, 0xEF, 0xB7, // X + 0x67, 0x1C, 0x57, 0x85, 0x30, 0xEC, 0x19, 0x1A, + 0x13, 0x59, 0x38, 0x1E, 0x6A, 0x71, 0x12, 0x7A, + 0x9D, 0x37, 0xC4, 0x86, 0xFD, 0x30, 0xDA, 0xE5, + 0x00, 0x20, // Y length + 0x7E, 0x76, 0xDC, 0x58, 0xF6, 0x93, 0xBD, 0x7E, // Y + 0x70, 0x10, 0x35, 0x8C, 0xE6, 0xB1, 0x65, 0xE4, + 0x83, 0xA2, 0x92, 0x10, 0x10, 0xDB, 0x67, 0xAC, + 0x11, 0xB1, 0xB5, 0x1B, 0x65, 0x19, 0x53, 0xD2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ciphertext + // padding not aligned to 16 bytes + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MAC + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }}, // errInvalidPadding + {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IV + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0xCA, 0x00, 0x20, // curve and X length + 0x11, 0x5C, 0x42, 0xE7, 0x57, 0xB2, 0xEF, 0xB7, // X + 0x67, 0x1C, 0x57, 0x85, 0x30, 0xEC, 0x19, 0x1A, + 0x13, 0x59, 0x38, 0x1E, 0x6A, 0x71, 0x12, 0x7A, + 0x9D, 0x37, 0xC4, 0x86, 0xFD, 0x30, 0xDA, 0xE5, + 0x00, 0x20, // Y length + 0x7E, 0x76, 0xDC, 0x58, 0xF6, 0x93, 0xBD, 0x7E, // Y + 0x70, 0x10, 0x35, 0x8C, 0xE6, 0xB1, 0x65, 0xE4, + 0x83, 0xA2, 0x92, 0x10, 0x10, 0xDB, 0x67, 0xAC, + 0x11, 0xB1, 0xB5, 0x1B, 0x65, 0x19, 0x53, 0xD2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ciphertext + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MAC + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }}, // ErrInvalidMAC + } + + for i, test := range tests1 { + _, err = btcec.Decrypt(privkey, test.ciphertext) + if err == nil { + t.Errorf("Decrypt #%d did not get error", i) + } + } + + // test error from removePKCSPadding + tests2 := []struct { + in []byte // input data + }{ + {bytes.Repeat([]byte{0x11}, 17)}, + {bytes.Repeat([]byte{0x07}, 15)}, + } + for i, test := range tests2 { + _, err = btcec.TstRemovePKCSPadding(test.in) + if err == nil { + t.Errorf("removePKCSPadding #%d did not get error", i) + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/doc.go b/vendor/github.com/btcsuite/btcd/btcec/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..fa8346ab0f917f935af034733bc0f726c2aa19f1 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/doc.go @@ -0,0 +1,21 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package btcec implements support for the elliptic curves needed for bitcoin. + +Bitcoin uses elliptic curve cryptography using koblitz curves +(specifically secp256k1) for cryptographic functions. See +http://www.secg.org/collateral/sec2_final.pdf for details on the +standard. + +This package provides the data structures and functions implementing the +crypto/elliptic Curve interface in order to permit using these curves +with the standard crypto/ecdsa package provided with go. Helper +functionality is provided to parse signatures and public keys from +standard formats. It was designed for use with btcd, but should be +general enough for other uses of elliptic curve crypto. It was originally based +on some initial work by ThePiachu, but has significantly diverged since then. +*/ +package btcec diff --git a/vendor/github.com/btcsuite/btcd/btcec/example_test.go b/vendor/github.com/btcsuite/btcd/btcec/example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..00fe7f84df2d5bc433096b739893439a2555f95a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/example_test.go @@ -0,0 +1,168 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec_test + +import ( + "encoding/hex" + "fmt" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/wire" +) + +// This example demonstrates signing a message with a secp256k1 private key that +// is first parsed form raw bytes and serializing the generated signature. +func Example_signMessage() { + // Decode a hex-encoded private key. + pkBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2d4f87" + + "20ee63e502ee2869afab7de234b80c") + if err != nil { + fmt.Println(err) + return + } + privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) + + // Sign a message using the private key. + message := "test message" + messageHash := wire.DoubleSha256([]byte(message)) + signature, err := privKey.Sign(messageHash) + if err != nil { + fmt.Println(err) + return + } + + // Serialize and display the signature. + fmt.Printf("Serialized Signature: %x\n", signature.Serialize()) + + // Verify the signature for the message using the public key. + verified := signature.Verify(messageHash, pubKey) + fmt.Printf("Signature Verified? %v\n", verified) + + // Output: + // Serialized Signature: 304402201008e236fa8cd0f25df4482dddbb622e8a8b26ef0ba731719458de3ccd93805b022032f8ebe514ba5f672466eba334639282616bb3c2f0ab09998037513d1f9e3d6d + // Signature Verified? true +} + +// This example demonstrates verifying a secp256k1 signature against a public +// key that is first parsed from raw bytes. The signature is also parsed from +// raw bytes. +func Example_verifySignature() { + // Decode hex-encoded serialized public key. + pubKeyBytes, err := hex.DecodeString("02a673638cb9587cb68ea08dbef685c" + + "6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5") + if err != nil { + fmt.Println(err) + return + } + pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256()) + if err != nil { + fmt.Println(err) + return + } + + // Decode hex-encoded serialized signature. + sigBytes, err := hex.DecodeString("30450220090ebfb3690a0ff115bb1b38b" + + "8b323a667b7653454f1bccb06d4bbdca42c2079022100ec95778b51e707" + + "1cb1205f8bde9af6592fc978b0452dafe599481c46d6b2e479") + + if err != nil { + fmt.Println(err) + return + } + signature, err := btcec.ParseSignature(sigBytes, btcec.S256()) + if err != nil { + fmt.Println(err) + return + } + + // Verify the signature for the message using the public key. + message := "test message" + messageHash := wire.DoubleSha256([]byte(message)) + verified := signature.Verify(messageHash, pubKey) + fmt.Println("Signature Verified?", verified) + + // Output: + // Signature Verified? true +} + +// This example demonstrates encrypting a message for a public key that is first +// parsed from raw bytes, then decrypting it using the corresponding private key. +func Example_encryptMessage() { + // Decode the hex-encoded pubkey of the recipient. + pubKeyBytes, err := hex.DecodeString("04115c42e757b2efb7671c578530ec191a1" + + "359381e6a71127a9d37c486fd30dae57e76dc58f693bd7e7010358ce6b165e483a29" + + "21010db67ac11b1b51b651953d2") // uncompressed pubkey + if err != nil { + fmt.Println(err) + return + } + pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256()) + if err != nil { + fmt.Println(err) + return + } + + // Encrypt a message decryptable by the private key corresponding to pubKey + message := "test message" + ciphertext, err := btcec.Encrypt(pubKey, []byte(message)) + if err != nil { + fmt.Println(err) + return + } + + // Decode the hex-encoded private key. + pkBytes, err := hex.DecodeString("a11b0a4e1a132305652ee7a8eb7848f6ad" + + "5ea381e3ce20a2c086a2e388230811") + if err != nil { + fmt.Println(err) + return + } + // note that we already have corresponding pubKey + privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) + + // Try decrypting and verify if it's the same message. + plaintext, err := btcec.Decrypt(privKey, ciphertext) + if err != nil { + fmt.Println(err) + return + } + + fmt.Println(string(plaintext)) + + // Output: + // test message +} + +// This example demonstrates decrypting a message using a private key that is +// first parsed from raw bytes. +func Example_decryptMessage() { + // Decode the hex-encoded private key. + pkBytes, err := hex.DecodeString("a11b0a4e1a132305652ee7a8eb7848f6ad" + + "5ea381e3ce20a2c086a2e388230811") + if err != nil { + fmt.Println(err) + return + } + + privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) + + ciphertext, err := hex.DecodeString("35f644fbfb208bc71e57684c3c8b437402ca" + + "002047a2f1b38aa1a8f1d5121778378414f708fe13ebf7b4a7bb74407288c1958969" + + "00207cf4ac6057406e40f79961c973309a892732ae7a74ee96cd89823913b8b8d650" + + "a44166dc61ea1c419d47077b748a9c06b8d57af72deb2819d98a9d503efc59fc8307" + + "d14174f8b83354fac3ff56075162") + + // Try decrypting the message. + plaintext, err := btcec.Decrypt(privKey, ciphertext) + if err != nil { + fmt.Println(err) + return + } + + fmt.Println(string(plaintext)) + + // Output: + // test message +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/field.go b/vendor/github.com/btcsuite/btcd/btcec/field.go new file mode 100644 index 0000000000000000000000000000000000000000..2a74878ba537bb72233e6579e06767d57fdaa24e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/field.go @@ -0,0 +1,1263 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Copyright (c) 2013-2014 Dave Collins +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +// References: +// [HAC]: Handbook of Applied Cryptography Menezes, van Oorschot, Vanstone. +// http://cacr.uwaterloo.ca/hac/ + +// All elliptic curve operations for secp256k1 are done in a finite field +// characterized by a 256-bit prime. Given this precision is larger than the +// biggest available native type, obviously some form of bignum math is needed. +// This package implements specialized fixed-precision field arithmetic rather +// than relying on an arbitrary-precision arithmetic package such as math/big +// for dealing with the field math since the size is known. As a result, rather +// large performance gains are achieved by taking advantage of many +// optimizations not available to arbitrary-precision arithmetic and generic +// modular arithmetic algorithms. +// +// There are various ways to internally represent each finite field element. +// For example, the most obvious representation would be to use an array of 4 +// uint64s (64 bits * 4 = 256 bits). However, that representation suffers from +// a couple of issues. First, there is no native Go type large enough to handle +// the intermediate results while adding or multiplying two 64-bit numbers, and +// second there is no space left for overflows when performing the intermediate +// arithmetic between each array element which would lead to expensive carry +// propagation. +// +// Given the above, this implementation represents the the field elements as +// 10 uint32s with each word (array entry) treated as base 2^26. This was +// chosen for the following reasons: +// 1) Most systems at the current time are 64-bit (or at least have 64-bit +// registers available for specialized purposes such as MMX) so the +// intermediate results can typically be done using a native register (and +// using uint64s to avoid the need for additional half-word arithmetic) +// 2) In order to allow addition of the internal words without having to +// propagate the the carry, the max normalized value for each register must +// be less than the number of bits available in the register +// 3) Since we're dealing with 32-bit values, 64-bits of overflow is a +// reasonable choice for #2 +// 4) Given the need for 256-bits of precision and the properties stated in #1, +// #2, and #3, the representation which best accommodates this is 10 uint32s +// with base 2^26 (26 bits * 10 = 260 bits, so the final word only needs 22 +// bits) which leaves the desired 64 bits (32 * 10 = 320, 320 - 256 = 64) for +// overflow +// +// Since it is so important that the field arithmetic is extremely fast for +// high performance crypto, this package does not perform any validation where +// it ordinarily would. For example, some functions only give the correct +// result is the field is normalized and there is no checking to ensure it is. +// While I typically prefer to ensure all state and input is valid for most +// packages, this code is really only used internally and every extra check +// counts. + +import ( + "encoding/hex" +) + +// Constants used to make the code more readable. +const ( + twoBitsMask = 0x3 + fourBitsMask = 0xf + sixBitsMask = 0x3f + eightBitsMask = 0xff +) + +// Constants related to the field representation. +const ( + // fieldWords is the number of words used to internally represent the + // 256-bit value. + fieldWords = 10 + + // fieldBase is the exponent used to form the numeric base of each word. + // 2^(fieldBase*i) where i is the word position. + fieldBase = 26 + + // fieldOverflowBits is the minimum number of "overflow" bits for each + // word in the field value. + fieldOverflowBits = 32 - fieldBase + + // fieldBaseMask is the mask for the bits in each word needed to + // represent the numeric base of each word (except the most significant + // word). + fieldBaseMask = (1 << fieldBase) - 1 + + // fieldMSBBits is the number of bits in the most significant word used + // to represent the value. + fieldMSBBits = 256 - (fieldBase * (fieldWords - 1)) + + // fieldMSBMask is the mask for the bits in the most significant word + // needed to represent the value. + fieldMSBMask = (1 << fieldMSBBits) - 1 + + // fieldPrimeWordZero is word zero of the secp256k1 prime in the + // internal field representation. It is used during modular reduction + // and negation. + fieldPrimeWordZero = 0x3fffc2f + + // fieldPrimeWordOne is word one of the secp256k1 prime in the + // internal field representation. It is used during modular reduction + // and negation. + fieldPrimeWordOne = 0x3ffffbf +) + +// fieldVal implements optimized fixed-precision arithmetic over the +// secp256k1 finite field. This means all arithmetic is performed modulo +// 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f. It +// represents each 256-bit value as 10 32-bit integers in base 2^26. This +// provides 6 bits of overflow in each word (10 bits in the most significant +// word) for a total of 64 bits of overflow (9*6 + 10 = 64). It only implements +// the arithmetic needed for elliptic curve operations. +// +// The following depicts the internal representation: +// ----------------------------------------------------------------- +// | n[9] | n[8] | ... | n[0] | +// | 32 bits available | 32 bits available | ... | 32 bits available | +// | 22 bits for value | 26 bits for value | ... | 26 bits for value | +// | 10 bits overflow | 6 bits overflow | ... | 6 bits overflow | +// | Mult: 2^(26*9) | Mult: 2^(26*8) | ... | Mult: 2^(26*0) | +// ----------------------------------------------------------------- +// +// For example, consider the number 2^49 + 1. It would be represented as: +// n[0] = 1 +// n[1] = 2^23 +// n[2..9] = 0 +// +// The full 256-bit value is then calculated by looping i from 9..0 and +// doing sum(n[i] * 2^(26i)) like so: +// n[9] * 2^(26*9) = 0 * 2^234 = 0 +// n[8] * 2^(26*8) = 0 * 2^208 = 0 +// ... +// n[1] * 2^(26*1) = 2^23 * 2^26 = 2^49 +// n[0] * 2^(26*0) = 1 * 2^0 = 1 +// Sum: 0 + 0 + ... + 2^49 + 1 = 2^49 + 1 +type fieldVal struct { + n [10]uint32 +} + +// String returns the field value as a human-readable hex string. +func (f fieldVal) String() string { + t := new(fieldVal).Set(&f).Normalize() + return hex.EncodeToString(t.Bytes()[:]) +} + +// Zero sets the field value to zero. A newly created field value is already +// set to zero. This function can be useful to clear an existing field value +// for reuse. +func (f *fieldVal) Zero() { + f.n[0] = 0 + f.n[1] = 0 + f.n[2] = 0 + f.n[3] = 0 + f.n[4] = 0 + f.n[5] = 0 + f.n[6] = 0 + f.n[7] = 0 + f.n[8] = 0 + f.n[9] = 0 +} + +// Set sets the field value equal to the passed value. +// +// The field value is returned to support chaining. This enables syntax like: +// f := new(fieldVal).Set(f2).Add(1) so that f = f2 + 1 where f2 is not +// modified. +func (f *fieldVal) Set(val *fieldVal) *fieldVal { + *f = *val + return f +} + +// SetInt sets the field value to the passed integer. This is a convenience +// function since it is fairly common to perform some arithemetic with small +// native integers. +// +// The field value is returned to support chaining. This enables syntax such +// as f := new(fieldVal).SetInt(2).Mul(f2) so that f = 2 * f2. +func (f *fieldVal) SetInt(ui uint) *fieldVal { + f.Zero() + f.n[0] = uint32(ui) + return f +} + +// SetBytes packs the passed 32-byte big-endian value into the internal field +// value representation. +// +// The field value is returned to support chaining. This enables syntax like: +// f := new(fieldVal).SetBytes(byteArray).Mul(f2) so that f = ba * f2. +func (f *fieldVal) SetBytes(b *[32]byte) *fieldVal { + // Pack the 256 total bits across the 10 uint32 words with a max of + // 26-bits per word. This could be done with a couple of for loops, + // but this unrolled version is significantly faster. Benchmarks show + // this is about 34 times faster than the variant which uses loops. + f.n[0] = uint32(b[31]) | uint32(b[30])<<8 | uint32(b[29])<<16 | + (uint32(b[28])&twoBitsMask)<<24 + f.n[1] = uint32(b[28])>>2 | uint32(b[27])<<6 | uint32(b[26])<<14 | + (uint32(b[25])&fourBitsMask)<<22 + f.n[2] = uint32(b[25])>>4 | uint32(b[24])<<4 | uint32(b[23])<<12 | + (uint32(b[22])&sixBitsMask)<<20 + f.n[3] = uint32(b[22])>>6 | uint32(b[21])<<2 | uint32(b[20])<<10 | + uint32(b[19])<<18 + f.n[4] = uint32(b[18]) | uint32(b[17])<<8 | uint32(b[16])<<16 | + (uint32(b[15])&twoBitsMask)<<24 + f.n[5] = uint32(b[15])>>2 | uint32(b[14])<<6 | uint32(b[13])<<14 | + (uint32(b[12])&fourBitsMask)<<22 + f.n[6] = uint32(b[12])>>4 | uint32(b[11])<<4 | uint32(b[10])<<12 | + (uint32(b[9])&sixBitsMask)<<20 + f.n[7] = uint32(b[9])>>6 | uint32(b[8])<<2 | uint32(b[7])<<10 | + uint32(b[6])<<18 + f.n[8] = uint32(b[5]) | uint32(b[4])<<8 | uint32(b[3])<<16 | + (uint32(b[2])&twoBitsMask)<<24 + f.n[9] = uint32(b[2])>>2 | uint32(b[1])<<6 | uint32(b[0])<<14 + return f +} + +// SetByteSlice packs the passed big-endian value into the internal field value +// representation. Only the first 32-bytes are used. As a result, it is up to +// the caller to ensure numbers of the appropriate size are used or the value +// will be truncated. +// +// The field value is returned to support chaining. This enables syntax like: +// f := new(fieldVal).SetByteSlice(byteSlice) +func (f *fieldVal) SetByteSlice(b []byte) *fieldVal { + var b32 [32]byte + for i := 0; i < len(b); i++ { + if i < 32 { + b32[i+(32-len(b))] = b[i] + } + } + return f.SetBytes(&b32) +} + +// SetHex decodes the passed big-endian hex string into the internal field value +// representation. Only the first 32-bytes are used. +// +// The field value is returned to support chaining. This enables syntax like: +// f := new(fieldVal).SetHex("0abc").Add(1) so that f = 0x0abc + 1 +func (f *fieldVal) SetHex(hexString string) *fieldVal { + if len(hexString)%2 != 0 { + hexString = "0" + hexString + } + bytes, _ := hex.DecodeString(hexString) + return f.SetByteSlice(bytes) +} + +// Normalize normalizes the internal field words into the desired range and +// performs fast modular reduction over the secp256k1 prime by making use of the +// special form of the prime. +func (f *fieldVal) Normalize() *fieldVal { + // The field representation leaves 6 bits of overflow in each + // word so intermediate calculations can be performed without needing + // to propagate the carry to each higher word during the calculations. + // In order to normalize, first we need to "compact" the full 256-bit + // value to the right and treat the additional 64 leftmost bits as + // the magnitude. + m := f.n[0] + t0 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[1] + t1 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[2] + t2 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[3] + t3 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[4] + t4 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[5] + t5 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[6] + t6 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[7] + t7 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[8] + t8 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[9] + t9 := m & fieldMSBMask + m = m >> fieldMSBBits + + // At this point, if the magnitude is greater than 0, the overall value + // is greater than the max possible 256-bit value. In particular, it is + // "how many times larger" than the max value it is. Since this field + // is doing arithmetic modulo the secp256k1 prime, we need to perform + // modular reduction over the prime. + // + // Per [HAC] section 14.3.4: Reduction method of moduli of special form, + // when the modulus is of the special form m = b^t - c, highly efficient + // reduction can be achieved. + // + // The secp256k1 prime is equivalent to 2^256 - 4294968273, so it fits + // this criteria. + // + // 4294968273 in field representation (base 2^26) is: + // n[0] = 977 + // n[1] = 64 + // That is to say (2^26 * 64) + 977 = 4294968273 + // + // The algorithm presented in the referenced section typically repeats + // until the quotient is zero. However, due to our field representation + // we already know at least how many times we would need to repeat as + // it's the value currently in m. Thus we can simply multiply the + // magnitude by the field representation of the prime and do a single + // iteration. Notice that nothing will be changed when the magnitude is + // zero, so we could skip this in that case, however always running + // regardless allows it to run in constant time. + r := t0 + m*977 + t0 = r & fieldBaseMask + r = (r >> fieldBase) + t1 + m*64 + t1 = r & fieldBaseMask + r = (r >> fieldBase) + t2 + t2 = r & fieldBaseMask + r = (r >> fieldBase) + t3 + t3 = r & fieldBaseMask + r = (r >> fieldBase) + t4 + t4 = r & fieldBaseMask + r = (r >> fieldBase) + t5 + t5 = r & fieldBaseMask + r = (r >> fieldBase) + t6 + t6 = r & fieldBaseMask + r = (r >> fieldBase) + t7 + t7 = r & fieldBaseMask + r = (r >> fieldBase) + t8 + t8 = r & fieldBaseMask + r = (r >> fieldBase) + t9 + t9 = r & fieldMSBMask + + // At this point, the result will be in the range 0 <= result <= + // prime + (2^64 - c). Therefore, one more subtraction of the prime + // might be needed if the current result is greater than or equal to the + // prime. The following does the final reduction in constant time. + // Note that the if/else here intentionally does the bitwise OR with + // zero even though it won't change the value to ensure constant time + // between the branches. + var mask int32 + if t0 < fieldPrimeWordZero { + mask |= -1 + } else { + mask |= 0 + } + if t1 < fieldPrimeWordOne { + mask |= -1 + } else { + mask |= 0 + } + if t2 < fieldBaseMask { + mask |= -1 + } else { + mask |= 0 + } + if t3 < fieldBaseMask { + mask |= -1 + } else { + mask |= 0 + } + if t4 < fieldBaseMask { + mask |= -1 + } else { + mask |= 0 + } + if t5 < fieldBaseMask { + mask |= -1 + } else { + mask |= 0 + } + if t6 < fieldBaseMask { + mask |= -1 + } else { + mask |= 0 + } + if t7 < fieldBaseMask { + mask |= -1 + } else { + mask |= 0 + } + if t8 < fieldBaseMask { + mask |= -1 + } else { + mask |= 0 + } + if t9 < fieldMSBMask { + mask |= -1 + } else { + mask |= 0 + } + t0 = t0 - uint32(^mask&fieldPrimeWordZero) + t1 = t1 - uint32(^mask&fieldPrimeWordOne) + t2 = t2 & uint32(mask) + t3 = t3 & uint32(mask) + t4 = t4 & uint32(mask) + t5 = t5 & uint32(mask) + t6 = t6 & uint32(mask) + t7 = t7 & uint32(mask) + t8 = t8 & uint32(mask) + t9 = t9 & uint32(mask) + + // Finally, set the normalized and reduced words. + f.n[0] = t0 + f.n[1] = t1 + f.n[2] = t2 + f.n[3] = t3 + f.n[4] = t4 + f.n[5] = t5 + f.n[6] = t6 + f.n[7] = t7 + f.n[8] = t8 + f.n[9] = t9 + return f +} + +// PutBytes unpacks the field value to a 32-byte big-endian value using the +// passed byte array. There is a similar function, Bytes, which unpacks the +// field value into a new array and returns that. This version is provided +// since it can be useful to cut down on the number of allocations by allowing +// the caller to reuse a buffer. +// +// The field value must be normalized for this function to return the correct +// result. +func (f *fieldVal) PutBytes(b *[32]byte) { + // Unpack the 256 total bits from the 10 uint32 words with a max of + // 26-bits per word. This could be done with a couple of for loops, + // but this unrolled version is a bit faster. Benchmarks show this is + // about 10 times faster than the variant which uses loops. + b[31] = byte(f.n[0] & eightBitsMask) + b[30] = byte((f.n[0] >> 8) & eightBitsMask) + b[29] = byte((f.n[0] >> 16) & eightBitsMask) + b[28] = byte((f.n[0]>>24)&twoBitsMask | (f.n[1]&sixBitsMask)<<2) + b[27] = byte((f.n[1] >> 6) & eightBitsMask) + b[26] = byte((f.n[1] >> 14) & eightBitsMask) + b[25] = byte((f.n[1]>>22)&fourBitsMask | (f.n[2]&fourBitsMask)<<4) + b[24] = byte((f.n[2] >> 4) & eightBitsMask) + b[23] = byte((f.n[2] >> 12) & eightBitsMask) + b[22] = byte((f.n[2]>>20)&sixBitsMask | (f.n[3]&twoBitsMask)<<6) + b[21] = byte((f.n[3] >> 2) & eightBitsMask) + b[20] = byte((f.n[3] >> 10) & eightBitsMask) + b[19] = byte((f.n[3] >> 18) & eightBitsMask) + b[18] = byte(f.n[4] & eightBitsMask) + b[17] = byte((f.n[4] >> 8) & eightBitsMask) + b[16] = byte((f.n[4] >> 16) & eightBitsMask) + b[15] = byte((f.n[4]>>24)&twoBitsMask | (f.n[5]&sixBitsMask)<<2) + b[14] = byte((f.n[5] >> 6) & eightBitsMask) + b[13] = byte((f.n[5] >> 14) & eightBitsMask) + b[12] = byte((f.n[5]>>22)&fourBitsMask | (f.n[6]&fourBitsMask)<<4) + b[11] = byte((f.n[6] >> 4) & eightBitsMask) + b[10] = byte((f.n[6] >> 12) & eightBitsMask) + b[9] = byte((f.n[6]>>20)&sixBitsMask | (f.n[7]&twoBitsMask)<<6) + b[8] = byte((f.n[7] >> 2) & eightBitsMask) + b[7] = byte((f.n[7] >> 10) & eightBitsMask) + b[6] = byte((f.n[7] >> 18) & eightBitsMask) + b[5] = byte(f.n[8] & eightBitsMask) + b[4] = byte((f.n[8] >> 8) & eightBitsMask) + b[3] = byte((f.n[8] >> 16) & eightBitsMask) + b[2] = byte((f.n[8]>>24)&twoBitsMask | (f.n[9]&sixBitsMask)<<2) + b[1] = byte((f.n[9] >> 6) & eightBitsMask) + b[0] = byte((f.n[9] >> 14) & eightBitsMask) +} + +// Bytes unpacks the field value to a 32-byte big-endian value. See PutBytes +// for a variant that allows the a buffer to be passed which can be useful to +// to cut down on the number of allocations by allowing the caller to reuse a +// buffer. +// +// The field value must be normalized for this function to return correct +// result. +func (f *fieldVal) Bytes() *[32]byte { + b := new([32]byte) + f.PutBytes(b) + return b +} + +// IsZero returns whether or not the field value is equal to zero. +func (f *fieldVal) IsZero() bool { + // The value can only be zero if no bits are set in any of the words. + // This is a constant time implementation. + bits := f.n[0] | f.n[1] | f.n[2] | f.n[3] | f.n[4] | + f.n[5] | f.n[6] | f.n[7] | f.n[8] | f.n[9] + + return bits == 0 +} + +// IsOdd returns whether or not the field value is an odd number. +// +// The field value must be normalized for this function to return correct +// result. +func (f *fieldVal) IsOdd() bool { + // Only odd numbers have the bottom bit set. + return f.n[0]&1 == 1 +} + +// Equals returns whether or not the two field values are the same. Both +// field values being compared must be normalized for this function to return +// the correct result. +func (f *fieldVal) Equals(val *fieldVal) bool { + // Xor only sets bits when they are different, so the two field values + // can only be the same if no bits are set after xoring each word. + // This is a constant time implementation. + bits := (f.n[0] ^ val.n[0]) | (f.n[1] ^ val.n[1]) | (f.n[2] ^ val.n[2]) | + (f.n[3] ^ val.n[3]) | (f.n[4] ^ val.n[4]) | (f.n[5] ^ val.n[5]) | + (f.n[6] ^ val.n[6]) | (f.n[7] ^ val.n[7]) | (f.n[8] ^ val.n[8]) | + (f.n[9] ^ val.n[9]) + + return bits == 0 +} + +// NegateVal negates the passed value and stores the result in f. The caller +// must provide the magnitude of the passed value for a correct result. +// +// The field value is returned to support chaining. This enables syntax like: +// f.NegateVal(f2).AddInt(1) so that f = -f2 + 1. +func (f *fieldVal) NegateVal(val *fieldVal, magnitude uint32) *fieldVal { + // Negation in the field is just the prime minus the value. However, + // in order to allow negation against a field value without having to + // normalize/reduce it first, multiply by the magnitude (that is how + // "far" away it is from the normalized value) to adjust. Also, since + // negating a value pushes it one more order of magnitude away from the + // normalized range, add 1 to compensate. + // + // For some intuition here, imagine you're performing mod 12 arithmetic + // (picture a clock) and you are negating the number 7. So you start at + // 12 (which is of course 0 under mod 12) and count backwards (left on + // the clock) 7 times to arrive at 5. Notice this is just 12-7 = 5. + // Now, assume you're starting with 19, which is a number that is + // already larger than the modulus and congruent to 7 (mod 12). When a + // value is already in the desired range, its magnitude is 1. Since 19 + // is an additional "step", its magnitude (mod 12) is 2. Since any + // multiple of the modulus is conguent to zero (mod m), the answer can + // be shortcut by simply mulplying the magnitude by the modulus and + // subtracting. Keeping with the example, this would be (2*12)-19 = 5. + f.n[0] = (magnitude+1)*fieldPrimeWordZero - val.n[0] + f.n[1] = (magnitude+1)*fieldPrimeWordOne - val.n[1] + f.n[2] = (magnitude+1)*fieldBaseMask - val.n[2] + f.n[3] = (magnitude+1)*fieldBaseMask - val.n[3] + f.n[4] = (magnitude+1)*fieldBaseMask - val.n[4] + f.n[5] = (magnitude+1)*fieldBaseMask - val.n[5] + f.n[6] = (magnitude+1)*fieldBaseMask - val.n[6] + f.n[7] = (magnitude+1)*fieldBaseMask - val.n[7] + f.n[8] = (magnitude+1)*fieldBaseMask - val.n[8] + f.n[9] = (magnitude+1)*fieldMSBMask - val.n[9] + + return f +} + +// Negate negates the field value. The existing field value is modified. The +// caller must provide the magnitude of the field value for a correct result. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Negate().AddInt(1) so that f = -f + 1. +func (f *fieldVal) Negate(magnitude uint32) *fieldVal { + return f.NegateVal(f, magnitude) +} + +// AddInt adds the passed integer to the existing field value and stores the +// result in f. This is a convenience function since it is fairly common to +// perform some arithemetic with small native integers. +// +// The field value is returned to support chaining. This enables syntax like: +// f.AddInt(1).Add(f2) so that f = f + 1 + f2. +func (f *fieldVal) AddInt(ui uint) *fieldVal { + // Since the field representation intentionally provides overflow bits, + // it's ok to use carryless addition as the carry bit is safely part of + // the word and will be normalized out. + f.n[0] += uint32(ui) + + return f +} + +// Add adds the passed value to the existing field value and stores the result +// in f. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Add(f2).AddInt(1) so that f = f + f2 + 1. +func (f *fieldVal) Add(val *fieldVal) *fieldVal { + // Since the field representation intentionally provides overflow bits, + // it's ok to use carryless addition as the carry bit is safely part of + // each word and will be normalized out. This could obviously be done + // in a loop, but the unrolled version is faster. + f.n[0] += val.n[0] + f.n[1] += val.n[1] + f.n[2] += val.n[2] + f.n[3] += val.n[3] + f.n[4] += val.n[4] + f.n[5] += val.n[5] + f.n[6] += val.n[6] + f.n[7] += val.n[7] + f.n[8] += val.n[8] + f.n[9] += val.n[9] + + return f +} + +// Add2 adds the passed two field values together and stores the result in f. +// +// The field value is returned to support chaining. This enables syntax like: +// f3.Add2(f, f2).AddInt(1) so that f3 = f + f2 + 1. +func (f *fieldVal) Add2(val *fieldVal, val2 *fieldVal) *fieldVal { + // Since the field representation intentionally provides overflow bits, + // it's ok to use carryless addition as the carry bit is safely part of + // each word and will be normalized out. This could obviously be done + // in a loop, but the unrolled version is faster. + f.n[0] = val.n[0] + val2.n[0] + f.n[1] = val.n[1] + val2.n[1] + f.n[2] = val.n[2] + val2.n[2] + f.n[3] = val.n[3] + val2.n[3] + f.n[4] = val.n[4] + val2.n[4] + f.n[5] = val.n[5] + val2.n[5] + f.n[6] = val.n[6] + val2.n[6] + f.n[7] = val.n[7] + val2.n[7] + f.n[8] = val.n[8] + val2.n[8] + f.n[9] = val.n[9] + val2.n[9] + + return f +} + +// MulInt multiplies the field value by the passed int and stores the result in +// f. Note that this function can overflow if multiplying the value by any of +// the individual words exceeds a max uint32. Therefore it is important that +// the caller ensures no overflows will occur before using this function. +// +// The field value is returned to support chaining. This enables syntax like: +// f.MulInt(2).Add(f2) so that f = 2 * f + f2. +func (f *fieldVal) MulInt(val uint) *fieldVal { + // Since each word of the field representation can hold up to + // fieldOverflowBits extra bits which will be normalized out, it's safe + // to multiply each word without using a larger type or carry + // propagation so long as the values won't overflow a uint32. This + // could obviously be done in a loop, but the unrolled version is + // faster. + ui := uint32(val) + f.n[0] *= ui + f.n[1] *= ui + f.n[2] *= ui + f.n[3] *= ui + f.n[4] *= ui + f.n[5] *= ui + f.n[6] *= ui + f.n[7] *= ui + f.n[8] *= ui + f.n[9] *= ui + + return f +} + +// Mul multiplies the passed value to the existing field value and stores the +// result in f. Note that this function can overflow if multiplying any +// of the individual words exceeds a max uint32. In practice, this means the +// magnitude of either value involved in the multiplication must be a max of +// 8. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Mul(f2).AddInt(1) so that f = (f * f2) + 1. +func (f *fieldVal) Mul(val *fieldVal) *fieldVal { + return f.Mul2(f, val) +} + +// Mul2 multiplies the passed two field values together and stores the result +// result in f. Note that this function can overflow if multiplying any of +// the individual words exceeds a max uint32. In practice, this means the +// magnitude of either value involved in the multiplication must be a max of +// 8. +// +// The field value is returned to support chaining. This enables syntax like: +// f3.Mul2(f, f2).AddInt(1) so that f3 = (f * f2) + 1. +func (f *fieldVal) Mul2(val *fieldVal, val2 *fieldVal) *fieldVal { + // This could be done with a couple of for loops and an array to store + // the intermediate terms, but this unrolled version is significantly + // faster. + + // Terms for 2^(fieldBase*0). + m := uint64(val.n[0]) * uint64(val2.n[0]) + t0 := m & fieldBaseMask + + // Terms for 2^(fieldBase*1). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[1]) + + uint64(val.n[1])*uint64(val2.n[0]) + t1 := m & fieldBaseMask + + // Terms for 2^(fieldBase*2). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[2]) + + uint64(val.n[1])*uint64(val2.n[1]) + + uint64(val.n[2])*uint64(val2.n[0]) + t2 := m & fieldBaseMask + + // Terms for 2^(fieldBase*3). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[3]) + + uint64(val.n[1])*uint64(val2.n[2]) + + uint64(val.n[2])*uint64(val2.n[1]) + + uint64(val.n[3])*uint64(val2.n[0]) + t3 := m & fieldBaseMask + + // Terms for 2^(fieldBase*4). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[4]) + + uint64(val.n[1])*uint64(val2.n[3]) + + uint64(val.n[2])*uint64(val2.n[2]) + + uint64(val.n[3])*uint64(val2.n[1]) + + uint64(val.n[4])*uint64(val2.n[0]) + t4 := m & fieldBaseMask + + // Terms for 2^(fieldBase*5). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[5]) + + uint64(val.n[1])*uint64(val2.n[4]) + + uint64(val.n[2])*uint64(val2.n[3]) + + uint64(val.n[3])*uint64(val2.n[2]) + + uint64(val.n[4])*uint64(val2.n[1]) + + uint64(val.n[5])*uint64(val2.n[0]) + t5 := m & fieldBaseMask + + // Terms for 2^(fieldBase*6). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[6]) + + uint64(val.n[1])*uint64(val2.n[5]) + + uint64(val.n[2])*uint64(val2.n[4]) + + uint64(val.n[3])*uint64(val2.n[3]) + + uint64(val.n[4])*uint64(val2.n[2]) + + uint64(val.n[5])*uint64(val2.n[1]) + + uint64(val.n[6])*uint64(val2.n[0]) + t6 := m & fieldBaseMask + + // Terms for 2^(fieldBase*7). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[7]) + + uint64(val.n[1])*uint64(val2.n[6]) + + uint64(val.n[2])*uint64(val2.n[5]) + + uint64(val.n[3])*uint64(val2.n[4]) + + uint64(val.n[4])*uint64(val2.n[3]) + + uint64(val.n[5])*uint64(val2.n[2]) + + uint64(val.n[6])*uint64(val2.n[1]) + + uint64(val.n[7])*uint64(val2.n[0]) + t7 := m & fieldBaseMask + + // Terms for 2^(fieldBase*8). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[8]) + + uint64(val.n[1])*uint64(val2.n[7]) + + uint64(val.n[2])*uint64(val2.n[6]) + + uint64(val.n[3])*uint64(val2.n[5]) + + uint64(val.n[4])*uint64(val2.n[4]) + + uint64(val.n[5])*uint64(val2.n[3]) + + uint64(val.n[6])*uint64(val2.n[2]) + + uint64(val.n[7])*uint64(val2.n[1]) + + uint64(val.n[8])*uint64(val2.n[0]) + t8 := m & fieldBaseMask + + // Terms for 2^(fieldBase*9). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[9]) + + uint64(val.n[1])*uint64(val2.n[8]) + + uint64(val.n[2])*uint64(val2.n[7]) + + uint64(val.n[3])*uint64(val2.n[6]) + + uint64(val.n[4])*uint64(val2.n[5]) + + uint64(val.n[5])*uint64(val2.n[4]) + + uint64(val.n[6])*uint64(val2.n[3]) + + uint64(val.n[7])*uint64(val2.n[2]) + + uint64(val.n[8])*uint64(val2.n[1]) + + uint64(val.n[9])*uint64(val2.n[0]) + t9 := m & fieldBaseMask + + // Terms for 2^(fieldBase*10). + m = (m >> fieldBase) + + uint64(val.n[1])*uint64(val2.n[9]) + + uint64(val.n[2])*uint64(val2.n[8]) + + uint64(val.n[3])*uint64(val2.n[7]) + + uint64(val.n[4])*uint64(val2.n[6]) + + uint64(val.n[5])*uint64(val2.n[5]) + + uint64(val.n[6])*uint64(val2.n[4]) + + uint64(val.n[7])*uint64(val2.n[3]) + + uint64(val.n[8])*uint64(val2.n[2]) + + uint64(val.n[9])*uint64(val2.n[1]) + t10 := m & fieldBaseMask + + // Terms for 2^(fieldBase*11). + m = (m >> fieldBase) + + uint64(val.n[2])*uint64(val2.n[9]) + + uint64(val.n[3])*uint64(val2.n[8]) + + uint64(val.n[4])*uint64(val2.n[7]) + + uint64(val.n[5])*uint64(val2.n[6]) + + uint64(val.n[6])*uint64(val2.n[5]) + + uint64(val.n[7])*uint64(val2.n[4]) + + uint64(val.n[8])*uint64(val2.n[3]) + + uint64(val.n[9])*uint64(val2.n[2]) + t11 := m & fieldBaseMask + + // Terms for 2^(fieldBase*12). + m = (m >> fieldBase) + + uint64(val.n[3])*uint64(val2.n[9]) + + uint64(val.n[4])*uint64(val2.n[8]) + + uint64(val.n[5])*uint64(val2.n[7]) + + uint64(val.n[6])*uint64(val2.n[6]) + + uint64(val.n[7])*uint64(val2.n[5]) + + uint64(val.n[8])*uint64(val2.n[4]) + + uint64(val.n[9])*uint64(val2.n[3]) + t12 := m & fieldBaseMask + + // Terms for 2^(fieldBase*13). + m = (m >> fieldBase) + + uint64(val.n[4])*uint64(val2.n[9]) + + uint64(val.n[5])*uint64(val2.n[8]) + + uint64(val.n[6])*uint64(val2.n[7]) + + uint64(val.n[7])*uint64(val2.n[6]) + + uint64(val.n[8])*uint64(val2.n[5]) + + uint64(val.n[9])*uint64(val2.n[4]) + t13 := m & fieldBaseMask + + // Terms for 2^(fieldBase*14). + m = (m >> fieldBase) + + uint64(val.n[5])*uint64(val2.n[9]) + + uint64(val.n[6])*uint64(val2.n[8]) + + uint64(val.n[7])*uint64(val2.n[7]) + + uint64(val.n[8])*uint64(val2.n[6]) + + uint64(val.n[9])*uint64(val2.n[5]) + t14 := m & fieldBaseMask + + // Terms for 2^(fieldBase*15). + m = (m >> fieldBase) + + uint64(val.n[6])*uint64(val2.n[9]) + + uint64(val.n[7])*uint64(val2.n[8]) + + uint64(val.n[8])*uint64(val2.n[7]) + + uint64(val.n[9])*uint64(val2.n[6]) + t15 := m & fieldBaseMask + + // Terms for 2^(fieldBase*16). + m = (m >> fieldBase) + + uint64(val.n[7])*uint64(val2.n[9]) + + uint64(val.n[8])*uint64(val2.n[8]) + + uint64(val.n[9])*uint64(val2.n[7]) + t16 := m & fieldBaseMask + + // Terms for 2^(fieldBase*17). + m = (m >> fieldBase) + + uint64(val.n[8])*uint64(val2.n[9]) + + uint64(val.n[9])*uint64(val2.n[8]) + t17 := m & fieldBaseMask + + // Terms for 2^(fieldBase*18). + m = (m >> fieldBase) + uint64(val.n[9])*uint64(val2.n[9]) + t18 := m & fieldBaseMask + + // What's left is for 2^(fieldBase*19). + t19 := m >> fieldBase + + // At this point, all of the terms are grouped into their respective + // base. + // + // Per [HAC] section 14.3.4: Reduction method of moduli of special form, + // when the modulus is of the special form m = b^t - c, highly efficient + // reduction can be achieved per the provided algorithm. + // + // The secp256k1 prime is equivalent to 2^256 - 4294968273, so it fits + // this criteria. + // + // 4294968273 in field representation (base 2^26) is: + // n[0] = 977 + // n[1] = 64 + // That is to say (2^26 * 64) + 977 = 4294968273 + // + // Since each word is in base 26, the upper terms (t10 and up) start + // at 260 bits (versus the final desired range of 256 bits), so the + // field representation of 'c' from above needs to be adjusted for the + // extra 4 bits by multiplying it by 2^4 = 16. 4294968273 * 16 = + // 68719492368. Thus, the adjusted field representation of 'c' is: + // n[0] = 977 * 16 = 15632 + // n[1] = 64 * 16 = 1024 + // That is to say (2^26 * 1024) + 15632 = 68719492368 + // + // To reduce the final term, t19, the entire 'c' value is needed instead + // of only n[0] because there are no more terms left to handle n[1]. + // This means there might be some magnitude left in the upper bits that + // is handled below. + m = t0 + t10*15632 + t0 = m & fieldBaseMask + m = (m >> fieldBase) + t1 + t10*1024 + t11*15632 + t1 = m & fieldBaseMask + m = (m >> fieldBase) + t2 + t11*1024 + t12*15632 + t2 = m & fieldBaseMask + m = (m >> fieldBase) + t3 + t12*1024 + t13*15632 + t3 = m & fieldBaseMask + m = (m >> fieldBase) + t4 + t13*1024 + t14*15632 + t4 = m & fieldBaseMask + m = (m >> fieldBase) + t5 + t14*1024 + t15*15632 + t5 = m & fieldBaseMask + m = (m >> fieldBase) + t6 + t15*1024 + t16*15632 + t6 = m & fieldBaseMask + m = (m >> fieldBase) + t7 + t16*1024 + t17*15632 + t7 = m & fieldBaseMask + m = (m >> fieldBase) + t8 + t17*1024 + t18*15632 + t8 = m & fieldBaseMask + m = (m >> fieldBase) + t9 + t18*1024 + t19*68719492368 + t9 = m & fieldMSBMask + m = m >> fieldMSBBits + + // At this point, if the magnitude is greater than 0, the overall value + // is greater than the max possible 256-bit value. In particular, it is + // "how many times larger" than the max value it is. + // + // The algorithm presented in [HAC] section 14.3.4 repeats until the + // quotient is zero. However, due to the above, we already know at + // least how many times we would need to repeat as it's the value + // currently in m. Thus we can simply multiply the magnitude by the + // field representation of the prime and do a single iteration. Notice + // that nothing will be changed when the magnitude is zero, so we could + // skip this in that case, however always running regardless allows it + // to run in constant time. The final result will be in the range + // 0 <= result <= prime + (2^64 - c), so it is guaranteed to have a + // magnitude of 1, but it is denormalized. + d := t0 + m*977 + f.n[0] = uint32(d & fieldBaseMask) + d = (d >> fieldBase) + t1 + m*64 + f.n[1] = uint32(d & fieldBaseMask) + f.n[2] = uint32((d >> fieldBase) + t2) + f.n[3] = uint32(t3) + f.n[4] = uint32(t4) + f.n[5] = uint32(t5) + f.n[6] = uint32(t6) + f.n[7] = uint32(t7) + f.n[8] = uint32(t8) + f.n[9] = uint32(t9) + + return f +} + +// Square squares the field value. The existing field value is modified. Note +// that this function can overflow if multiplying any of the individual words +// exceeds a max uint32. In practice, this means the magnitude of the field +// must be a max of 8 to prevent overflow. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Square().Mul(f2) so that f = f^2 * f2. +func (f *fieldVal) Square() *fieldVal { + return f.SquareVal(f) +} + +// SquareVal squares the passed value and stores the result in f. Note that +// this function can overflow if multiplying any of the individual words +// exceeds a max uint32. In practice, this means the magnitude of the field +// being squred must be a max of 8 to prevent overflow. +// +// The field value is returned to support chaining. This enables syntax like: +// f3.SquareVal(f).Mul(f) so that f3 = f^2 * f = f^3. +func (f *fieldVal) SquareVal(val *fieldVal) *fieldVal { + // This could be done with a couple of for loops and an array to store + // the intermediate terms, but this unrolled version is significantly + // faster. + + // Terms for 2^(fieldBase*0). + m := uint64(val.n[0]) * uint64(val.n[0]) + t0 := m & fieldBaseMask + + // Terms for 2^(fieldBase*1). + m = (m >> fieldBase) + 2*uint64(val.n[0])*uint64(val.n[1]) + t1 := m & fieldBaseMask + + // Terms for 2^(fieldBase*2). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[2]) + + uint64(val.n[1])*uint64(val.n[1]) + t2 := m & fieldBaseMask + + // Terms for 2^(fieldBase*3). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[3]) + + 2*uint64(val.n[1])*uint64(val.n[2]) + t3 := m & fieldBaseMask + + // Terms for 2^(fieldBase*4). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[4]) + + 2*uint64(val.n[1])*uint64(val.n[3]) + + uint64(val.n[2])*uint64(val.n[2]) + t4 := m & fieldBaseMask + + // Terms for 2^(fieldBase*5). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[5]) + + 2*uint64(val.n[1])*uint64(val.n[4]) + + 2*uint64(val.n[2])*uint64(val.n[3]) + t5 := m & fieldBaseMask + + // Terms for 2^(fieldBase*6). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[6]) + + 2*uint64(val.n[1])*uint64(val.n[5]) + + 2*uint64(val.n[2])*uint64(val.n[4]) + + uint64(val.n[3])*uint64(val.n[3]) + t6 := m & fieldBaseMask + + // Terms for 2^(fieldBase*7). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[7]) + + 2*uint64(val.n[1])*uint64(val.n[6]) + + 2*uint64(val.n[2])*uint64(val.n[5]) + + 2*uint64(val.n[3])*uint64(val.n[4]) + t7 := m & fieldBaseMask + + // Terms for 2^(fieldBase*8). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[8]) + + 2*uint64(val.n[1])*uint64(val.n[7]) + + 2*uint64(val.n[2])*uint64(val.n[6]) + + 2*uint64(val.n[3])*uint64(val.n[5]) + + uint64(val.n[4])*uint64(val.n[4]) + t8 := m & fieldBaseMask + + // Terms for 2^(fieldBase*9). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[9]) + + 2*uint64(val.n[1])*uint64(val.n[8]) + + 2*uint64(val.n[2])*uint64(val.n[7]) + + 2*uint64(val.n[3])*uint64(val.n[6]) + + 2*uint64(val.n[4])*uint64(val.n[5]) + t9 := m & fieldBaseMask + + // Terms for 2^(fieldBase*10). + m = (m >> fieldBase) + + 2*uint64(val.n[1])*uint64(val.n[9]) + + 2*uint64(val.n[2])*uint64(val.n[8]) + + 2*uint64(val.n[3])*uint64(val.n[7]) + + 2*uint64(val.n[4])*uint64(val.n[6]) + + uint64(val.n[5])*uint64(val.n[5]) + t10 := m & fieldBaseMask + + // Terms for 2^(fieldBase*11). + m = (m >> fieldBase) + + 2*uint64(val.n[2])*uint64(val.n[9]) + + 2*uint64(val.n[3])*uint64(val.n[8]) + + 2*uint64(val.n[4])*uint64(val.n[7]) + + 2*uint64(val.n[5])*uint64(val.n[6]) + t11 := m & fieldBaseMask + + // Terms for 2^(fieldBase*12). + m = (m >> fieldBase) + + 2*uint64(val.n[3])*uint64(val.n[9]) + + 2*uint64(val.n[4])*uint64(val.n[8]) + + 2*uint64(val.n[5])*uint64(val.n[7]) + + uint64(val.n[6])*uint64(val.n[6]) + t12 := m & fieldBaseMask + + // Terms for 2^(fieldBase*13). + m = (m >> fieldBase) + + 2*uint64(val.n[4])*uint64(val.n[9]) + + 2*uint64(val.n[5])*uint64(val.n[8]) + + 2*uint64(val.n[6])*uint64(val.n[7]) + t13 := m & fieldBaseMask + + // Terms for 2^(fieldBase*14). + m = (m >> fieldBase) + + 2*uint64(val.n[5])*uint64(val.n[9]) + + 2*uint64(val.n[6])*uint64(val.n[8]) + + uint64(val.n[7])*uint64(val.n[7]) + t14 := m & fieldBaseMask + + // Terms for 2^(fieldBase*15). + m = (m >> fieldBase) + + 2*uint64(val.n[6])*uint64(val.n[9]) + + 2*uint64(val.n[7])*uint64(val.n[8]) + t15 := m & fieldBaseMask + + // Terms for 2^(fieldBase*16). + m = (m >> fieldBase) + + 2*uint64(val.n[7])*uint64(val.n[9]) + + uint64(val.n[8])*uint64(val.n[8]) + t16 := m & fieldBaseMask + + // Terms for 2^(fieldBase*17). + m = (m >> fieldBase) + 2*uint64(val.n[8])*uint64(val.n[9]) + t17 := m & fieldBaseMask + + // Terms for 2^(fieldBase*18). + m = (m >> fieldBase) + uint64(val.n[9])*uint64(val.n[9]) + t18 := m & fieldBaseMask + + // What's left is for 2^(fieldBase*19). + t19 := m >> fieldBase + + // At this point, all of the terms are grouped into their respective + // base. + // + // Per [HAC] section 14.3.4: Reduction method of moduli of special form, + // when the modulus is of the special form m = b^t - c, highly efficient + // reduction can be achieved per the provided algorithm. + // + // The secp256k1 prime is equivalent to 2^256 - 4294968273, so it fits + // this criteria. + // + // 4294968273 in field representation (base 2^26) is: + // n[0] = 977 + // n[1] = 64 + // That is to say (2^26 * 64) + 977 = 4294968273 + // + // Since each word is in base 26, the upper terms (t10 and up) start + // at 260 bits (versus the final desired range of 256 bits), so the + // field representation of 'c' from above needs to be adjusted for the + // extra 4 bits by multiplying it by 2^4 = 16. 4294968273 * 16 = + // 68719492368. Thus, the adjusted field representation of 'c' is: + // n[0] = 977 * 16 = 15632 + // n[1] = 64 * 16 = 1024 + // That is to say (2^26 * 1024) + 15632 = 68719492368 + // + // To reduce the final term, t19, the entire 'c' value is needed instead + // of only n[0] because there are no more terms left to handle n[1]. + // This means there might be some magnitude left in the upper bits that + // is handled below. + m = t0 + t10*15632 + t0 = m & fieldBaseMask + m = (m >> fieldBase) + t1 + t10*1024 + t11*15632 + t1 = m & fieldBaseMask + m = (m >> fieldBase) + t2 + t11*1024 + t12*15632 + t2 = m & fieldBaseMask + m = (m >> fieldBase) + t3 + t12*1024 + t13*15632 + t3 = m & fieldBaseMask + m = (m >> fieldBase) + t4 + t13*1024 + t14*15632 + t4 = m & fieldBaseMask + m = (m >> fieldBase) + t5 + t14*1024 + t15*15632 + t5 = m & fieldBaseMask + m = (m >> fieldBase) + t6 + t15*1024 + t16*15632 + t6 = m & fieldBaseMask + m = (m >> fieldBase) + t7 + t16*1024 + t17*15632 + t7 = m & fieldBaseMask + m = (m >> fieldBase) + t8 + t17*1024 + t18*15632 + t8 = m & fieldBaseMask + m = (m >> fieldBase) + t9 + t18*1024 + t19*68719492368 + t9 = m & fieldMSBMask + m = m >> fieldMSBBits + + // At this point, if the magnitude is greater than 0, the overall value + // is greater than the max possible 256-bit value. In particular, it is + // "how many times larger" than the max value it is. + // + // The algorithm presented in [HAC] section 14.3.4 repeats until the + // quotient is zero. However, due to the above, we already know at + // least how many times we would need to repeat as it's the value + // currently in m. Thus we can simply multiply the magnitude by the + // field representation of the prime and do a single iteration. Notice + // that nothing will be changed when the magnitude is zero, so we could + // skip this in that case, however always running regardless allows it + // to run in constant time. The final result will be in the range + // 0 <= result <= prime + (2^64 - c), so it is guaranteed to have a + // magnitude of 1, but it is denormalized. + n := t0 + m*977 + f.n[0] = uint32(n & fieldBaseMask) + n = (n >> fieldBase) + t1 + m*64 + f.n[1] = uint32(n & fieldBaseMask) + f.n[2] = uint32((n >> fieldBase) + t2) + f.n[3] = uint32(t3) + f.n[4] = uint32(t4) + f.n[5] = uint32(t5) + f.n[6] = uint32(t6) + f.n[7] = uint32(t7) + f.n[8] = uint32(t8) + f.n[9] = uint32(t9) + + return f +} + +// Inverse finds the modular multiplicative inverse of the field value. The +// existing field value is modified. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Inverse().Mul(f2) so that f = f^-1 * f2. +func (f *fieldVal) Inverse() *fieldVal { + // Fermat's little theorem states that for a nonzero number a and prime + // prime p, a^(p-1) = 1 (mod p). Since the multipliciative inverse is + // a*b = 1 (mod p), it follows that b = a*a^(p-2) = a^(p-1) = 1 (mod p). + // Thus, a^(p-2) is the multiplicative inverse. + // + // In order to efficiently compute a^(p-2), p-2 needs to be split into + // a sequence of squares and multipications that minimizes the number of + // multiplications needed (since they are more costly than squarings). + // Intermediate results are saved and reused as well. + // + // The secp256k1 prime - 2 is 2^256 - 4294968275. + // + // This has a cost of 258 field squarings and 33 field multiplications. + var a2, a3, a4, a10, a11, a21, a42, a45, a63, a1019, a1023 fieldVal + a2.SquareVal(f) + a3.Mul2(&a2, f) + a4.SquareVal(&a2) + a10.SquareVal(&a4).Mul(&a2) + a11.Mul2(&a10, f) + a21.Mul2(&a10, &a11) + a42.SquareVal(&a21) + a45.Mul2(&a42, &a3) + a63.Mul2(&a42, &a21) + a1019.SquareVal(&a63).Square().Square().Square().Mul(&a11) + a1023.Mul2(&a1019, &a4) + f.Set(&a63) // f = a^(2^6 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^11 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^16 - 1024) + f.Mul(&a1023) // f = a^(2^16 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^21 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^26 - 1024) + f.Mul(&a1023) // f = a^(2^26 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^31 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^36 - 1024) + f.Mul(&a1023) // f = a^(2^36 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^41 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^46 - 1024) + f.Mul(&a1023) // f = a^(2^46 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^51 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^56 - 1024) + f.Mul(&a1023) // f = a^(2^56 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^61 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^66 - 1024) + f.Mul(&a1023) // f = a^(2^66 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^71 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^76 - 1024) + f.Mul(&a1023) // f = a^(2^76 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^81 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^86 - 1024) + f.Mul(&a1023) // f = a^(2^86 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^91 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^96 - 1024) + f.Mul(&a1023) // f = a^(2^96 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^101 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^106 - 1024) + f.Mul(&a1023) // f = a^(2^106 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^111 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^116 - 1024) + f.Mul(&a1023) // f = a^(2^116 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^121 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^126 - 1024) + f.Mul(&a1023) // f = a^(2^126 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^131 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^136 - 1024) + f.Mul(&a1023) // f = a^(2^136 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^141 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^146 - 1024) + f.Mul(&a1023) // f = a^(2^146 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^151 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^156 - 1024) + f.Mul(&a1023) // f = a^(2^156 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^161 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^166 - 1024) + f.Mul(&a1023) // f = a^(2^166 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^171 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^176 - 1024) + f.Mul(&a1023) // f = a^(2^176 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^181 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^186 - 1024) + f.Mul(&a1023) // f = a^(2^186 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^191 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^196 - 1024) + f.Mul(&a1023) // f = a^(2^196 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^201 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^206 - 1024) + f.Mul(&a1023) // f = a^(2^206 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^211 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^216 - 1024) + f.Mul(&a1023) // f = a^(2^216 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^221 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^226 - 1024) + f.Mul(&a1019) // f = a^(2^226 - 5) + f.Square().Square().Square().Square().Square() // f = a^(2^231 - 160) + f.Square().Square().Square().Square().Square() // f = a^(2^236 - 5120) + f.Mul(&a1023) // f = a^(2^236 - 4097) + f.Square().Square().Square().Square().Square() // f = a^(2^241 - 131104) + f.Square().Square().Square().Square().Square() // f = a^(2^246 - 4195328) + f.Mul(&a1023) // f = a^(2^246 - 4194305) + f.Square().Square().Square().Square().Square() // f = a^(2^251 - 134217760) + f.Square().Square().Square().Square().Square() // f = a^(2^256 - 4294968320) + return f.Mul(&a45) // f = a^(2^256 - 4294968275) = a^(p-2) +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/field_test.go b/vendor/github.com/btcsuite/btcd/btcec/field_test.go new file mode 100644 index 0000000000000000000000000000000000000000..39d0ad32daa26f1fb1fa3ca1c27e9f6a7b39f390 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/field_test.go @@ -0,0 +1,744 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Copyright (c) 2013-2014 Dave Collins +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec_test + +import ( + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcec" +) + +// TestSetInt ensures that setting a field value to various native integers +// works as expected. +func TestSetInt(t *testing.T) { + tests := []struct { + in uint + raw [10]uint32 + }{ + {5, [10]uint32{5, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + // 2^26 + {67108864, [10]uint32{67108864, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + // 2^26 + 1 + {67108865, [10]uint32{67108865, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + // 2^32 - 1 + {4294967295, [10]uint32{4294967295, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().SetInt(test.in) + result := f.TstRawInts() + if !reflect.DeepEqual(result, test.raw) { + t.Errorf("fieldVal.Set #%d wrong result\ngot: %v\n"+ + "want: %v", i, result, test.raw) + continue + } + } +} + +// TestZero ensures that zeroing a field value zero works as expected. +func TestZero(t *testing.T) { + f := btcec.NewFieldVal().SetInt(2) + f.Zero() + for idx, rawInt := range f.TstRawInts() { + if rawInt != 0 { + t.Errorf("internal field integer at index #%d is not "+ + "zero - got %d", idx, rawInt) + } + } +} + +// TestIsZero ensures that checking if a field IsZero works as expected. +func TestIsZero(t *testing.T) { + f := btcec.NewFieldVal() + if !f.IsZero() { + t.Errorf("new field value is not zero - got %v (rawints %x)", f, + f.TstRawInts()) + } + + f.SetInt(1) + if f.IsZero() { + t.Errorf("field claims it's zero when it's not - got %v "+ + "(raw rawints %x)", f, f.TstRawInts()) + } + + f.Zero() + if !f.IsZero() { + t.Errorf("field claims it's not zero when it is - got %v "+ + "(raw rawints %x)", f, f.TstRawInts()) + } +} + +// TestStringer ensures the stringer returns the appropriate hex string. +func TestStringer(t *testing.T) { + tests := []struct { + in string + expected string + }{ + {"0", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"1", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"a", "000000000000000000000000000000000000000000000000000000000000000a"}, + {"b", "000000000000000000000000000000000000000000000000000000000000000b"}, + {"c", "000000000000000000000000000000000000000000000000000000000000000c"}, + {"d", "000000000000000000000000000000000000000000000000000000000000000d"}, + {"e", "000000000000000000000000000000000000000000000000000000000000000e"}, + {"f", "000000000000000000000000000000000000000000000000000000000000000f"}, + {"f0", "00000000000000000000000000000000000000000000000000000000000000f0"}, + // 2^26-1 + { + "3ffffff", + "0000000000000000000000000000000000000000000000000000000003ffffff", + }, + // 2^32-1 + { + "ffffffff", + "00000000000000000000000000000000000000000000000000000000ffffffff", + }, + // 2^64-1 + { + "ffffffffffffffff", + "000000000000000000000000000000000000000000000000ffffffffffffffff", + }, + // 2^96-1 + { + "ffffffffffffffffffffffff", + "0000000000000000000000000000000000000000ffffffffffffffffffffffff", + }, + // 2^128-1 + { + "ffffffffffffffffffffffffffffffff", + "00000000000000000000000000000000ffffffffffffffffffffffffffffffff", + }, + // 2^160-1 + { + "ffffffffffffffffffffffffffffffffffffffff", + "000000000000000000000000ffffffffffffffffffffffffffffffffffffffff", + }, + // 2^192-1 + { + "ffffffffffffffffffffffffffffffffffffffffffffffff", + "0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff", + }, + // 2^224-1 + { + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, + // 2^256-4294968273 (the btcec prime, so should result in 0) + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "0000000000000000000000000000000000000000000000000000000000000000", + }, + // 2^256-4294968274 (the secp256k1 prime+1, so should result in 1) + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "0000000000000000000000000000000000000000000000000000000000000001", + }, + + // Invalid hex + {"g", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"1h", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"i1", "0000000000000000000000000000000000000000000000000000000000000000"}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().SetHex(test.in) + result := f.String() + if result != test.expected { + t.Errorf("fieldVal.String #%d wrong result\ngot: %v\n"+ + "want: %v", i, result, test.expected) + continue + } + } +} + +// TestNormalize ensures that normalizing the internal field words works as +// expected. +func TestNormalize(t *testing.T) { + tests := []struct { + raw [10]uint32 // Intentionally denormalized value + normalized [10]uint32 // Normalized form of the raw value + }{ + { + [10]uint32{0x00000005, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000005, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^26 + { + [10]uint32{0x04000000, 0x0, 0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000000, 0x1, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^26 + 1 + { + [10]uint32{0x04000001, 0x0, 0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000001, 0x1, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^32 - 1 + { + [10]uint32{0xffffffff, 0x00, 0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x03ffffff, 0x3f, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^32 + { + [10]uint32{0x04000000, 0x3f, 0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000000, 0x40, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^32 + 1 + { + [10]uint32{0x04000001, 0x3f, 0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000001, 0x40, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^64 - 1 + { + [10]uint32{0xffffffff, 0xffffffc0, 0xfc0, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x03ffffff, 0x03ffffff, 0xfff, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^64 + { + [10]uint32{0x04000000, 0x03ffffff, 0x0fff, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000000, 0x00000000, 0x1000, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^64 + 1 + { + [10]uint32{0x04000001, 0x03ffffff, 0x0fff, 0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000001, 0x00000000, 0x1000, 0, 0, 0, 0, 0, 0, 0}, + }, + // 2^96 - 1 + { + [10]uint32{0xffffffff, 0xffffffc0, 0xffffffc0, 0x3ffc0, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x03ffffff, 0x03ffffff, 0x03ffffff, 0x3ffff, 0, 0, 0, 0, 0, 0}, + }, + // 2^96 + { + [10]uint32{0x04000000, 0x03ffffff, 0x03ffffff, 0x3ffff, 0, 0, 0, 0, 0, 0}, + [10]uint32{0x00000000, 0x00000000, 0x00000000, 0x40000, 0, 0, 0, 0, 0, 0}, + }, + // 2^128 - 1 + { + [10]uint32{0xffffffff, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffc0, 0, 0, 0, 0, 0}, + [10]uint32{0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0xffffff, 0, 0, 0, 0, 0}, + }, + // 2^128 + { + [10]uint32{0x04000000, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x0ffffff, 0, 0, 0, 0, 0}, + [10]uint32{0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1000000, 0, 0, 0, 0, 0}, + }, + // 2^256 - 4294968273 (secp256k1 prime) + { + [10]uint32{0xfffffc2f, 0xffffff80, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0x3fffc0}, + [10]uint32{0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000}, + }, + // 2^256 - 1 + { + [10]uint32{0xffffffff, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0xffffffc0, 0x3fffc0}, + [10]uint32{0x000003d0, 0x00000040, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().TstSetRawInts(test.raw).Normalize() + result := f.TstRawInts() + if !reflect.DeepEqual(result, test.normalized) { + t.Errorf("fieldVal.Set #%d wrong normalized result\n"+ + "got: %x\nwant: %x", i, result, test.normalized) + continue + } + } +} + +// TestIsOdd ensures that checking if a field value IsOdd works as expected. +func TestIsOdd(t *testing.T) { + tests := []struct { + in string // hex encoded value + expected bool // expected oddness + }{ + {"0", false}, + {"1", true}, + {"2", false}, + // 2^32 - 1 + {"ffffffff", true}, + // 2^64 - 2 + {"fffffffffffffffe", false}, + // secp256k1 prime + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", true}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().SetHex(test.in) + result := f.IsOdd() + if result != test.expected { + t.Errorf("fieldVal.IsOdd #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, test.expected) + continue + } + } +} + +// TestEquals ensures that checking two field values for equality via Equals +// works as expected. +func TestEquals(t *testing.T) { + tests := []struct { + in1 string // hex encoded value + in2 string // hex encoded value + expected bool // expected equality + }{ + {"0", "0", true}, + {"0", "1", false}, + {"1", "0", false}, + // 2^32 - 1 == 2^32 - 1? + {"ffffffff", "ffffffff", true}, + // 2^64 - 1 == 2^64 - 2? + {"ffffffffffffffff", "fffffffffffffffe", false}, + // 0 == prime (mod prime)? + {"0", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", true}, + // 1 == prime+1 (mod prime)? + {"1", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", true}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().SetHex(test.in1).Normalize() + f2 := btcec.NewFieldVal().SetHex(test.in2).Normalize() + result := f.Equals(f2) + if result != test.expected { + t.Errorf("fieldVal.Equals #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, test.expected) + continue + } + } +} + +// TestNegate ensures that negating field values via Negate works as expected. +func TestNegate(t *testing.T) { + tests := []struct { + in string // hex encoded value + expected string // expected hex encoded value + }{ + // secp256k1 prime (aka 0) + {"0", "0"}, + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "0"}, + {"0", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"}, + // secp256k1 prime-1 + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", "1"}, + {"1", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e"}, + // secp256k1 prime-2 + {"2", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d"}, + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", "2"}, + // Random sampling + { + "b3d9aac9c5e43910b4385b53c7e78c21d4cd5f8e683c633aed04c233efc2e120", + "4c2655363a1bc6ef4bc7a4ac381873de2b32a07197c39cc512fb3dcb103d1b0f", + }, + { + "f8a85984fee5a12a7c8dd08830d83423c937d77c379e4a958e447a25f407733f", + "757a67b011a5ed583722f77cf27cbdc36c82883c861b56a71bb85d90bf888f0", + }, + { + "45ee6142a7fda884211e93352ed6cb2807800e419533be723a9548823ece8312", + "ba119ebd5802577bdee16ccad12934d7f87ff1be6acc418dc56ab77cc131791d", + }, + { + "53c2a668f07e411a2e473e1c3b6dcb495dec1227af27673761d44afe5b43d22b", + "ac3d59970f81bee5d1b8c1e3c49234b6a213edd850d898c89e2bb500a4bc2a04", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().SetHex(test.in).Normalize() + expected := btcec.NewFieldVal().SetHex(test.expected).Normalize() + result := f.Negate(1).Normalize() + if !result.Equals(expected) { + t.Errorf("fieldVal.Negate #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, expected) + continue + } + } +} + +// TestAddInt ensures that adding an integer to field values via AddInt works as +// expected. +func TestAddInt(t *testing.T) { + tests := []struct { + in1 string // hex encoded value + in2 uint // unsigned integer to add to the value above + expected string // expected hex encoded value + }{ + {"0", 1, "1"}, + {"1", 0, "1"}, + {"1", 1, "2"}, + // secp256k1 prime-1 + 1 + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", 1, "0"}, + // secp256k1 prime + 1 + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", 1, "1"}, + // Random samples. + { + "ff95ad9315aff04ab4af0ce673620c7145dc85d03bab5ba4b09ca2c4dec2d6c1", + 0x10f, + "ff95ad9315aff04ab4af0ce673620c7145dc85d03bab5ba4b09ca2c4dec2d7d0", + }, + { + "44bdae6b772e7987941f1ba314e6a5b7804a4c12c00961b57d20f41deea9cecf", + 0x2cf11d41, + "44bdae6b772e7987941f1ba314e6a5b7804a4c12c00961b57d20f41e1b9aec10", + }, + { + "88c3ecae67b591935fb1f6a9499c35315ffad766adca665c50b55f7105122c9c", + 0x4829aa2d, + "88c3ecae67b591935fb1f6a9499c35315ffad766adca665c50b55f714d3bd6c9", + }, + { + "8523e9edf360ca32a95aae4e57fcde5a542b471d08a974d94ea0ee09a015e2a6", + 0xa21265a5, + "8523e9edf360ca32a95aae4e57fcde5a542b471d08a974d94ea0ee0a4228484b", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().SetHex(test.in1).Normalize() + expected := btcec.NewFieldVal().SetHex(test.expected).Normalize() + result := f.AddInt(test.in2).Normalize() + if !result.Equals(expected) { + t.Errorf("fieldVal.AddInt #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, expected) + continue + } + } +} + +// TestAdd ensures that adding two field values together via Add works as +// expected. +func TestAdd(t *testing.T) { + tests := []struct { + in1 string // first hex encoded value + in2 string // second hex encoded value to add + expected string // expected hex encoded value + }{ + {"0", "1", "1"}, + {"1", "0", "1"}, + {"1", "1", "2"}, + // secp256k1 prime-1 + 1 + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", "1", "0"}, + // secp256k1 prime + 1 + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "1", "1"}, + // Random samples. + { + "2b2012f975404e5065b4292fb8bed0a5d315eacf24c74d8b27e73bcc5430edcc", + "2c3cefa4e4753e8aeec6ac4c12d99da4d78accefda3b7885d4c6bab46c86db92", + "575d029e59b58cdb547ad57bcb986e4aaaa0b7beff02c610fcadf680c0b7c95e", + }, + { + "8131e8722fe59bb189692b96c9f38de92885730f1dd39ab025daffb94c97f79c", + "ff5454b765f0aab5f0977dcc629becc84cabeb9def48e79c6aadb2622c490fa9", + "80863d2995d646677a00a9632c8f7ab175315ead0d1c824c9088b21c78e10b16", + }, + { + "c7c95e93d0892b2b2cdd77e80eb646ea61be7a30ac7e097e9f843af73fad5c22", + "3afe6f91a74dfc1c7f15c34907ee981656c37236d946767dd53ccad9190e437c", + "02c7ce2577d72747abf33b3116a4df00b881ec6785c47ffc74c105d158bba36f", + }, + { + "fd1c26f6a23381e5d785ba889494ec059369b888ad8431cd67d8c934b580dbe1", + "a475aa5a31dcca90ef5b53c097d9133d6b7117474b41e7877bb199590fc0489c", + "a191d150d4104c76c6e10e492c6dff42fedacfcff8c61954e38a628ec541284e", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().SetHex(test.in1).Normalize() + f2 := btcec.NewFieldVal().SetHex(test.in2).Normalize() + expected := btcec.NewFieldVal().SetHex(test.expected).Normalize() + result := f.Add(f2).Normalize() + if !result.Equals(expected) { + t.Errorf("fieldVal.Add #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, expected) + continue + } + } +} + +// TestAdd2 ensures that adding two field values together via Add2 works as +// expected. +func TestAdd2(t *testing.T) { + tests := []struct { + in1 string // first hex encoded value + in2 string // second hex encoded value to add + expected string // expected hex encoded value + }{ + {"0", "1", "1"}, + {"1", "0", "1"}, + {"1", "1", "2"}, + // secp256k1 prime-1 + 1 + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", "1", "0"}, + // secp256k1 prime + 1 + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "1", "1"}, + // Random samples. + { + "ad82b8d1cc136e23e9fd77fe2c7db1fe5a2ecbfcbde59ab3529758334f862d28", + "4d6a4e95d6d61f4f46b528bebe152d408fd741157a28f415639347a84f6f574b", + "faed0767a2e98d7330b2a0bcea92df3eea060d12380e8ec8b62a9fdb9ef58473", + }, + { + "f3f43a2540054a86e1df98547ec1c0e157b193e5350fb4a3c3ea214b228ac5e7", + "25706572592690ea3ddc951a1b48b504a4c83dc253756e1b96d56fdfb3199522", + "19649f97992bdb711fbc2d6e9a0a75e5fc79d1a7888522bf5abf912bd5a45eda", + }, + { + "6915bb94eef13ff1bb9b2633d997e13b9b1157c713363cc0e891416d6734f5b8", + "11f90d6ac6fe1c4e8900b1c85fb575c251ec31b9bc34b35ada0aea1c21eded22", + "7b0ec8ffb5ef5c40449bd7fc394d56fdecfd8980cf6af01bc29c2b898922e2da", + }, + { + "48b0c9eae622eed9335b747968544eb3e75cb2dc8128388f948aa30f88cabde4", + "0989882b52f85f9d524a3a3061a0e01f46d597839d2ba637320f4b9510c8d2d5", + "523a5216391b4e7685a5aea9c9f52ed32e324a601e53dec6c699eea4999390b9", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().SetHex(test.in1).Normalize() + f2 := btcec.NewFieldVal().SetHex(test.in2).Normalize() + expected := btcec.NewFieldVal().SetHex(test.expected).Normalize() + result := f.Add2(f, f2).Normalize() + if !result.Equals(expected) { + t.Errorf("fieldVal.Add2 #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, expected) + continue + } + } +} + +// TestMulInt ensures that adding an integer to field values via MulInt works as +// expected. +func TestMulInt(t *testing.T) { + tests := []struct { + in1 string // hex encoded value + in2 uint // unsigned integer to multiply with value above + expected string // expected hex encoded value + }{ + {"0", 0, "0"}, + {"1", 0, "0"}, + {"0", 1, "0"}, + {"1", 1, "1"}, + // secp256k1 prime-1 * 2 + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + 2, + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + }, + // secp256k1 prime * 3 + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", 3, "0"}, + // secp256k1 prime-1 * 8 + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + 8, + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc27", + }, + // Random samples for first value. The second value is limited + // to 8 since that is the maximum int used in the elliptic curve + // calculations. + { + "b75674dc9180d306c692163ac5e089f7cef166af99645c0c23568ab6d967288a", + 6, + "4c06bd2b6904f228a76c8560a3433bced9a8681d985a2848d407404d186b0280", + }, + { + "54873298ac2b5ba8591c125ae54931f5ea72040aee07b208d6135476fb5b9c0e", + 3, + "fd9597ca048212f90b543710afdb95e1bf560c20ca17161a8239fd64f212d42a", + }, + { + "7c30fbd363a74c17e1198f56b090b59bbb6c8755a74927a6cba7a54843506401", + 5, + "6cf4eb20f2447c77657fccb172d38c0aa91ea4ac446dc641fa463a6b5091fba7", + }, + { + "fb4529be3e027a3d1587d8a500b72f2d312e3577340ef5175f96d113be4c2ceb", + 8, + "da294df1f013d1e8ac3ec52805b979698971abb9a077a8bafcb688a4f261820f", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().SetHex(test.in1).Normalize() + expected := btcec.NewFieldVal().SetHex(test.expected).Normalize() + result := f.MulInt(test.in2).Normalize() + if !result.Equals(expected) { + t.Errorf("fieldVal.MulInt #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, expected) + continue + } + } +} + +// TestMul ensures that multiplying two field valuess via Mul works as expected. +func TestMul(t *testing.T) { + tests := []struct { + in1 string // first hex encoded value + in2 string // second hex encoded value to multiply with + expected string // expected hex encoded value + }{ + {"0", "0", "0"}, + {"1", "0", "0"}, + {"0", "1", "0"}, + {"1", "1", "1"}, + // secp256k1 prime-1 * 2 + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + "2", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + }, + // secp256k1 prime * 3 + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "3", "0"}, + // secp256k1 prime-1 * 8 + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + "8", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc27", + }, + // Random samples. + { + "cfb81753d5ef499a98ecc04c62cb7768c2e4f1740032946db1c12e405248137e", + "58f355ad27b4d75fb7db0442452e732c436c1f7c5a7c4e214fa9cc031426a7d3", + "1018cd2d7c2535235b71e18db9cd98027386328d2fa6a14b36ec663c4c87282b", + }, + { + "26e9d61d1cdf3920e9928e85fa3df3e7556ef9ab1d14ec56d8b4fc8ed37235bf", + "2dfc4bbe537afee979c644f8c97b31e58be5296d6dbc460091eae630c98511cf", + "da85f48da2dc371e223a1ae63bd30b7e7ee45ae9b189ac43ff357e9ef8cf107a", + }, + { + "5db64ed5afb71646c8b231585d5b2bf7e628590154e0854c4c29920b999ff351", + "279cfae5eea5d09ade8e6a7409182f9de40981bc31c84c3d3dfe1d933f152e9a", + "2c78fbae91792dd0b157abe3054920049b1879a7cc9d98cfda927d83be411b37", + }, + { + "b66dfc1f96820b07d2bdbd559c19319a3a73c97ceb7b3d662f4fe75ecb6819e6", + "bf774aba43e3e49eb63a6e18037d1118152568f1a3ac4ec8b89aeb6ff8008ae1", + "c4f016558ca8e950c21c3f7fc15f640293a979c7b01754ee7f8b3340d4902ebb", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().SetHex(test.in1).Normalize() + f2 := btcec.NewFieldVal().SetHex(test.in2).Normalize() + expected := btcec.NewFieldVal().SetHex(test.expected).Normalize() + result := f.Mul(f2).Normalize() + if !result.Equals(expected) { + t.Errorf("fieldVal.Mul #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, expected) + continue + } + } +} + +// TestSquare ensures that squaring field values via Square works as expected. +func TestSquare(t *testing.T) { + tests := []struct { + in string // hex encoded value + expected string // expected hex encoded value + }{ + // secp256k1 prime (aka 0) + {"0", "0"}, + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "0"}, + {"0", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"}, + // secp256k1 prime-1 + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", "1"}, + // secp256k1 prime-2 + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", "4"}, + // Random sampling + { + "b0ba920360ea8436a216128047aab9766d8faf468895eb5090fc8241ec758896", + "133896b0b69fda8ce9f648b9a3af38f345290c9eea3cbd35bafcadf7c34653d3", + }, + { + "c55d0d730b1d0285a1599995938b042a756e6e8857d390165ffab480af61cbd5", + "cd81758b3f5877cbe7e5b0a10cebfa73bcbf0957ca6453e63ee8954ab7780bee", + }, + { + "e89c1f9a70d93651a1ba4bca5b78658f00de65a66014a25544d3365b0ab82324", + "39ffc7a43e5dbef78fd5d0354fb82c6d34f5a08735e34df29da14665b43aa1f", + }, + { + "7dc26186079d22bcbe1614aa20ae627e62d72f9be7ad1e99cac0feb438956f05", + "bf86bcfc4edb3d81f916853adfda80c07c57745b008b60f560b1912f95bce8ae", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().SetHex(test.in).Normalize() + expected := btcec.NewFieldVal().SetHex(test.expected).Normalize() + result := f.Square().Normalize() + if !result.Equals(expected) { + t.Errorf("fieldVal.Square #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, expected) + continue + } + } +} + +// TestInverse ensures that finding the multiplicative inverse via Inverse works +// as expected. +func TestInverse(t *testing.T) { + tests := []struct { + in string // hex encoded value + expected string // expected hex encoded value + }{ + // secp256k1 prime (aka 0) + {"0", "0"}, + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "0"}, + {"0", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"}, + // secp256k1 prime-1 + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e", + }, + // secp256k1 prime-2 + { + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2d", + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffe17", + }, + // Random sampling + { + "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca", + "987aeb257b063df0c6d1334051c47092b6d8766c4bf10c463786d93f5bc54354", + }, + { + "69d1323ce9f1f7b3bd3c7320b0d6311408e30281e273e39a0d8c7ee1c8257919", + "49340981fa9b8d3dad72de470b34f547ed9179c3953797d0943af67806f4bb6", + }, + { + "e0debf988ae098ecda07d0b57713e97c6d213db19753e8c95aa12a2fc1cc5272", + "64f58077b68af5b656b413ea366863f7b2819f8d27375d9c4d9804135ca220c2", + }, + { + "dcd394f91f74c2ba16aad74a22bb0ed47fe857774b8f2d6c09e28bfb14642878", + "fb848ec64d0be572a63c38fe83df5e7f3d032f60bf8c969ef67d36bf4ada22a9", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + f := btcec.NewFieldVal().SetHex(test.in).Normalize() + expected := btcec.NewFieldVal().SetHex(test.expected).Normalize() + result := f.Inverse().Normalize() + if !result.Equals(expected) { + t.Errorf("fieldVal.Inverse #%d wrong result\n"+ + "got: %v\nwant: %v", i, result, expected) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/genprecomps.go b/vendor/github.com/btcsuite/btcd/btcec/genprecomps.go new file mode 100644 index 0000000000000000000000000000000000000000..d4a9c1b8306089d194f13a7020e370de5abddba5 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/genprecomps.go @@ -0,0 +1,63 @@ +// Copyright 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// This file is ignored during the regular build due to the following build tag. +// It is called by go generate and used to automatically generate pre-computed +// tables used to accelerate operations. +// +build ignore + +package main + +import ( + "bytes" + "compress/zlib" + "encoding/base64" + "fmt" + "log" + "os" + + "github.com/btcsuite/btcd/btcec" +) + +func main() { + fi, err := os.Create("secp256k1.go") + if err != nil { + log.Fatal(err) + } + defer fi.Close() + + // Compress the serialized byte points. + serialized := btcec.S256().SerializedBytePoints() + var compressed bytes.Buffer + w := zlib.NewWriter(&compressed) + if _, err := w.Write(serialized); err != nil { + fmt.Println(err) + os.Exit(1) + } + w.Close() + + // Encode the compressed byte points with base64. + encoded := make([]byte, base64.StdEncoding.EncodedLen(compressed.Len())) + base64.StdEncoding.Encode(encoded, compressed.Bytes()) + + fmt.Fprintln(fi, "// Copyright (c) 2015 The btcsuite developers") + fmt.Fprintln(fi, "// Use of this source code is governed by an ISC") + fmt.Fprintln(fi, "// license that can be found in the LICENSE file.") + fmt.Fprintln(fi) + fmt.Fprintln(fi, "package btcec") + fmt.Fprintln(fi) + fmt.Fprintln(fi, "// Auto-generated file (see genprecomps.go)") + fmt.Fprintln(fi, "// DO NOT EDIT") + fmt.Fprintln(fi) + fmt.Fprintf(fi, "var secp256k1BytePoints = %q\n", string(encoded)) + + a1, b1, a2, b2 := btcec.S256().EndomorphismVectors() + fmt.Println("The following values are the computed linearly " + + "independent vectors needed to make use of the secp256k1 " + + "endomorphism:") + fmt.Printf("a1: %x\n", a1) + fmt.Printf("b1: %x\n", b1) + fmt.Printf("a2: %x\n", a2) + fmt.Printf("b2: %x\n", b2) +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/gensecp256k1.go b/vendor/github.com/btcsuite/btcd/btcec/gensecp256k1.go new file mode 100644 index 0000000000000000000000000000000000000000..1928702da84ffeb2240c15b4faa13b5255b83a3b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/gensecp256k1.go @@ -0,0 +1,203 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// This file is ignored during the regular build due to the following build tag. +// This build tag is set during go generate. +// +build gensecp256k1 + +package btcec + +// References: +// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone) + +import ( + "encoding/binary" + "math/big" +) + +// secp256k1BytePoints are dummy points used so the code which generates the +// real values can compile. +var secp256k1BytePoints = "" + +// getDoublingPoints returns all the possible G^(2^i) for i in +// 0..n-1 where n is the curve's bit size (256 in the case of secp256k1) +// the coordinates are recorded as Jacobian coordinates. +func (curve *KoblitzCurve) getDoublingPoints() [][3]fieldVal { + doublingPoints := make([][3]fieldVal, curve.BitSize) + + // initialize px, py, pz to the Jacobian coordinates for the base point + px, py := curve.bigAffineToField(curve.Gx, curve.Gy) + pz := new(fieldVal).SetInt(1) + for i := 0; i < curve.BitSize; i++ { + doublingPoints[i] = [3]fieldVal{*px, *py, *pz} + // P = 2*P + curve.doubleJacobian(px, py, pz, px, py, pz) + } + return doublingPoints +} + +// SerializedBytePoints returns a serialized byte slice which contains all of +// the possible points per 8-bit window. This is used to when generating +// secp256k1.go. +func (curve *KoblitzCurve) SerializedBytePoints() []byte { + doublingPoints := curve.getDoublingPoints() + + // Segregate the bits into byte-sized windows + serialized := make([]byte, curve.byteSize*256*3*10*4) + offset := 0 + for byteNum := 0; byteNum < curve.byteSize; byteNum++ { + // Grab the 8 bits that make up this byte from doublingPoints. + startingBit := 8 * (curve.byteSize - byteNum - 1) + computingPoints := doublingPoints[startingBit : startingBit+8] + + // Compute all points in this window and serialize them. + for i := 0; i < 256; i++ { + px, py, pz := new(fieldVal), new(fieldVal), new(fieldVal) + for j := 0; j < 8; j++ { + if i>>uint(j)&1 == 1 { + curve.addJacobian(px, py, pz, &computingPoints[j][0], + &computingPoints[j][1], &computingPoints[j][2], px, py, pz) + } + } + for i := 0; i < 10; i++ { + binary.LittleEndian.PutUint32(serialized[offset:], px.n[i]) + offset += 4 + } + for i := 0; i < 10; i++ { + binary.LittleEndian.PutUint32(serialized[offset:], py.n[i]) + offset += 4 + } + for i := 0; i < 10; i++ { + binary.LittleEndian.PutUint32(serialized[offset:], pz.n[i]) + offset += 4 + } + } + } + + return serialized +} + +// sqrt returns the square root of the provided big integer using Newton's +// method. It's only compiled and used during generation of pre-computed +// values, so speed is not a huge concern. +func sqrt(n *big.Int) *big.Int { + // Initial guess = 2^(log_2(n)/2) + guess := big.NewInt(2) + guess.Exp(guess, big.NewInt(int64(n.BitLen()/2)), nil) + + // Now refine using Newton's method. + big2 := big.NewInt(2) + prevGuess := big.NewInt(0) + for { + prevGuess.Set(guess) + guess.Add(guess, new(big.Int).Div(n, guess)) + guess.Div(guess, big2) + if guess.Cmp(prevGuess) == 0 { + break + } + } + return guess +} + +// EndomorphismVectors runs the first 3 steps of algorithm 3.74 from [GECC] to +// generate the linearly independent vectors needed to generate a balanced +// length-two representation of a multiplier such that k = k1 + k2λ (mod N) and +// returns them. Since the values will always be the same given the fact that N +// and λ are fixed, the final results can be accelerated by storing the +// precomputed values with the curve. +func (curve *KoblitzCurve) EndomorphismVectors() (a1, b1, a2, b2 *big.Int) { + bigMinus1 := big.NewInt(-1) + + // This section uses an extended Euclidean algorithm to generate a + // sequence of equations: + // s[i] * N + t[i] * λ = r[i] + + nSqrt := sqrt(curve.N) + u, v := new(big.Int).Set(curve.N), new(big.Int).Set(curve.lambda) + x1, y1 := big.NewInt(1), big.NewInt(0) + x2, y2 := big.NewInt(0), big.NewInt(1) + q, r := new(big.Int), new(big.Int) + qu, qx1, qy1 := new(big.Int), new(big.Int), new(big.Int) + s, t := new(big.Int), new(big.Int) + ri, ti := new(big.Int), new(big.Int) + a1, b1, a2, b2 = new(big.Int), new(big.Int), new(big.Int), new(big.Int) + found, oneMore := false, false + for u.Sign() != 0 { + // q = v/u + q.Div(v, u) + + // r = v - q*u + qu.Mul(q, u) + r.Sub(v, qu) + + // s = x2 - q*x1 + qx1.Mul(q, x1) + s.Sub(x2, qx1) + + // t = y2 - q*y1 + qy1.Mul(q, y1) + t.Sub(y2, qy1) + + // v = u, u = r, x2 = x1, x1 = s, y2 = y1, y1 = t + v.Set(u) + u.Set(r) + x2.Set(x1) + x1.Set(s) + y2.Set(y1) + y1.Set(t) + + // As soon as the remainder is less than the sqrt of n, the + // values of a1 and b1 are known. + if !found && r.Cmp(nSqrt) < 0 { + // When this condition executes ri and ti represent the + // r[i] and t[i] values such that i is the greatest + // index for which r >= sqrt(n). Meanwhile, the current + // r and t values are r[i+1] and t[i+1], respectively. + + // a1 = r[i+1], b1 = -t[i+1] + a1.Set(r) + b1.Mul(t, bigMinus1) + found = true + oneMore = true + + // Skip to the next iteration so ri and ti are not + // modified. + continue + + } else if oneMore { + // When this condition executes ri and ti still + // represent the r[i] and t[i] values while the current + // r and t are r[i+2] and t[i+2], respectively. + + // sum1 = r[i]^2 + t[i]^2 + rSquared := new(big.Int).Mul(ri, ri) + tSquared := new(big.Int).Mul(ti, ti) + sum1 := new(big.Int).Add(rSquared, tSquared) + + // sum2 = r[i+2]^2 + t[i+2]^2 + r2Squared := new(big.Int).Mul(r, r) + t2Squared := new(big.Int).Mul(t, t) + sum2 := new(big.Int).Add(r2Squared, t2Squared) + + // if (r[i]^2 + t[i]^2) <= (r[i+2]^2 + t[i+2]^2) + if sum1.Cmp(sum2) <= 0 { + // a2 = r[i], b2 = -t[i] + a2.Set(ri) + b2.Mul(ti, bigMinus1) + } else { + // a2 = r[i+2], b2 = -t[i+2] + a2.Set(r) + b2.Mul(t, bigMinus1) + } + + // All done. + break + } + + ri.Set(r) + ti.Set(t) + } + + return a1, b1, a2, b2 +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/internal_test.go b/vendor/github.com/btcsuite/btcd/btcec/internal_test.go new file mode 100644 index 0000000000000000000000000000000000000000..23f35684a9c4ca8e1d2b263147db017d73a191d9 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/internal_test.go @@ -0,0 +1,82 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "math/big" +) + +const ( + TstPubkeyUncompressed = pubkeyUncompressed + TstPubkeyCompressed = pubkeyCompressed + TstPubkeyHybrid = pubkeyHybrid +) + +// TstRawInts allows the test package to get the integers from the internal +// field representation for ensuring correctness. It is only available during +// the tests. +func (f *fieldVal) TstRawInts() [10]uint32 { + return f.n +} + +// TstSetRawInts allows the test package to directly set the integers used by +// the internal field representation. It is only available during the tests. +func (f *fieldVal) TstSetRawInts(raw [10]uint32) *fieldVal { + for i := 0; i < len(raw); i++ { + f.n[i] = raw[i] + } + return f +} + +// TstFieldJacobianToBigAffine makes the internal fieldJacobianToBigAffine +// function available to the test package. +func (curve *KoblitzCurve) TstFieldJacobianToBigAffine(x, y, z *fieldVal) (*big.Int, *big.Int) { + return curve.fieldJacobianToBigAffine(x, y, z) +} + +// TstIsJacobianOnCurve returns boolean if the point (x,y,z) is on the curve. +func (curve *KoblitzCurve) TstIsJacobianOnCurve(x, y, z *fieldVal) bool { + // Elliptic curve equation for secp256k1 is: y^2 = x^3 + 7 + // In Jacobian coordinates, Y = y/z^3 and X = x/z^2 + // Thus: + // (y/z^3)^2 = (x/z^2)^3 + 7 + // y^2/z^6 = x^3/z^6 + 7 + // y^2 = x^3 + 7*z^6 + var y2, z2, x3, result fieldVal + y2.SquareVal(y).Normalize() + z2.SquareVal(z) + x3.SquareVal(x).Mul(x) + result.SquareVal(&z2).Mul(&z2).MulInt(7).Add(&x3).Normalize() + return y2.Equals(&result) +} + +// TstAddJacobian makes the internal addJacobian function available to the test +// package. +func (curve *KoblitzCurve) TstAddJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3 *fieldVal) { + curve.addJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3) +} + +// TstDoubleJacobian makes the internal doubleJacobian function available to the test +// package. +func (curve *KoblitzCurve) TstDoubleJacobian(x1, y1, z1, x3, y3, z3 *fieldVal) { + curve.doubleJacobian(x1, y1, z1, x3, y3, z3) +} + +// NewFieldVal returns a new field value set to 0. This is only available to +// the test package. +func NewFieldVal() *fieldVal { + return new(fieldVal) +} + +// TstNonceRFC6979 makes the nonceRFC6979 function available to the test package. +func TstNonceRFC6979(privkey *big.Int, hash []byte) *big.Int { + return nonceRFC6979(privkey, hash) +} + +// TstRemovePKCSPadding makes the internal removePKCSPadding function available +// to the test package. +func TstRemovePKCSPadding(src []byte) ([]byte, error) { + return removePKCSPadding(src) +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/precompute.go b/vendor/github.com/btcsuite/btcd/btcec/precompute.go new file mode 100644 index 0000000000000000000000000000000000000000..034cd55332152ad05e501813528c98e12123ce59 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/precompute.go @@ -0,0 +1,67 @@ +// Copyright 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "compress/zlib" + "encoding/base64" + "encoding/binary" + "io/ioutil" + "strings" +) + +//go:generate go run -tags gensecp256k1 genprecomps.go + +// loadS256BytePoints decompresses and deserializes the pre-computed byte points +// used to accelerate scalar base multiplication for the secp256k1 curve. This +// approach is used since it allows the compile to use significantly less ram +// and be performed much faster than it is with hard-coding the final in-memory +// data structure. At the same time, it is quite fast to generate the in-memory +// data structure at init time with this approach versus computing the table. +func loadS256BytePoints() error { + // There will be no byte points to load when generating them. + bp := secp256k1BytePoints + if len(bp) == 0 { + return nil + } + + // Decompress the pre-computed table used to accelerate scalar base + // multiplication. + decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(bp)) + r, err := zlib.NewReader(decoder) + if err != nil { + return err + } + serialized, err := ioutil.ReadAll(r) + if err != nil { + return err + } + + // Deserialize the precomputed byte points and set the curve to them. + offset := 0 + var bytePoints [32][256][3]fieldVal + for byteNum := 0; byteNum < 32; byteNum++ { + // All points in this window. + for i := 0; i < 256; i++ { + px := &bytePoints[byteNum][i][0] + py := &bytePoints[byteNum][i][1] + pz := &bytePoints[byteNum][i][2] + for i := 0; i < 10; i++ { + px.n[i] = binary.LittleEndian.Uint32(serialized[offset:]) + offset += 4 + } + for i := 0; i < 10; i++ { + py.n[i] = binary.LittleEndian.Uint32(serialized[offset:]) + offset += 4 + } + for i := 0; i < 10; i++ { + pz.n[i] = binary.LittleEndian.Uint32(serialized[offset:]) + offset += 4 + } + } + } + secp256k1.bytePoints = &bytePoints + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/privkey.go b/vendor/github.com/btcsuite/btcd/btcec/privkey.go new file mode 100644 index 0000000000000000000000000000000000000000..676a8c3fb010b730fd3ce5efa722294dc275fbd3 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/privkey.go @@ -0,0 +1,73 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "math/big" +) + +// PrivateKey wraps an ecdsa.PrivateKey as a convenience mainly for signing +// things with the the private key without having to directly import the ecdsa +// package. +type PrivateKey ecdsa.PrivateKey + +// PrivKeyFromBytes returns a private and public key for `curve' based on the +// private key passed as an argument as a byte slice. +func PrivKeyFromBytes(curve elliptic.Curve, pk []byte) (*PrivateKey, + *PublicKey) { + x, y := curve.ScalarBaseMult(pk) + + priv := &ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: curve, + X: x, + Y: y, + }, + D: new(big.Int).SetBytes(pk), + } + + return (*PrivateKey)(priv), (*PublicKey)(&priv.PublicKey) +} + +// NewPrivateKey is a wrapper for ecdsa.GenerateKey that returns a PrivateKey +// instead of the normal ecdsa.PrivateKey. +func NewPrivateKey(curve elliptic.Curve) (*PrivateKey, error) { + key, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + return nil, err + } + return (*PrivateKey)(key), nil +} + +// PubKey returns the PublicKey corresponding to this private key. +func (p *PrivateKey) PubKey() *PublicKey { + return (*PublicKey)(&p.PublicKey) +} + +// ToECDSA returns the private key as a *ecdsa.PrivateKey. +func (p *PrivateKey) ToECDSA() *ecdsa.PrivateKey { + return (*ecdsa.PrivateKey)(p) +} + +// Sign generates an ECDSA signature for the provided hash (which should be the result +// of hashing a larger message) using the private key. Produced signature +// is deterministic (same message and same key yield the same signature) and canonical +// in accordance with RFC6979 and BIP0062. +func (p *PrivateKey) Sign(hash []byte) (*Signature, error) { + return signRFC6979(p, hash) +} + +// PrivKeyBytesLen defines the length in bytes of a serialized private key. +const PrivKeyBytesLen = 32 + +// Serialize returns the private key number d as a big-endian binary-encoded +// number, padded to a length of 32 bytes. +func (p *PrivateKey) Serialize() []byte { + b := make([]byte, 0, PrivKeyBytesLen) + return paddedAppend(PrivKeyBytesLen, b, p.ToECDSA().D.Bytes()) +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/privkey_test.go b/vendor/github.com/btcsuite/btcd/btcec/privkey_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6fd787e26c436de45231a93851609da78d18ef60 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/privkey_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec_test + +import ( + "bytes" + "testing" + + "github.com/btcsuite/btcd/btcec" +) + +func TestPrivKeys(t *testing.T) { + tests := []struct { + name string + key []byte + }{ + { + name: "check curve", + key: []byte{ + 0xea, 0xf0, 0x2c, 0xa3, 0x48, 0xc5, 0x24, 0xe6, + 0x39, 0x26, 0x55, 0xba, 0x4d, 0x29, 0x60, 0x3c, + 0xd1, 0xa7, 0x34, 0x7d, 0x9d, 0x65, 0xcf, 0xe9, + 0x3c, 0xe1, 0xeb, 0xff, 0xdc, 0xa2, 0x26, 0x94, + }, + }, + } + + for _, test := range tests { + priv, pub := btcec.PrivKeyFromBytes(btcec.S256(), test.key) + + _, err := btcec.ParsePubKey( + pub.SerializeUncompressed(), btcec.S256()) + if err != nil { + t.Errorf("%s privkey: %v", test.name, err) + continue + } + + hash := []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9} + sig, err := priv.Sign(hash) + if err != nil { + t.Errorf("%s could not sign: %v", test.name, err) + continue + } + + if !sig.Verify(hash, pub) { + t.Errorf("%s could not verify: %v", test.name, err) + continue + } + + serializedKey := priv.Serialize() + if !bytes.Equal(serializedKey, test.key) { + t.Errorf("%s unexpected serialized bytes - got: %x, "+ + "want: %x", test.name, serializedKey, test.key) + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/pubkey.go b/vendor/github.com/btcsuite/btcd/btcec/pubkey.go new file mode 100644 index 0000000000000000000000000000000000000000..be6f8509787a6b59be29bad9e10d8795d5764e80 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/pubkey.go @@ -0,0 +1,172 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "crypto/ecdsa" + "errors" + "fmt" + "math/big" +) + +// These constants define the lengths of serialized public keys. +const ( + PubKeyBytesLenCompressed = 33 + PubKeyBytesLenUncompressed = 65 + PubKeyBytesLenHybrid = 65 +) + +func isOdd(a *big.Int) bool { + return a.Bit(0) == 1 +} + +// decompressPoint decompresses a point on the given curve given the X point and +// the solution to use. +func decompressPoint(curve *KoblitzCurve, x *big.Int, ybit bool) (*big.Int, error) { + // TODO(oga) This will probably only work for secp256k1 due to + // optimizations. + + // Y = +-sqrt(x^3 + B) + x3 := new(big.Int).Mul(x, x) + x3.Mul(x3, x) + x3.Add(x3, curve.Params().B) + + // now calculate sqrt mod p of x2 + B + // This code used to do a full sqrt based on tonelli/shanks, + // but this was replaced by the algorithms referenced in + // https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294 + y := new(big.Int).Exp(x3, curve.QPlus1Div4(), curve.Params().P) + + if ybit != isOdd(y) { + y.Sub(curve.Params().P, y) + } + if ybit != isOdd(y) { + return nil, fmt.Errorf("ybit doesn't match oddness") + } + return y, nil +} + +const ( + pubkeyCompressed byte = 0x2 // y_bit + x coord + pubkeyUncompressed byte = 0x4 // x coord + y coord + pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord +) + +// ParsePubKey parses a public key for a koblitz curve from a bytestring into a +// ecdsa.Publickey, verifying that it is valid. It supports compressed, +// uncompressed and hybrid signature formats. +func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err error) { + pubkey := PublicKey{} + pubkey.Curve = curve + + if len(pubKeyStr) == 0 { + return nil, errors.New("pubkey string is empty") + } + + format := pubKeyStr[0] + ybit := (format & 0x1) == 0x1 + format &= ^byte(0x1) + + switch len(pubKeyStr) { + case PubKeyBytesLenUncompressed: + if format != pubkeyUncompressed && format != pubkeyHybrid { + return nil, fmt.Errorf("invalid magic in pubkey str: "+ + "%d", pubKeyStr[0]) + } + + pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33]) + pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:]) + // hybrid keys have extra information, make use of it. + if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) { + return nil, fmt.Errorf("ybit doesn't match oddness") + } + case PubKeyBytesLenCompressed: + // format is 0x2 | solution, <X coordinate> + // solution determines which solution of the curve we use. + /// y^2 = x^3 + Curve.B + if format != pubkeyCompressed { + return nil, fmt.Errorf("invalid magic in compressed "+ + "pubkey string: %d", pubKeyStr[0]) + } + pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33]) + pubkey.Y, err = decompressPoint(curve, pubkey.X, ybit) + if err != nil { + return nil, err + } + default: // wrong! + return nil, fmt.Errorf("invalid pub key length %d", + len(pubKeyStr)) + } + + if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 { + return nil, fmt.Errorf("pubkey X parameter is >= to P") + } + if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 { + return nil, fmt.Errorf("pubkey Y parameter is >= to P") + } + if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) { + return nil, fmt.Errorf("pubkey isn't on secp256k1 curve") + } + return &pubkey, nil +} + +// PublicKey is an ecdsa.PublicKey with additional functions to +// serialize in uncompressed, compressed, and hybrid formats. +type PublicKey ecdsa.PublicKey + +// ToECDSA returns the public key as a *ecdsa.PublicKey. +func (p *PublicKey) ToECDSA() *ecdsa.PublicKey { + return (*ecdsa.PublicKey)(p) +} + +// SerializeUncompressed serializes a public key in a 65-byte uncompressed +// format. +func (p *PublicKey) SerializeUncompressed() []byte { + b := make([]byte, 0, PubKeyBytesLenUncompressed) + b = append(b, pubkeyUncompressed) + b = paddedAppend(32, b, p.X.Bytes()) + return paddedAppend(32, b, p.Y.Bytes()) +} + +// SerializeCompressed serializes a public key in a 33-byte compressed format. +func (p *PublicKey) SerializeCompressed() []byte { + b := make([]byte, 0, PubKeyBytesLenCompressed) + format := pubkeyCompressed + if isOdd(p.Y) { + format |= 0x1 + } + b = append(b, format) + return paddedAppend(32, b, p.X.Bytes()) +} + +// SerializeHybrid serializes a public key in a 65-byte hybrid format. +func (p *PublicKey) SerializeHybrid() []byte { + b := make([]byte, 0, PubKeyBytesLenHybrid) + format := pubkeyHybrid + if isOdd(p.Y) { + format |= 0x1 + } + b = append(b, format) + b = paddedAppend(32, b, p.X.Bytes()) + return paddedAppend(32, b, p.Y.Bytes()) +} + +// IsEqual compares this PublicKey instance to the one passed, returning true if +// both PublicKeys are equivalent. A PublicKey is equivalent to another, if they +// both have the same X and Y coordinate. +func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool { + return p.X.Cmp(otherPubKey.X) == 0 && + p.Y.Cmp(otherPubKey.Y) == 0 +} + +// paddedAppend appends the src byte slice to dst, returning the new slice. +// If the length of the source is smaller than the passed size, leading zero +// bytes are appended to the dst slice before appending src. +func paddedAppend(size uint, dst, src []byte) []byte { + for i := 0; i < int(size)-len(src); i++ { + dst = append(dst, 0) + } + return append(dst, src...) +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/pubkey_test.go b/vendor/github.com/btcsuite/btcd/btcec/pubkey_test.go new file mode 100644 index 0000000000000000000000000000000000000000..16c6f2572053eb6f5a15540a2354ce4e91d69326 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/pubkey_test.go @@ -0,0 +1,285 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec_test + +import ( + "bytes" + "testing" + + "github.com/btcsuite/btcd/btcec" + "github.com/davecgh/go-spew/spew" +) + +type pubKeyTest struct { + name string + key []byte + format byte + isValid bool +} + +var pubKeyTests = []pubKeyTest{ + // pubkey from bitcoin blockchain tx + // 0437cd7f8525ceed2324359c2d0ba26006d92d85 + { + name: "uncompressed ok", + key: []byte{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: true, + format: btcec.TstPubkeyUncompressed, + }, + { + name: "uncompressed x changed", + key: []byte{0x04, 0x15, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: false, + }, + { + name: "uncompressed y changed", + key: []byte{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa4, + }, + isValid: false, + }, + { + name: "uncompressed claims compressed", + key: []byte{0x03, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: false, + }, + { + name: "uncompressed as hybrid ok", + key: []byte{0x07, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: true, + format: btcec.TstPubkeyHybrid, + }, + { + name: "uncompressed as hybrid wrong", + key: []byte{0x06, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: false, + }, + // from tx 0b09c51c51ff762f00fb26217269d2a18e77a4fa87d69b3c363ab4df16543f20 + { + name: "compressed ok (ybit = 0)", + key: []byte{0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, + 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, + 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, + 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, + }, + isValid: true, + format: btcec.TstPubkeyCompressed, + }, + // from tx fdeb8e72524e8dab0da507ddbaf5f88fe4a933eb10a66bc4745bb0aa11ea393c + { + name: "compressed ok (ybit = 1)", + key: []byte{0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, + 0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34, + 0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, + 0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e, + }, + isValid: true, + format: btcec.TstPubkeyCompressed, + }, + { + name: "compressed claims uncompressed (ybit = 0)", + key: []byte{0x04, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, + 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, + 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, + 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, + }, + isValid: false, + }, + { + name: "compressed claims uncompressed (ybit = 1)", + key: []byte{0x05, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, + 0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34, + 0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, + 0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e, + }, + isValid: false, + }, + { + name: "wrong length)", + key: []byte{0x05}, + isValid: false, + }, + { + name: "X == P", + key: []byte{0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: false, + }, + { + name: "X > P", + key: []byte{0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFD, 0x2F, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, + 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, + 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, + 0xb4, 0x12, 0xa3, + }, + isValid: false, + }, + { + name: "Y == P", + key: []byte{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, + 0xFF, 0xFC, 0x2F, + }, + isValid: false, + }, + { + name: "Y > P", + key: []byte{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, + 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, + 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, + 0xFF, 0xFD, 0x2F, + }, + isValid: false, + }, + { + name: "hybrid", + key: []byte{0x06, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, + 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07, + 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, + 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98, 0x48, 0x3a, + 0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb, + 0xfc, 0x0e, 0x11, 0x08, 0xa8, 0xfd, 0x17, 0xb4, 0x48, + 0xa6, 0x85, 0x54, 0x19, 0x9c, 0x47, 0xd0, 0x8f, 0xfb, + 0x10, 0xd4, 0xb8, + }, + format: btcec.TstPubkeyHybrid, + isValid: true, + }, +} + +func TestPubKeys(t *testing.T) { + for _, test := range pubKeyTests { + pk, err := btcec.ParsePubKey(test.key, btcec.S256()) + if err != nil { + if test.isValid { + t.Errorf("%s pubkey failed when shouldn't %v", + test.name, err) + } + continue + } + if !test.isValid { + t.Errorf("%s counted as valid when it should fail", + test.name) + continue + } + var pkStr []byte + switch test.format { + case btcec.TstPubkeyUncompressed: + pkStr = (*btcec.PublicKey)(pk).SerializeUncompressed() + case btcec.TstPubkeyCompressed: + pkStr = (*btcec.PublicKey)(pk).SerializeCompressed() + case btcec.TstPubkeyHybrid: + pkStr = (*btcec.PublicKey)(pk).SerializeHybrid() + } + if !bytes.Equal(test.key, pkStr) { + t.Errorf("%s pubkey: serialized keys do not match.", + test.name) + spew.Dump(test.key) + spew.Dump(pkStr) + } + } +} + +func TestPublicKeyIsEqual(t *testing.T) { + pubKey1, err := btcec.ParsePubKey( + []byte{0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, + 0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34, + 0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, + 0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e, + }, + btcec.S256(), + ) + if err != nil { + t.Fatalf("failed to parse raw bytes for pubKey1: %v", err) + } + + pubKey2, err := btcec.ParsePubKey( + []byte{0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, + 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, + 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, + 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, + }, + btcec.S256(), + ) + if err != nil { + t.Fatalf("failed to parse raw bytes for pubKey2: %v", err) + } + + if !pubKey1.IsEqual(pubKey1) { + t.Fatalf("value of IsEqual is incorrect, %v is "+ + "equal to %v", pubKey1, pubKey1) + } + + if pubKey1.IsEqual(pubKey2) { + t.Fatalf("value of IsEqual is incorrect, %v is not "+ + "equal to %v", pubKey1, pubKey2) + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/secp256k1.go b/vendor/github.com/btcsuite/btcd/btcec/secp256k1.go new file mode 100644 index 0000000000000000000000000000000000000000..5e7bffe4d9bb132c68fc761c36f896a9a0149e59 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/secp256k1.go @@ -0,0 +1,10 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +// Auto-generated file (see genprecomps.go) +// DO NOT EDIT + +var secp256k1BytePoints = "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" diff --git a/vendor/github.com/btcsuite/btcd/btcec/signature.go b/vendor/github.com/btcsuite/btcd/btcec/signature.go new file mode 100644 index 0000000000000000000000000000000000000000..ab5a345c4cd3b71c51e5bc362dd46170f29d2e64 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/signature.go @@ -0,0 +1,540 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/hmac" + "errors" + "fmt" + "hash" + "math/big" + + "github.com/btcsuite/fastsha256" +) + +// Errors returned by canonicalPadding. +var ( + errNegativeValue = errors.New("value may be interpreted as negative") + errExcessivelyPaddedValue = errors.New("value is excessively padded") +) + +// Signature is a type representing an ecdsa signature. +type Signature struct { + R *big.Int + S *big.Int +} + +var ( + // Curve order and halforder, used to tame ECDSA malleability (see BIP-0062) + order = new(big.Int).Set(S256().N) + halforder = new(big.Int).Rsh(order, 1) + + // Used in RFC6979 implementation when testing the nonce for correctness + one = big.NewInt(1) + + // oneInitializer is used to fill a byte slice with byte 0x01. It is provided + // here to avoid the need to create it multiple times. + oneInitializer = []byte{0x01} +) + +// Serialize returns the ECDSA signature in the more strict DER format. Note +// that the serialized bytes returned do not include the appended hash type +// used in Bitcoin signature scripts. +// +// encoding/asn1 is broken so we hand roll this output: +// +// 0x30 <length> 0x02 <length r> r 0x02 <length s> s +func (sig *Signature) Serialize() []byte { + // low 'S' malleability breaker + sigS := sig.S + if sigS.Cmp(halforder) == 1 { + sigS = new(big.Int).Sub(order, sigS) + } + // Ensure the encoded bytes for the r and s values are canonical and + // thus suitable for DER encoding. + rb := canonicalizeInt(sig.R) + sb := canonicalizeInt(sigS) + + // total length of returned signature is 1 byte for each magic and + // length (6 total), plus lengths of r and s + length := 6 + len(rb) + len(sb) + b := make([]byte, length, length) + + b[0] = 0x30 + b[1] = byte(length - 2) + b[2] = 0x02 + b[3] = byte(len(rb)) + offset := copy(b[4:], rb) + 4 + b[offset] = 0x02 + b[offset+1] = byte(len(sb)) + copy(b[offset+2:], sb) + return b +} + +// Verify calls ecdsa.Verify to verify the signature of hash using the public +// key. It returns true if the signature is valid, false otherwise. +func (sig *Signature) Verify(hash []byte, pubKey *PublicKey) bool { + return ecdsa.Verify(pubKey.ToECDSA(), hash, sig.R, sig.S) +} + +// IsEqual compares this Signature instance to the one passed, returning true +// if both Signatures are equivalent. A signature is equivalent to another, if +// they both have the same scalar value for R and S. +func (sig *Signature) IsEqual(otherSig *Signature) bool { + return sig.R.Cmp(otherSig.R) == 0 && + sig.S.Cmp(otherSig.S) == 0 +} + +func parseSig(sigStr []byte, curve elliptic.Curve, der bool) (*Signature, error) { + // Originally this code used encoding/asn1 in order to parse the + // signature, but a number of problems were found with this approach. + // Despite the fact that signatures are stored as DER, the difference + // between go's idea of a bignum (and that they have sign) doesn't agree + // with the openssl one (where they do not). The above is true as of + // Go 1.1. In the end it was simpler to rewrite the code to explicitly + // understand the format which is this: + // 0x30 <length of whole message> <0x02> <length of R> <R> 0x2 + // <length of S> <S>. + + signature := &Signature{} + + // minimal message is when both numbers are 1 bytes. adding up to: + // 0x30 + len + 0x02 + 0x01 + <byte> + 0x2 + 0x01 + <byte> + if len(sigStr) < 8 { + return nil, errors.New("malformed signature: too short") + } + // 0x30 + index := 0 + if sigStr[index] != 0x30 { + return nil, errors.New("malformed signature: no header magic") + } + index++ + // length of remaining message + siglen := sigStr[index] + index++ + if int(siglen+2) > len(sigStr) { + return nil, errors.New("malformed signature: bad length") + } + // trim the slice we're working on so we only look at what matters. + sigStr = sigStr[:siglen+2] + + // 0x02 + if sigStr[index] != 0x02 { + return nil, + errors.New("malformed signature: no 1st int marker") + } + index++ + + // Length of signature R. + rLen := int(sigStr[index]) + // must be positive, must be able to fit in another 0x2, <len> <s> + // hence the -3. We assume that the length must be at least one byte. + index++ + if rLen <= 0 || rLen > len(sigStr)-index-3 { + return nil, errors.New("malformed signature: bogus R length") + } + + // Then R itself. + rBytes := sigStr[index : index+rLen] + if der { + switch err := canonicalPadding(rBytes); err { + case errNegativeValue: + return nil, errors.New("signature R is negative") + case errExcessivelyPaddedValue: + return nil, errors.New("signature R is excessively padded") + } + } + signature.R = new(big.Int).SetBytes(rBytes) + index += rLen + // 0x02. length already checked in previous if. + if sigStr[index] != 0x02 { + return nil, errors.New("malformed signature: no 2nd int marker") + } + index++ + + // Length of signature S. + sLen := int(sigStr[index]) + index++ + // S should be the rest of the string. + if sLen <= 0 || sLen > len(sigStr)-index { + return nil, errors.New("malformed signature: bogus S length") + } + + // Then S itself. + sBytes := sigStr[index : index+sLen] + if der { + switch err := canonicalPadding(sBytes); err { + case errNegativeValue: + return nil, errors.New("signature S is negative") + case errExcessivelyPaddedValue: + return nil, errors.New("signature S is excessively padded") + } + } + signature.S = new(big.Int).SetBytes(sBytes) + index += sLen + + // sanity check length parsing + if index != len(sigStr) { + return nil, fmt.Errorf("malformed signature: bad final length %v != %v", + index, len(sigStr)) + } + + // Verify also checks this, but we can be more sure that we parsed + // correctly if we verify here too. + // FWIW the ecdsa spec states that R and S must be | 1, N - 1 | + // but crypto/ecdsa only checks for Sign != 0. Mirror that. + if signature.R.Sign() != 1 { + return nil, errors.New("signature R isn't 1 or more") + } + if signature.S.Sign() != 1 { + return nil, errors.New("signature S isn't 1 or more") + } + if signature.R.Cmp(curve.Params().N) >= 0 { + return nil, errors.New("signature R is >= curve.N") + } + if signature.S.Cmp(curve.Params().N) >= 0 { + return nil, errors.New("signature S is >= curve.N") + } + + return signature, nil +} + +// ParseSignature parses a signature in BER format for the curve type `curve' +// into a Signature type, perfoming some basic sanity checks. If parsing +// according to the more strict DER format is needed, use ParseDERSignature. +func ParseSignature(sigStr []byte, curve elliptic.Curve) (*Signature, error) { + return parseSig(sigStr, curve, false) +} + +// ParseDERSignature parses a signature in DER format for the curve type +// `curve` into a Signature type. If parsing according to the less strict +// BER format is needed, use ParseSignature. +func ParseDERSignature(sigStr []byte, curve elliptic.Curve) (*Signature, error) { + return parseSig(sigStr, curve, true) +} + +// canonicalizeInt returns the bytes for the passed big integer adjusted as +// necessary to ensure that a big-endian encoded integer can't possibly be +// misinterpreted as a negative number. This can happen when the most +// significant bit is set, so it is padded by a leading zero byte in this case. +// Also, the returned bytes will have at least a single byte when the passed +// value is 0. This is required for DER encoding. +func canonicalizeInt(val *big.Int) []byte { + b := val.Bytes() + if len(b) == 0 { + b = []byte{0x00} + } + if b[0]&0x80 != 0 { + paddedBytes := make([]byte, len(b)+1) + copy(paddedBytes[1:], b) + b = paddedBytes + } + return b +} + +// canonicalPadding checks whether a big-endian encoded integer could +// possibly be misinterpreted as a negative number (even though OpenSSL +// treats all numbers as unsigned), or if there is any unnecessary +// leading zero padding. +func canonicalPadding(b []byte) error { + switch { + case b[0]&0x80 == 0x80: + return errNegativeValue + case len(b) > 1 && b[0] == 0x00 && b[1]&0x80 != 0x80: + return errExcessivelyPaddedValue + default: + return nil + } +} + +// hashToInt converts a hash value to an integer. There is some disagreement +// about how this is done. [NSA] suggests that this is done in the obvious +// manner, but [SECG] truncates the hash to the bit-length of the curve order +// first. We follow [SECG] because that's what OpenSSL does. Additionally, +// OpenSSL right shifts excess bits from the number if the hash is too large +// and we mirror that too. +// This is borrowed from crypto/ecdsa. +func hashToInt(hash []byte, c elliptic.Curve) *big.Int { + orderBits := c.Params().N.BitLen() + orderBytes := (orderBits + 7) / 8 + if len(hash) > orderBytes { + hash = hash[:orderBytes] + } + + ret := new(big.Int).SetBytes(hash) + excess := len(hash)*8 - orderBits + if excess > 0 { + ret.Rsh(ret, uint(excess)) + } + return ret +} + +// recoverKeyFromSignature recoves a public key from the signature "sig" on the +// given message hash "msg". Based on the algorithm found in section 5.1.5 of +// SEC 1 Ver 2.0, page 47-48 (53 and 54 in the pdf). This performs the details +// in the inner loop in Step 1. The counter provided is actually the j parameter +// of the loop * 2 - on the first iteration of j we do the R case, else the -R +// case in step 1.6. This counter is used in the bitcoin compressed signature +// format and thus we match bitcoind's behaviour here. +func recoverKeyFromSignature(curve *KoblitzCurve, sig *Signature, msg []byte, + iter int, doChecks bool) (*PublicKey, error) { + // 1.1 x = (n * i) + r + Rx := new(big.Int).Mul(curve.Params().N, + new(big.Int).SetInt64(int64(iter/2))) + Rx.Add(Rx, sig.R) + if Rx.Cmp(curve.Params().P) != -1 { + return nil, errors.New("calculated Rx is larger than curve P") + } + + // convert 02<Rx> to point R. (step 1.2 and 1.3). If we are on an odd + // iteration then 1.6 will be done with -R, so we calculate the other + // term when uncompressing the point. + Ry, err := decompressPoint(curve, Rx, iter%2 == 1) + if err != nil { + return nil, err + } + + // 1.4 Check n*R is point at infinity + if doChecks { + nRx, nRy := curve.ScalarMult(Rx, Ry, curve.Params().N.Bytes()) + if nRx.Sign() != 0 || nRy.Sign() != 0 { + return nil, errors.New("n*R does not equal the point at infinity") + } + } + + // 1.5 calculate e from message using the same algorithm as ecdsa + // signature calculation. + e := hashToInt(msg, curve) + + // Step 1.6.1: + // We calculate the two terms sR and eG separately multiplied by the + // inverse of r (from the signature). We then add them to calculate + // Q = r^-1(sR-eG) + invr := new(big.Int).ModInverse(sig.R, curve.Params().N) + + // first term. + invrS := new(big.Int).Mul(invr, sig.S) + invrS.Mod(invrS, curve.Params().N) + sRx, sRy := curve.ScalarMult(Rx, Ry, invrS.Bytes()) + + // second term. + e.Neg(e) + e.Mod(e, curve.Params().N) + e.Mul(e, invr) + e.Mod(e, curve.Params().N) + minuseGx, minuseGy := curve.ScalarBaseMult(e.Bytes()) + + // TODO(oga) this would be faster if we did a mult and add in one + // step to prevent the jacobian conversion back and forth. + Qx, Qy := curve.Add(sRx, sRy, minuseGx, minuseGy) + + return &PublicKey{ + Curve: curve, + X: Qx, + Y: Qy, + }, nil +} + +// SignCompact produces a compact signature of the data in hash with the given +// private key on the given koblitz curve. The isCompressed parameter should +// be used to detail if the given signature should reference a compressed +// public key or not. If successful the bytes of the compact signature will be +// returned in the format: +// <(byte of 27+public key solution)+4 if compressed >< padded bytes for signature R><padded bytes for signature S> +// where the R and S parameters are padde up to the bitlengh of the curve. +func SignCompact(curve *KoblitzCurve, key *PrivateKey, + hash []byte, isCompressedKey bool) ([]byte, error) { + sig, err := key.Sign(hash) + if err != nil { + return nil, err + } + + // bitcoind checks the bit length of R and S here. The ecdsa signature + // algorithm returns R and S mod N therefore they will be the bitsize of + // the curve, and thus correctly sized. + for i := 0; i < (curve.H+1)*2; i++ { + pk, err := recoverKeyFromSignature(curve, sig, hash, i, true) + if err == nil && pk.X.Cmp(key.X) == 0 && pk.Y.Cmp(key.Y) == 0 { + result := make([]byte, 1, 2*curve.byteSize+1) + result[0] = 27 + byte(i) + if isCompressedKey { + result[0] += 4 + } + // Not sure this needs rounding but safer to do so. + curvelen := (curve.BitSize + 7) / 8 + + // Pad R and S to curvelen if needed. + bytelen := (sig.R.BitLen() + 7) / 8 + if bytelen < curvelen { + result = append(result, + make([]byte, curvelen-bytelen)...) + } + result = append(result, sig.R.Bytes()...) + + bytelen = (sig.S.BitLen() + 7) / 8 + if bytelen < curvelen { + result = append(result, + make([]byte, curvelen-bytelen)...) + } + result = append(result, sig.S.Bytes()...) + + return result, nil + } + } + + return nil, errors.New("no valid solution for pubkey found") +} + +// RecoverCompact verifies the compact signature "signature" of "hash" for the +// Koblitz curve in "curve". If the signature matches then the recovered public +// key will be returned as well as a boolen if the original key was compressed +// or not, else an error will be returned. +func RecoverCompact(curve *KoblitzCurve, signature, + hash []byte) (*PublicKey, bool, error) { + bitlen := (curve.BitSize + 7) / 8 + if len(signature) != 1+bitlen*2 { + return nil, false, errors.New("invalid compact signature size") + } + + iteration := int((signature[0] - 27) & ^byte(4)) + + // format is <header byte><bitlen R><bitlen S> + sig := &Signature{ + R: new(big.Int).SetBytes(signature[1 : bitlen+1]), + S: new(big.Int).SetBytes(signature[bitlen+1:]), + } + // The iteration used here was encoded + key, err := recoverKeyFromSignature(curve, sig, hash, iteration, false) + if err != nil { + return nil, false, err + } + + return key, ((signature[0] - 27) & 4) == 4, nil +} + +// signRFC6979 generates a deterministic ECDSA signature according to RFC 6979 and BIP 62. +func signRFC6979(privateKey *PrivateKey, hash []byte) (*Signature, error) { + + privkey := privateKey.ToECDSA() + N := order + k := nonceRFC6979(privkey.D, hash) + inv := new(big.Int).ModInverse(k, N) + r, _ := privkey.Curve.ScalarBaseMult(k.Bytes()) + if r.Cmp(N) == 1 { + r.Sub(r, N) + } + + if r.Sign() == 0 { + return nil, errors.New("calculated R is zero") + } + + e := hashToInt(hash, privkey.Curve) + s := new(big.Int).Mul(privkey.D, r) + s.Add(s, e) + s.Mul(s, inv) + s.Mod(s, N) + + if s.Cmp(halforder) == 1 { + s.Sub(N, s) + } + if s.Sign() == 0 { + return nil, errors.New("calculated S is zero") + } + return &Signature{R: r, S: s}, nil +} + +// nonceRFC6979 generates an ECDSA nonce (`k`) deterministically according to RFC 6979. +// It takes a 32-byte hash as an input and returns 32-byte nonce to be used in ECDSA algorithm. +func nonceRFC6979(privkey *big.Int, hash []byte) *big.Int { + + curve := S256() + q := curve.Params().N + x := privkey + alg := fastsha256.New + + qlen := q.BitLen() + holen := alg().Size() + rolen := (qlen + 7) >> 3 + bx := append(int2octets(x, rolen), bits2octets(hash, curve, rolen)...) + + // Step B + v := bytes.Repeat(oneInitializer, holen) + + // Step C (Go zeroes the all allocated memory) + k := make([]byte, holen) + + // Step D + k = mac(alg, k, append(append(v, 0x00), bx...)) + + // Step E + v = mac(alg, k, v) + + // Step F + k = mac(alg, k, append(append(v, 0x01), bx...)) + + // Step G + v = mac(alg, k, v) + + // Step H + for { + // Step H1 + var t []byte + + // Step H2 + for len(t)*8 < qlen { + v = mac(alg, k, v) + t = append(t, v...) + } + + // Step H3 + secret := hashToInt(t, curve) + if secret.Cmp(one) >= 0 && secret.Cmp(q) < 0 { + return secret + } + k = mac(alg, k, append(v, 0x00)) + v = mac(alg, k, v) + } +} + +// mac returns an HMAC of the given key and message. +func mac(alg func() hash.Hash, k, m []byte) []byte { + h := hmac.New(alg, k) + h.Write(m) + return h.Sum(nil) +} + +// https://tools.ietf.org/html/rfc6979#section-2.3.3 +func int2octets(v *big.Int, rolen int) []byte { + out := v.Bytes() + + // left pad with zeros if it's too short + if len(out) < rolen { + out2 := make([]byte, rolen) + copy(out2[rolen-len(out):], out) + return out2 + } + + // drop most significant bytes if it's too long + if len(out) > rolen { + out2 := make([]byte, rolen) + copy(out2, out[len(out)-rolen:]) + return out2 + } + + return out +} + +// https://tools.ietf.org/html/rfc6979#section-2.3.4 +func bits2octets(in []byte, curve elliptic.Curve, rolen int) []byte { + z1 := hashToInt(in, curve) + z2 := new(big.Int).Sub(z1, curve.Params().N) + if z2.Sign() < 0 { + return int2octets(z1, rolen) + } + return int2octets(z2, rolen) +} diff --git a/vendor/github.com/btcsuite/btcd/btcec/signature_test.go b/vendor/github.com/btcsuite/btcd/btcec/signature_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c13bbe0f9bd7b36e3e3899f8afd030f2b36dd8ce --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcec/signature_test.go @@ -0,0 +1,611 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec_test + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "fmt" + "math/big" + "testing" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/fastsha256" +) + +type signatureTest struct { + name string + sig []byte + der bool + isValid bool +} + +// decodeHex decodes the passed hex string and returns the resulting bytes. It +// panics if an error occurs. This is only used in the tests as a helper since +// the only way it can fail is if there is an error in the test source code. +func decodeHex(hexStr string) []byte { + b, err := hex.DecodeString(hexStr) + if err != nil { + panic("invalid hex string in test source: err " + err.Error() + + ", hex: " + hexStr) + } + + return b +} + +var signatureTests = []signatureTest{ + // signatures from bitcoin blockchain tx + // 0437cd7f8525ceed2324359c2d0ba26006d92d85 + { + name: "valid signature.", + sig: []byte{0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: true, + }, + { + name: "empty.", + sig: []byte{}, + isValid: false, + }, + { + name: "bad magic.", + sig: []byte{0x31, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: false, + }, + { + name: "bad 1st int marker magic.", + sig: []byte{0x30, 0x44, 0x03, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: false, + }, + { + name: "bad 2nd int marker.", + sig: []byte{0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x03, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: false, + }, + { + name: "short len", + sig: []byte{0x30, 0x43, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: false, + }, + { + name: "long len", + sig: []byte{0x30, 0x45, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: false, + }, + { + name: "long X", + sig: []byte{0x30, 0x44, 0x02, 0x42, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: false, + }, + { + name: "long Y", + sig: []byte{0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x21, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: false, + }, + { + name: "short Y", + sig: []byte{0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x19, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: false, + }, + { + name: "trailing crap.", + sig: []byte{0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, 0x01, + }, + der: true, + + // This test is now passing (used to be failing) because there + // are signatures in the blockchain that have trailing zero + // bytes before the hashtype. So ParseSignature was fixed to + // permit buffers with trailing nonsense after the actual + // signature. + isValid: true, + }, + { + name: "X == N ", + sig: []byte{0x30, 0x44, 0x02, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, + 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, + 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: false, + }, + { + name: "X == N ", + sig: []byte{0x30, 0x44, 0x02, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, + 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, + 0x42, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: false, + isValid: false, + }, + { + name: "Y == N", + sig: []byte{0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, + 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41, + }, + der: true, + isValid: false, + }, + { + name: "Y > N", + sig: []byte{0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, + 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x42, + }, + der: false, + isValid: false, + }, + { + name: "0 len X.", + sig: []byte{0x30, 0x24, 0x02, 0x00, 0x02, 0x20, 0x18, 0x15, + 0x22, 0xec, 0x8e, 0xca, 0x07, 0xde, 0x48, 0x60, 0xa4, + 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, + 0xbb, 0xac, 0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, + 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: false, + }, + { + name: "0 len Y.", + sig: []byte{0x30, 0x24, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x00, + }, + der: true, + isValid: false, + }, + { + name: "extra R padding.", + sig: []byte{0x30, 0x45, 0x02, 0x21, 0x00, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: false, + }, + { + name: "extra S padding.", + sig: []byte{0x30, 0x45, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x21, 0x00, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, + 0x07, 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, + 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, + 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + der: true, + isValid: false, + }, + // Standard checks (in BER format, without checking for 'canonical' DER + // signatures) don't test for negative numbers here because there isn't + // a way that is the same between openssl and go that will mark a number + // as negative. The Go ASN.1 parser marks numbers as negative when + // openssl does not (it doesn't handle negative numbers that I can tell + // at all. When not parsing DER signatures, which is done by by bitcoind + // when accepting transactions into its mempool, we otherwise only check + // for the coordinates being zero. + { + name: "X == 0", + sig: []byte{0x30, 0x25, 0x02, 0x01, 0x00, 0x02, 0x20, 0x18, + 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07, 0xde, 0x48, 0x60, + 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, + 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, + 0x76, 0x8d, 0x1d, 0x09, + }, + der: false, + isValid: false, + }, + { + name: "Y == 0.", + sig: []byte{0x30, 0x25, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, + 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, + 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x01, 0x00, + }, + der: false, + isValid: false, + }, +} + +func TestSignatures(t *testing.T) { + for _, test := range signatureTests { + var err error + if test.der { + _, err = btcec.ParseDERSignature(test.sig, btcec.S256()) + } else { + _, err = btcec.ParseSignature(test.sig, btcec.S256()) + } + if err != nil { + if test.isValid { + t.Errorf("%s signature failed when shouldn't %v", + test.name, err) + } /* else { + t.Errorf("%s got error %v", test.name, err) + } */ + continue + } + if !test.isValid { + t.Errorf("%s counted as valid when it should fail", + test.name) + } + } +} + +// TestSignatureSerialize ensures that serializing signatures works as expected. +func TestSignatureSerialize(t *testing.T) { + tests := []struct { + name string + ecsig *btcec.Signature + expected []byte + }{ + // signature from bitcoin blockchain tx + // 0437cd7f8525ceed2324359c2d0ba26006d92d85 + { + "valid 1 - r and s most significant bits are zero", + &btcec.Signature{ + R: fromHex("4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41"), + S: fromHex("181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09"), + }, + []byte{ + 0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, + 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, + 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, + 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15, + 0x22, 0xec, 0x8e, 0xca, 0x07, 0xde, 0x48, 0x60, + 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, + 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, 0x22, + 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, + }, + }, + // signature from bitcoin blockchain tx + // cb00f8a0573b18faa8c4f467b049f5d202bf1101d9ef2633bc611be70376a4b4 + { + "valid 2 - r most significant bit is one", + &btcec.Signature{ + R: fromHex("0082235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"), + S: fromHex("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"), + }, + []byte{ + 0x30, 0x45, 0x02, 0x21, 0x00, 0x82, 0x23, 0x5e, + 0x21, 0xa2, 0x30, 0x00, 0x22, 0x73, 0x8d, 0xab, + 0xb8, 0xe1, 0xbb, 0xd9, 0xd1, 0x9c, 0xfb, 0x1e, + 0x7a, 0xb8, 0xc3, 0x0a, 0x23, 0xb0, 0xaf, 0xbb, + 0x8d, 0x17, 0x8a, 0xbc, 0xf3, 0x02, 0x20, 0x24, + 0xbf, 0x68, 0xe2, 0x56, 0xc5, 0x34, 0xdd, 0xfa, + 0xf9, 0x66, 0xbf, 0x90, 0x8d, 0xeb, 0x94, 0x43, + 0x05, 0x59, 0x6f, 0x7b, 0xdc, 0xc3, 0x8d, 0x69, + 0xac, 0xad, 0x7f, 0x9c, 0x86, 0x87, 0x24, + }, + }, + // signature from bitcoin blockchain tx + // fda204502a3345e08afd6af27377c052e77f1fefeaeb31bdd45f1e1237ca5470 + { + "valid 3 - s most significant bit is one", + &btcec.Signature{ + R: fromHex("1cadddc2838598fee7dc35a12b340c6bde8b389f7bfd19a1252a17c4b5ed2d71"), + S: new(big.Int).Add(fromHex("00c1a251bbecb14b058a8bd77f65de87e51c47e95904f4c0e9d52eddc21c1415ac"), btcec.S256().N), + }, + []byte{ + 0x30, 0x45, 0x02, 0x20, 0x1c, 0xad, 0xdd, 0xc2, + 0x83, 0x85, 0x98, 0xfe, 0xe7, 0xdc, 0x35, 0xa1, + 0x2b, 0x34, 0x0c, 0x6b, 0xde, 0x8b, 0x38, 0x9f, + 0x7b, 0xfd, 0x19, 0xa1, 0x25, 0x2a, 0x17, 0xc4, + 0xb5, 0xed, 0x2d, 0x71, 0x02, 0x21, 0x00, 0xc1, + 0xa2, 0x51, 0xbb, 0xec, 0xb1, 0x4b, 0x05, 0x8a, + 0x8b, 0xd7, 0x7f, 0x65, 0xde, 0x87, 0xe5, 0x1c, + 0x47, 0xe9, 0x59, 0x04, 0xf4, 0xc0, 0xe9, 0xd5, + 0x2e, 0xdd, 0xc2, 0x1c, 0x14, 0x15, 0xac, + }, + }, + { + "zero signature", + &btcec.Signature{ + R: big.NewInt(0), + S: big.NewInt(0), + }, + []byte{0x30, 0x06, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00}, + }, + } + + for i, test := range tests { + result := test.ecsig.Serialize() + if !bytes.Equal(result, test.expected) { + t.Errorf("Serialize #%d (%s) unexpected result:\n"+ + "got: %x\nwant: %x", i, test.name, result, + test.expected) + } + } +} + +func testSignCompact(t *testing.T, tag string, curve *btcec.KoblitzCurve, + data []byte, isCompressed bool) { + tmp, _ := btcec.NewPrivateKey(curve) + priv := (*btcec.PrivateKey)(tmp) + + hashed := []byte("testing") + sig, err := btcec.SignCompact(curve, priv, hashed, isCompressed) + if err != nil { + t.Errorf("%s: error signing: %s", tag, err) + return + } + + pk, wasCompressed, err := btcec.RecoverCompact(curve, sig, hashed) + if err != nil { + t.Errorf("%s: error recovering: %s", tag, err) + return + } + if pk.X.Cmp(priv.X) != 0 || pk.Y.Cmp(priv.Y) != 0 { + t.Errorf("%s: recovered pubkey doesn't match original "+ + "(%v,%v) vs (%v,%v) ", tag, pk.X, pk.Y, priv.X, priv.Y) + return + } + if wasCompressed != isCompressed { + t.Errorf("%s: recovered pubkey doesn't match compressed state "+ + "(%v vs %v)", tag, isCompressed, wasCompressed) + return + } + + // If we change the compressed bit we should get the same key back, + // but the compressed flag should be reversed. + if isCompressed { + sig[0] -= 4 + } else { + sig[0] += 4 + } + + pk, wasCompressed, err = btcec.RecoverCompact(curve, sig, hashed) + if err != nil { + t.Errorf("%s: error recovering (2): %s", tag, err) + return + } + if pk.X.Cmp(priv.X) != 0 || pk.Y.Cmp(priv.Y) != 0 { + t.Errorf("%s: recovered pubkey (2) doesn't match original "+ + "(%v,%v) vs (%v,%v) ", tag, pk.X, pk.Y, priv.X, priv.Y) + return + } + if wasCompressed == isCompressed { + t.Errorf("%s: recovered pubkey doesn't match reversed "+ + "compressed state (%v vs %v)", tag, isCompressed, + wasCompressed) + return + } +} + +func TestSignCompact(t *testing.T) { + for i := 0; i < 256; i++ { + name := fmt.Sprintf("test %d", i) + data := make([]byte, 32) + _, err := rand.Read(data) + if err != nil { + t.Errorf("failed to read random data for %s", name) + continue + } + compressed := i%2 != 0 + testSignCompact(t, name, btcec.S256(), data, compressed) + } +} + +func TestRFC6979(t *testing.T) { + // Test vectors matching Trezor and CoreBitcoin implementations. + // - https://github.com/trezor/trezor-crypto/blob/9fea8f8ab377dc514e40c6fd1f7c89a74c1d8dc6/tests.c#L432-L453 + // - https://github.com/oleganza/CoreBitcoin/blob/e93dd71207861b5bf044415db5fa72405e7d8fbc/CoreBitcoin/BTCKey%2BTests.m#L23-L49 + tests := []struct { + key string + msg string + nonce string + signature string + }{ + { + "cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", + "sample", + "2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", + "3045022100af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b384202205009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124", + }, + { + // This signature hits the case when S is higher than halforder. + // If S is not canonicalized (lowered by halforder), this test will fail. + "0000000000000000000000000000000000000000000000000000000000000001", + "Satoshi Nakamoto", + "8f8a276c19f4149656b280621e358cce24f5f52542772691ee69063b74f15d15", + "3045022100934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d802202442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5", + }, + { + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "Satoshi Nakamoto", + "33a19b60e25fb6f4435af53a3d42d493644827367e6453928554f43e49aa6f90", + "3045022100fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d002206b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5", + }, + { + "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181", + "Alan Turing", + "525a82b70e67874398067543fd84c83d30c175fdc45fdeee082fe13b1d7cfdf1", + "304402207063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c022058dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea", + }, + { + "0000000000000000000000000000000000000000000000000000000000000001", + "All those moments will be lost in time, like tears in rain. Time to die...", + "38aa22d72376b4dbc472e06c3ba403ee0a394da63fc58d88686c611aba98d6b3", + "30450221008600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b0220547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21", + }, + { + "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2", + "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!", + "1f4b84c23a86a221d233f2521be018d9318639d5b8bbd6374a8a59232d16ad3d", + "3045022100b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b0220279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6", + }, + } + + for i, test := range tests { + privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), decodeHex(test.key)) + hash := fastsha256.Sum256([]byte(test.msg)) + + // Ensure deterministically generated nonce is the expected value. + gotNonce := btcec.TstNonceRFC6979(privKey.D, hash[:]).Bytes() + wantNonce := decodeHex(test.nonce) + if !bytes.Equal(gotNonce, wantNonce) { + t.Errorf("NonceRFC6979 #%d (%s): Nonce is incorrect: "+ + "%x (expected %x)", i, test.msg, gotNonce, + wantNonce) + continue + } + + // Ensure deterministically generated signature is the expected value. + gotSig, err := privKey.Sign(hash[:]) + if err != nil { + t.Errorf("Sign #%d (%s): unexpected error: %v", i, + test.msg, err) + continue + } + gotSigBytes := gotSig.Serialize() + wantSigBytes := decodeHex(test.signature) + if !bytes.Equal(gotSigBytes, wantSigBytes) { + t.Errorf("Sign #%d (%s): mismatched signature: %x "+ + "(expected %x)", i, test.msg, gotSigBytes, + wantSigBytes) + continue + } + } +} + +func TestSignatureIsEqual(t *testing.T) { + sig1 := &btcec.Signature{ + R: fromHex("0082235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"), + S: fromHex("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"), + } + sig2 := &btcec.Signature{ + R: fromHex("4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41"), + S: fromHex("181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09"), + } + + if !sig1.IsEqual(sig1) { + t.Fatalf("value of IsEqual is incorrect, %v is "+ + "equal to %v", sig1, sig1) + } + + if sig1.IsEqual(sig2) { + t.Fatalf("value of IsEqual is incorrect, %v is not "+ + "equal to %v", sig1, sig2) + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/CONTRIBUTORS b/vendor/github.com/btcsuite/btcd/btcjson/CONTRIBUTORS new file mode 100644 index 0000000000000000000000000000000000000000..b8d98b32f342e8e7c61229150dac1eec2e790cc5 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/CONTRIBUTORS @@ -0,0 +1,16 @@ +# This is the list of people who have contributed code to the repository. +# +# Names should be added to this file only after verifying that the individual +# or the individual's organization has agreed to the LICENSE. +# +# Names should be added to this file like so: +# Name <email address> + +John C. Vernaleo <jcv@conformal.com> +Dave Collins <davec@conformal.com> +Owain G. Ainsworth <oga@conformal.com> +David Hill <dhill@conformal.com> +Josh Rickmar <jrick@conformal.com> +Andreas Metsälä <andreas.metsala@gmail.com> +Francis Lam <flam@alum.mit.edu> +Geert-Johan Riemer <geertjohan.riemer@gmail.com> diff --git a/vendor/github.com/btcsuite/btcd/btcjson/README.md b/vendor/github.com/btcsuite/btcd/btcjson/README.md new file mode 100644 index 0000000000000000000000000000000000000000..57a2b9c996e96bd71ce2c829b6e7dc52b4a81d6b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/README.md @@ -0,0 +1,76 @@ +btcjson +======= + +[] +(https://travis-ci.org/btcsuite/btcd) [![ISC License] +(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[] +(http://godoc.org/github.com/btcsuite/btcd/btcjson) + +Package btcjson implements concrete types for marshalling to and from the +bitcoin JSON-RPC API. A comprehensive suite of tests is provided to ensure +proper functionality. + +Although this package was primarily written for the btcsuite, it has +intentionally been designed so it can be used as a standalone package for any +projects needing to marshal to and from bitcoin JSON-RPC requests and responses. + +Note that although it's possible to use this package directly to implement an +RPC client, it is not recommended since it is only intended as an infrastructure +package. Instead, RPC clients should use the +[btcrpcclient](https://github.com/btcsuite/btcrpcclient) package which provides +a full blown RPC client with many features such as automatic connection +management, websocket support, automatic notification re-registration on +reconnect, and conversion from the raw underlying RPC types (strings, floats, +ints, etc) to higher-level types with many nice and useful properties. + +## Installation and Updating + +```bash +$ go get -u github.com/btcsuite/btcd/btcjson +``` + +## Examples + +* [Marshal Command] + (http://godoc.org/github.com/btcsuite/btcd/btcjson#example-MarshalCmd) + Demonstrates how to create and marshal a command into a JSON-RPC request. + +* [Unmarshal Command] + (http://godoc.org/github.com/btcsuite/btcd/btcjson#example-UnmarshalCmd) + Demonstrates how to unmarshal a JSON-RPC request and then unmarshal the + concrete request into a concrete command. + +* [Marshal Response] + (http://godoc.org/github.com/btcsuite/btcd/btcjson#example-MarshalResponse) + Demonstrates how to marshal a JSON-RPC response. + +* [Unmarshal Response] + (http://godoc.org/github.com/btcsuite/btcd/btcjson#example-package--UnmarshalResponse) + Demonstrates how to unmarshal a JSON-RPC response and then unmarshal the + result field in the response to a concrete type. + +## GPG Verification Key + +All official release tags are signed by Conformal so users can ensure the code +has not been tampered with and is coming from the btcsuite developers. To +verify the signature perform the following: + +- Download the public key from the Conformal website at + https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt + +- Import the public key into your GPG keyring: + ```bash + gpg --import GIT-GPG-KEY-conformal.txt + ``` + +- Verify the release tag with the following command where `TAG_NAME` is a + placeholder for the specific tag: + ```bash + git tag -v TAG_NAME + ``` + +## License + +Package btcjson is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/vendor/github.com/btcsuite/btcd/btcjson/btcdextcmds.go b/vendor/github.com/btcsuite/btcd/btcjson/btcdextcmds.go new file mode 100644 index 0000000000000000000000000000000000000000..7d416395f09755cf26974df8512115d9e6d4b3a1 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/btcdextcmds.go @@ -0,0 +1,101 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// NOTE: This file is intended to house the RPC commands that are supported by +// a chain server with btcd extensions. + +package btcjson + +// NodeSubCmd defines the type used in the addnode JSON-RPC command for the +// sub command field. +type NodeSubCmd string + +const ( + // NConnect indicates the specified host that should be connected to. + NConnect NodeSubCmd = "connect" + + // NRemove indicates the specified peer that should be removed as a + // persistent peer. + NRemove NodeSubCmd = "remove" + + // NDisconnect indicates the specified peer should be disonnected. + NDisconnect NodeSubCmd = "disconnect" +) + +// NodeCmd defines the dropnode JSON-RPC command. +type NodeCmd struct { + SubCmd NodeSubCmd `jsonrpcusage:"\"connect|remove|disconnect\""` + Target string + ConnectSubCmd *string `jsonrpcusage:"\"perm|temp\""` +} + +// NewNodeCmd returns a new instance which can be used to issue a `node` +// JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewNodeCmd(subCmd NodeSubCmd, target string, connectSubCmd *string) *NodeCmd { + return &NodeCmd{ + SubCmd: subCmd, + Target: target, + ConnectSubCmd: connectSubCmd, + } +} + +// DebugLevelCmd defines the debuglevel JSON-RPC command. This command is not a +// standard Bitcoin command. It is an extension for btcd. +type DebugLevelCmd struct { + LevelSpec string +} + +// NewDebugLevelCmd returns a new DebugLevelCmd which can be used to issue a +// debuglevel JSON-RPC command. This command is not a standard Bitcoin command. +// It is an extension for btcd. +func NewDebugLevelCmd(levelSpec string) *DebugLevelCmd { + return &DebugLevelCmd{ + LevelSpec: levelSpec, + } +} + +// GenerateCmd defines the generate JSON-RPC command. +type GenerateCmd struct { + NumBlocks uint32 +} + +// NewGenerateCmd returns a new instance which can be used to issue a generate +// JSON-RPC command. +func NewGenerateCmd(numBlocks uint32) *GenerateCmd { + return &GenerateCmd{ + NumBlocks: numBlocks, + } +} + +// GetBestBlockCmd defines the getbestblock JSON-RPC command. +type GetBestBlockCmd struct{} + +// NewGetBestBlockCmd returns a new instance which can be used to issue a +// getbestblock JSON-RPC command. +func NewGetBestBlockCmd() *GetBestBlockCmd { + return &GetBestBlockCmd{} +} + +// GetCurrentNetCmd defines the getcurrentnet JSON-RPC command. +type GetCurrentNetCmd struct{} + +// NewGetCurrentNetCmd returns a new instance which can be used to issue a +// getcurrentnet JSON-RPC command. +func NewGetCurrentNetCmd() *GetCurrentNetCmd { + return &GetCurrentNetCmd{} +} + +func init() { + // No special flags for commands in this file. + flags := UsageFlag(0) + + MustRegisterCmd("debuglevel", (*DebugLevelCmd)(nil), flags) + MustRegisterCmd("node", (*NodeCmd)(nil), flags) + MustRegisterCmd("generate", (*GenerateCmd)(nil), flags) + MustRegisterCmd("getbestblock", (*GetBestBlockCmd)(nil), flags) + MustRegisterCmd("getcurrentnet", (*GetCurrentNetCmd)(nil), flags) +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/btcdextcmds_test.go b/vendor/github.com/btcsuite/btcd/btcjson/btcdextcmds_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c4d8d4983c6a57619ea426a87680aef9817fe12a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/btcdextcmds_test.go @@ -0,0 +1,205 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestBtcdExtCmds tests all of the btcd extended commands marshal and unmarshal +// into valid results include handling of optional fields being omitted in the +// marshalled command, while optional fields with defaults have the default +// assigned on unmarshalled commands. +func TestBtcdExtCmds(t *testing.T) { + t.Parallel() + + testID := int(1) + tests := []struct { + name string + newCmd func() (interface{}, error) + staticCmd func() interface{} + marshalled string + unmarshalled interface{} + }{ + { + name: "debuglevel", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("debuglevel", "trace") + }, + staticCmd: func() interface{} { + return btcjson.NewDebugLevelCmd("trace") + }, + marshalled: `{"jsonrpc":"1.0","method":"debuglevel","params":["trace"],"id":1}`, + unmarshalled: &btcjson.DebugLevelCmd{ + LevelSpec: "trace", + }, + }, + { + name: "node", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("node", btcjson.NRemove, "1.1.1.1") + }, + staticCmd: func() interface{} { + return btcjson.NewNodeCmd("remove", "1.1.1.1", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"node","params":["remove","1.1.1.1"],"id":1}`, + unmarshalled: &btcjson.NodeCmd{ + SubCmd: btcjson.NRemove, + Target: "1.1.1.1", + }, + }, + { + name: "node", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("node", btcjson.NDisconnect, "1.1.1.1") + }, + staticCmd: func() interface{} { + return btcjson.NewNodeCmd("disconnect", "1.1.1.1", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"node","params":["disconnect","1.1.1.1"],"id":1}`, + unmarshalled: &btcjson.NodeCmd{ + SubCmd: btcjson.NDisconnect, + Target: "1.1.1.1", + }, + }, + { + name: "node", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("node", btcjson.NConnect, "1.1.1.1", "perm") + }, + staticCmd: func() interface{} { + return btcjson.NewNodeCmd("connect", "1.1.1.1", btcjson.String("perm")) + }, + marshalled: `{"jsonrpc":"1.0","method":"node","params":["connect","1.1.1.1","perm"],"id":1}`, + unmarshalled: &btcjson.NodeCmd{ + SubCmd: btcjson.NConnect, + Target: "1.1.1.1", + ConnectSubCmd: btcjson.String("perm"), + }, + }, + { + name: "node", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("node", btcjson.NConnect, "1.1.1.1", "temp") + }, + staticCmd: func() interface{} { + return btcjson.NewNodeCmd("connect", "1.1.1.1", btcjson.String("temp")) + }, + marshalled: `{"jsonrpc":"1.0","method":"node","params":["connect","1.1.1.1","temp"],"id":1}`, + unmarshalled: &btcjson.NodeCmd{ + SubCmd: btcjson.NConnect, + Target: "1.1.1.1", + ConnectSubCmd: btcjson.String("temp"), + }, + }, + { + name: "generate", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("generate", 1) + }, + staticCmd: func() interface{} { + return btcjson.NewGenerateCmd(1) + }, + marshalled: `{"jsonrpc":"1.0","method":"generate","params":[1],"id":1}`, + unmarshalled: &btcjson.GenerateCmd{ + NumBlocks: 1, + }, + }, + { + name: "getbestblock", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getbestblock") + }, + staticCmd: func() interface{} { + return btcjson.NewGetBestBlockCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getbestblock","params":[],"id":1}`, + unmarshalled: &btcjson.GetBestBlockCmd{}, + }, + { + name: "getcurrentnet", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getcurrentnet") + }, + staticCmd: func() interface{} { + return btcjson.NewGetCurrentNetCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getcurrentnet","params":[],"id":1}`, + unmarshalled: &btcjson.GetCurrentNetCmd{}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Marshal the command as created by the new static command + // creation function. + marshalled, err := btcjson.MarshalCmd(testID, test.staticCmd()) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + // Ensure the command is created without error via the generic + // new command creation function. + cmd, err := test.newCmd() + if err != nil { + t.Errorf("Test #%d (%s) unexpected NewCmd error: %v ", + i, test.name, err) + } + + // Marshal the command as created by the generic new command + // creation function. + marshalled, err = btcjson.MarshalCmd(testID, cmd) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + var request btcjson.Request + if err := json.Unmarshal(marshalled, &request); err != nil { + t.Errorf("Test #%d (%s) unexpected error while "+ + "unmarshalling JSON-RPC request: %v", i, + test.name, err) + continue + } + + cmd, err = btcjson.UnmarshalCmd(&request) + if err != nil { + t.Errorf("UnmarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !reflect.DeepEqual(cmd, test.unmarshalled) { + t.Errorf("Test #%d (%s) unexpected unmarshalled command "+ + "- got %s, want %s", i, test.name, + fmt.Sprintf("(%T) %+[1]v", cmd), + fmt.Sprintf("(%T) %+[1]v\n", test.unmarshalled)) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/btcwalletextcmds.go b/vendor/github.com/btcsuite/btcd/btcjson/btcwalletextcmds.go new file mode 100644 index 0000000000000000000000000000000000000000..9cbc273a6db7e7322c9f57dc94d780f778c91bc0 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/btcwalletextcmds.go @@ -0,0 +1,104 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// NOTE: This file is intended to house the RPC commands that are supported by +// a wallet server with btcwallet extensions. + +package btcjson + +// CreateNewAccountCmd defines the createnewaccount JSON-RPC command. +type CreateNewAccountCmd struct { + Account string +} + +// NewCreateNewAccountCmd returns a new instance which can be used to issue a +// createnewaccount JSON-RPC command. +func NewCreateNewAccountCmd(account string) *CreateNewAccountCmd { + return &CreateNewAccountCmd{ + Account: account, + } +} + +// DumpWalletCmd defines the dumpwallet JSON-RPC command. +type DumpWalletCmd struct { + Filename string +} + +// NewDumpWalletCmd returns a new instance which can be used to issue a +// dumpwallet JSON-RPC command. +func NewDumpWalletCmd(filename string) *DumpWalletCmd { + return &DumpWalletCmd{ + Filename: filename, + } +} + +// ImportAddressCmd defines the importaddress JSON-RPC command. +type ImportAddressCmd struct { + Address string + Rescan *bool `jsonrpcdefault:"true"` +} + +// NewImportAddressCmd returns a new instance which can be used to issue an +// importaddress JSON-RPC command. +func NewImportAddressCmd(address string, rescan *bool) *ImportAddressCmd { + return &ImportAddressCmd{ + Address: address, + Rescan: rescan, + } +} + +// ImportPubKeyCmd defines the importpubkey JSON-RPC command. +type ImportPubKeyCmd struct { + PubKey string + Rescan *bool `jsonrpcdefault:"true"` +} + +// NewImportPubKeyCmd returns a new instance which can be used to issue an +// importpubkey JSON-RPC command. +func NewImportPubKeyCmd(pubKey string, rescan *bool) *ImportPubKeyCmd { + return &ImportPubKeyCmd{ + PubKey: pubKey, + Rescan: rescan, + } +} + +// ImportWalletCmd defines the importwallet JSON-RPC command. +type ImportWalletCmd struct { + Filename string +} + +// NewImportWalletCmd returns a new instance which can be used to issue a +// importwallet JSON-RPC command. +func NewImportWalletCmd(filename string) *ImportWalletCmd { + return &ImportWalletCmd{ + Filename: filename, + } +} + +// RenameAccountCmd defines the renameaccount JSON-RPC command. +type RenameAccountCmd struct { + OldAccount string + NewAccount string +} + +// NewRenameAccountCmd returns a new instance which can be used to issue a +// renameaccount JSON-RPC command. +func NewRenameAccountCmd(oldAccount, newAccount string) *RenameAccountCmd { + return &RenameAccountCmd{ + OldAccount: oldAccount, + NewAccount: newAccount, + } +} + +func init() { + // The commands in this file are only usable with a wallet server. + flags := UFWalletOnly + + MustRegisterCmd("createnewaccount", (*CreateNewAccountCmd)(nil), flags) + MustRegisterCmd("dumpwallet", (*DumpWalletCmd)(nil), flags) + MustRegisterCmd("importaddress", (*ImportAddressCmd)(nil), flags) + MustRegisterCmd("importpubkey", (*ImportPubKeyCmd)(nil), flags) + MustRegisterCmd("importwallet", (*ImportWalletCmd)(nil), flags) + MustRegisterCmd("renameaccount", (*RenameAccountCmd)(nil), flags) +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/btcwalletextcmds_test.go b/vendor/github.com/btcsuite/btcd/btcjson/btcwalletextcmds_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e478a08897e6ea28079dfa840be88b0520a45370 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/btcwalletextcmds_test.go @@ -0,0 +1,208 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestBtcWalletExtCmds tests all of the btcwallet extended commands marshal and +// unmarshal into valid results include handling of optional fields being +// omitted in the marshalled command, while optional fields with defaults have +// the default assigned on unmarshalled commands. +func TestBtcWalletExtCmds(t *testing.T) { + t.Parallel() + + testID := int(1) + tests := []struct { + name string + newCmd func() (interface{}, error) + staticCmd func() interface{} + marshalled string + unmarshalled interface{} + }{ + { + name: "createnewaccount", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("createnewaccount", "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewCreateNewAccountCmd("acct") + }, + marshalled: `{"jsonrpc":"1.0","method":"createnewaccount","params":["acct"],"id":1}`, + unmarshalled: &btcjson.CreateNewAccountCmd{ + Account: "acct", + }, + }, + { + name: "dumpwallet", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("dumpwallet", "filename") + }, + staticCmd: func() interface{} { + return btcjson.NewDumpWalletCmd("filename") + }, + marshalled: `{"jsonrpc":"1.0","method":"dumpwallet","params":["filename"],"id":1}`, + unmarshalled: &btcjson.DumpWalletCmd{ + Filename: "filename", + }, + }, + { + name: "importaddress", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("importaddress", "1Address") + }, + staticCmd: func() interface{} { + return btcjson.NewImportAddressCmd("1Address", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"importaddress","params":["1Address"],"id":1}`, + unmarshalled: &btcjson.ImportAddressCmd{ + Address: "1Address", + Rescan: btcjson.Bool(true), + }, + }, + { + name: "importaddress optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("importaddress", "1Address", false) + }, + staticCmd: func() interface{} { + return btcjson.NewImportAddressCmd("1Address", btcjson.Bool(false)) + }, + marshalled: `{"jsonrpc":"1.0","method":"importaddress","params":["1Address",false],"id":1}`, + unmarshalled: &btcjson.ImportAddressCmd{ + Address: "1Address", + Rescan: btcjson.Bool(false), + }, + }, + { + name: "importpubkey", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("importpubkey", "031234") + }, + staticCmd: func() interface{} { + return btcjson.NewImportPubKeyCmd("031234", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"importpubkey","params":["031234"],"id":1}`, + unmarshalled: &btcjson.ImportPubKeyCmd{ + PubKey: "031234", + Rescan: btcjson.Bool(true), + }, + }, + { + name: "importpubkey optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("importpubkey", "031234", false) + }, + staticCmd: func() interface{} { + return btcjson.NewImportPubKeyCmd("031234", btcjson.Bool(false)) + }, + marshalled: `{"jsonrpc":"1.0","method":"importpubkey","params":["031234",false],"id":1}`, + unmarshalled: &btcjson.ImportPubKeyCmd{ + PubKey: "031234", + Rescan: btcjson.Bool(false), + }, + }, + { + name: "importwallet", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("importwallet", "filename") + }, + staticCmd: func() interface{} { + return btcjson.NewImportWalletCmd("filename") + }, + marshalled: `{"jsonrpc":"1.0","method":"importwallet","params":["filename"],"id":1}`, + unmarshalled: &btcjson.ImportWalletCmd{ + Filename: "filename", + }, + }, + { + name: "renameaccount", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("renameaccount", "oldacct", "newacct") + }, + staticCmd: func() interface{} { + return btcjson.NewRenameAccountCmd("oldacct", "newacct") + }, + marshalled: `{"jsonrpc":"1.0","method":"renameaccount","params":["oldacct","newacct"],"id":1}`, + unmarshalled: &btcjson.RenameAccountCmd{ + OldAccount: "oldacct", + NewAccount: "newacct", + }, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Marshal the command as created by the new static command + // creation function. + marshalled, err := btcjson.MarshalCmd(testID, test.staticCmd()) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + // Ensure the command is created without error via the generic + // new command creation function. + cmd, err := test.newCmd() + if err != nil { + t.Errorf("Test #%d (%s) unexpected NewCmd error: %v ", + i, test.name, err) + } + + // Marshal the command as created by the generic new command + // creation function. + marshalled, err = btcjson.MarshalCmd(testID, cmd) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + var request btcjson.Request + if err := json.Unmarshal(marshalled, &request); err != nil { + t.Errorf("Test #%d (%s) unexpected error while "+ + "unmarshalling JSON-RPC request: %v", i, + test.name, err) + continue + } + + cmd, err = btcjson.UnmarshalCmd(&request) + if err != nil { + t.Errorf("UnmarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !reflect.DeepEqual(cmd, test.unmarshalled) { + t.Errorf("Test #%d (%s) unexpected unmarshalled command "+ + "- got %s, want %s", i, test.name, + fmt.Sprintf("(%T) %+[1]v", cmd), + fmt.Sprintf("(%T) %+[1]v\n", test.unmarshalled)) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/chainsvrcmds.go b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrcmds.go new file mode 100644 index 0000000000000000000000000000000000000000..c16e97ab3b407cad8ee14b24e541fe9d189b95f2 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrcmds.go @@ -0,0 +1,756 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// NOTE: This file is intended to house the RPC commands that are supported by +// a chain server. + +package btcjson + +import ( + "encoding/json" + "fmt" +) + +// AddNodeSubCmd defines the type used in the addnode JSON-RPC command for the +// sub command field. +type AddNodeSubCmd string + +const ( + // ANAdd indicates the specified host should be added as a persistent + // peer. + ANAdd AddNodeSubCmd = "add" + + // ANRemove indicates the specified peer should be removed. + ANRemove AddNodeSubCmd = "remove" + + // ANOneTry indicates the specified host should try to connect once, + // but it should not be made persistent. + ANOneTry AddNodeSubCmd = "onetry" +) + +// AddNodeCmd defines the addnode JSON-RPC command. +type AddNodeCmd struct { + Addr string + SubCmd AddNodeSubCmd `jsonrpcusage:"\"add|remove|onetry\""` +} + +// NewAddNodeCmd returns a new instance which can be used to issue an addnode +// JSON-RPC command. +func NewAddNodeCmd(addr string, subCmd AddNodeSubCmd) *AddNodeCmd { + return &AddNodeCmd{ + Addr: addr, + SubCmd: subCmd, + } +} + +// TransactionInput represents the inputs to a transaction. Specifically a +// transaction hash and output number pair. +type TransactionInput struct { + Txid string `json:"txid"` + Vout uint32 `json:"vout"` +} + +// CreateRawTransactionCmd defines the createrawtransaction JSON-RPC command. +type CreateRawTransactionCmd struct { + Inputs []TransactionInput + Amounts map[string]float64 `jsonrpcusage:"{\"address\":amount,...}"` // In BTC + LockTime *int64 +} + +// NewCreateRawTransactionCmd returns a new instance which can be used to issue +// a createrawtransaction JSON-RPC command. +// +// Amounts are in BTC. +func NewCreateRawTransactionCmd(inputs []TransactionInput, amounts map[string]float64, + lockTime *int64) *CreateRawTransactionCmd { + + return &CreateRawTransactionCmd{ + Inputs: inputs, + Amounts: amounts, + LockTime: lockTime, + } +} + +// DecodeRawTransactionCmd defines the decoderawtransaction JSON-RPC command. +type DecodeRawTransactionCmd struct { + HexTx string +} + +// NewDecodeRawTransactionCmd returns a new instance which can be used to issue +// a decoderawtransaction JSON-RPC command. +func NewDecodeRawTransactionCmd(hexTx string) *DecodeRawTransactionCmd { + return &DecodeRawTransactionCmd{ + HexTx: hexTx, + } +} + +// DecodeScriptCmd defines the decodescript JSON-RPC command. +type DecodeScriptCmd struct { + HexScript string +} + +// NewDecodeScriptCmd returns a new instance which can be used to issue a +// decodescript JSON-RPC command. +func NewDecodeScriptCmd(hexScript string) *DecodeScriptCmd { + return &DecodeScriptCmd{ + HexScript: hexScript, + } +} + +// GetAddedNodeInfoCmd defines the getaddednodeinfo JSON-RPC command. +type GetAddedNodeInfoCmd struct { + DNS bool + Node *string +} + +// NewGetAddedNodeInfoCmd returns a new instance which can be used to issue a +// getaddednodeinfo JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetAddedNodeInfoCmd(dns bool, node *string) *GetAddedNodeInfoCmd { + return &GetAddedNodeInfoCmd{ + DNS: dns, + Node: node, + } +} + +// GetBestBlockHashCmd defines the getbestblockhash JSON-RPC command. +type GetBestBlockHashCmd struct{} + +// NewGetBestBlockHashCmd returns a new instance which can be used to issue a +// getbestblockhash JSON-RPC command. +func NewGetBestBlockHashCmd() *GetBestBlockHashCmd { + return &GetBestBlockHashCmd{} +} + +// GetBlockCmd defines the getblock JSON-RPC command. +type GetBlockCmd struct { + Hash string + Verbose *bool `jsonrpcdefault:"true"` + VerboseTx *bool `jsonrpcdefault:"false"` +} + +// NewGetBlockCmd returns a new instance which can be used to issue a getblock +// JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetBlockCmd(hash string, verbose, verboseTx *bool) *GetBlockCmd { + return &GetBlockCmd{ + Hash: hash, + Verbose: verbose, + VerboseTx: verboseTx, + } +} + +// GetBlockChainInfoCmd defines the getblockchaininfo JSON-RPC command. +type GetBlockChainInfoCmd struct{} + +// NewGetBlockChainInfoCmd returns a new instance which can be used to issue a +// getblockchaininfo JSON-RPC command. +func NewGetBlockChainInfoCmd() *GetBlockChainInfoCmd { + return &GetBlockChainInfoCmd{} +} + +// GetBlockCountCmd defines the getblockcount JSON-RPC command. +type GetBlockCountCmd struct{} + +// NewGetBlockCountCmd returns a new instance which can be used to issue a +// getblockcount JSON-RPC command. +func NewGetBlockCountCmd() *GetBlockCountCmd { + return &GetBlockCountCmd{} +} + +// GetBlockHashCmd defines the getblockhash JSON-RPC command. +type GetBlockHashCmd struct { + Index int64 +} + +// NewGetBlockHashCmd returns a new instance which can be used to issue a +// getblockhash JSON-RPC command. +func NewGetBlockHashCmd(index int64) *GetBlockHashCmd { + return &GetBlockHashCmd{ + Index: index, + } +} + +// GetBlockHeaderCmd defines the getblockheader JSON-RPC command. +type GetBlockHeaderCmd struct { + Hash string + Verbose *bool `jsonrpcdefault:"true"` +} + +// NewGetBlockHeaderCmd returns a new instance which can be used to issue a +// getblockheader JSON-RPC command. +func NewGetBlockHeaderCmd(hash string, verbose *bool) *GetBlockHeaderCmd { + return &GetBlockHeaderCmd{ + Hash: hash, + Verbose: verbose, + } +} + +// TemplateRequest is a request object as defined in BIP22 +// (https://en.bitcoin.it/wiki/BIP_0022), it is optionally provided as an +// pointer argument to GetBlockTemplateCmd. +type TemplateRequest struct { + Mode string `json:"mode,omitempty"` + Capabilities []string `json:"capabilities,omitempty"` + + // Optional long polling. + LongPollID string `json:"longpollid,omitempty"` + + // Optional template tweaking. SigOpLimit and SizeLimit can be int64 + // or bool. + SigOpLimit interface{} `json:"sigoplimit,omitempty"` + SizeLimit interface{} `json:"sizelimit,omitempty"` + MaxVersion uint32 `json:"maxversion,omitempty"` + + // Basic pool extension from BIP 0023. + Target string `json:"target,omitempty"` + + // Block proposal from BIP 0023. Data is only provided when Mode is + // "proposal". + Data string `json:"data,omitempty"` + WorkID string `json:"workid,omitempty"` +} + +// convertTemplateRequestField potentially converts the provided value as +// needed. +func convertTemplateRequestField(fieldName string, iface interface{}) (interface{}, error) { + switch val := iface.(type) { + case nil: + return nil, nil + case bool: + return val, nil + case float64: + if val == float64(int64(val)) { + return int64(val), nil + } + } + + str := fmt.Sprintf("the %s field must be unspecified, a boolean, or "+ + "a 64-bit integer", fieldName) + return nil, makeError(ErrInvalidType, str) +} + +// UnmarshalJSON provides a custom Unmarshal method for TemplateRequest. This +// is necessary because the SigOpLimit and SizeLimit fields can only be specific +// types. +func (t *TemplateRequest) UnmarshalJSON(data []byte) error { + type templateRequest TemplateRequest + + request := (*templateRequest)(t) + if err := json.Unmarshal(data, &request); err != nil { + return err + } + + // The SigOpLimit field can only be nil, bool, or int64. + val, err := convertTemplateRequestField("sigoplimit", request.SigOpLimit) + if err != nil { + return err + } + request.SigOpLimit = val + + // The SizeLimit field can only be nil, bool, or int64. + val, err = convertTemplateRequestField("sizelimit", request.SizeLimit) + if err != nil { + return err + } + request.SizeLimit = val + + return nil +} + +// GetBlockTemplateCmd defines the getblocktemplate JSON-RPC command. +type GetBlockTemplateCmd struct { + Request *TemplateRequest +} + +// NewGetBlockTemplateCmd returns a new instance which can be used to issue a +// getblocktemplate JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetBlockTemplateCmd(request *TemplateRequest) *GetBlockTemplateCmd { + return &GetBlockTemplateCmd{ + Request: request, + } +} + +// GetChainTipsCmd defines the getchaintips JSON-RPC command. +type GetChainTipsCmd struct{} + +// NewGetChainTipsCmd returns a new instance which can be used to issue a +// getchaintips JSON-RPC command. +func NewGetChainTipsCmd() *GetChainTipsCmd { + return &GetChainTipsCmd{} +} + +// GetConnectionCountCmd defines the getconnectioncount JSON-RPC command. +type GetConnectionCountCmd struct{} + +// NewGetConnectionCountCmd returns a new instance which can be used to issue a +// getconnectioncount JSON-RPC command. +func NewGetConnectionCountCmd() *GetConnectionCountCmd { + return &GetConnectionCountCmd{} +} + +// GetDifficultyCmd defines the getdifficulty JSON-RPC command. +type GetDifficultyCmd struct{} + +// NewGetDifficultyCmd returns a new instance which can be used to issue a +// getdifficulty JSON-RPC command. +func NewGetDifficultyCmd() *GetDifficultyCmd { + return &GetDifficultyCmd{} +} + +// GetGenerateCmd defines the getgenerate JSON-RPC command. +type GetGenerateCmd struct{} + +// NewGetGenerateCmd returns a new instance which can be used to issue a +// getgenerate JSON-RPC command. +func NewGetGenerateCmd() *GetGenerateCmd { + return &GetGenerateCmd{} +} + +// GetHashesPerSecCmd defines the gethashespersec JSON-RPC command. +type GetHashesPerSecCmd struct{} + +// NewGetHashesPerSecCmd returns a new instance which can be used to issue a +// gethashespersec JSON-RPC command. +func NewGetHashesPerSecCmd() *GetHashesPerSecCmd { + return &GetHashesPerSecCmd{} +} + +// GetInfoCmd defines the getinfo JSON-RPC command. +type GetInfoCmd struct{} + +// NewGetInfoCmd returns a new instance which can be used to issue a +// getinfo JSON-RPC command. +func NewGetInfoCmd() *GetInfoCmd { + return &GetInfoCmd{} +} + +// GetMempoolInfoCmd defines the getmempoolinfo JSON-RPC command. +type GetMempoolInfoCmd struct{} + +// NewGetMempoolInfoCmd returns a new instance which can be used to issue a +// getmempool JSON-RPC command. +func NewGetMempoolInfoCmd() *GetMempoolInfoCmd { + return &GetMempoolInfoCmd{} +} + +// GetMiningInfoCmd defines the getmininginfo JSON-RPC command. +type GetMiningInfoCmd struct{} + +// NewGetMiningInfoCmd returns a new instance which can be used to issue a +// getmininginfo JSON-RPC command. +func NewGetMiningInfoCmd() *GetMiningInfoCmd { + return &GetMiningInfoCmd{} +} + +// GetNetworkInfoCmd defines the getnetworkinfo JSON-RPC command. +type GetNetworkInfoCmd struct{} + +// NewGetNetworkInfoCmd returns a new instance which can be used to issue a +// getnetworkinfo JSON-RPC command. +func NewGetNetworkInfoCmd() *GetNetworkInfoCmd { + return &GetNetworkInfoCmd{} +} + +// GetNetTotalsCmd defines the getnettotals JSON-RPC command. +type GetNetTotalsCmd struct{} + +// NewGetNetTotalsCmd returns a new instance which can be used to issue a +// getnettotals JSON-RPC command. +func NewGetNetTotalsCmd() *GetNetTotalsCmd { + return &GetNetTotalsCmd{} +} + +// GetNetworkHashPSCmd defines the getnetworkhashps JSON-RPC command. +type GetNetworkHashPSCmd struct { + Blocks *int `jsonrpcdefault:"120"` + Height *int `jsonrpcdefault:"-1"` +} + +// NewGetNetworkHashPSCmd returns a new instance which can be used to issue a +// getnetworkhashps JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetNetworkHashPSCmd(numBlocks, height *int) *GetNetworkHashPSCmd { + return &GetNetworkHashPSCmd{ + Blocks: numBlocks, + Height: height, + } +} + +// GetPeerInfoCmd defines the getpeerinfo JSON-RPC command. +type GetPeerInfoCmd struct{} + +// NewGetPeerInfoCmd returns a new instance which can be used to issue a getpeer +// JSON-RPC command. +func NewGetPeerInfoCmd() *GetPeerInfoCmd { + return &GetPeerInfoCmd{} +} + +// GetRawMempoolCmd defines the getmempool JSON-RPC command. +type GetRawMempoolCmd struct { + Verbose *bool `jsonrpcdefault:"false"` +} + +// NewGetRawMempoolCmd returns a new instance which can be used to issue a +// getrawmempool JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetRawMempoolCmd(verbose *bool) *GetRawMempoolCmd { + return &GetRawMempoolCmd{ + Verbose: verbose, + } +} + +// GetRawTransactionCmd defines the getrawtransaction JSON-RPC command. +// +// NOTE: This field is an int versus a bool to remain compatible with Bitcoin +// Core even though it really should be a bool. +type GetRawTransactionCmd struct { + Txid string + Verbose *int `jsonrpcdefault:"0"` +} + +// NewGetRawTransactionCmd returns a new instance which can be used to issue a +// getrawtransaction JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetRawTransactionCmd(txHash string, verbose *int) *GetRawTransactionCmd { + return &GetRawTransactionCmd{ + Txid: txHash, + Verbose: verbose, + } +} + +// GetTxOutCmd defines the gettxout JSON-RPC command. +type GetTxOutCmd struct { + Txid string + Vout uint32 + IncludeMempool *bool `jsonrpcdefault:"true"` +} + +// NewGetTxOutCmd returns a new instance which can be used to issue a gettxout +// JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetTxOutCmd(txHash string, vout uint32, includeMempool *bool) *GetTxOutCmd { + return &GetTxOutCmd{ + Txid: txHash, + Vout: vout, + IncludeMempool: includeMempool, + } +} + +// GetTxOutProofCmd defines the gettxoutproof JSON-RPC command. +type GetTxOutProofCmd struct { + TxIDs []string + BlockHash *string +} + +// NewGetTxOutProofCmd returns a new instance which can be used to issue a +// gettxoutproof JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetTxOutProofCmd(txIDs []string, blockHash *string) *GetTxOutProofCmd { + return &GetTxOutProofCmd{ + TxIDs: txIDs, + BlockHash: blockHash, + } +} + +// GetTxOutSetInfoCmd defines the gettxoutsetinfo JSON-RPC command. +type GetTxOutSetInfoCmd struct{} + +// NewGetTxOutSetInfoCmd returns a new instance which can be used to issue a +// gettxoutsetinfo JSON-RPC command. +func NewGetTxOutSetInfoCmd() *GetTxOutSetInfoCmd { + return &GetTxOutSetInfoCmd{} +} + +// GetWorkCmd defines the getwork JSON-RPC command. +type GetWorkCmd struct { + Data *string +} + +// NewGetWorkCmd returns a new instance which can be used to issue a getwork +// JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetWorkCmd(data *string) *GetWorkCmd { + return &GetWorkCmd{ + Data: data, + } +} + +// HelpCmd defines the help JSON-RPC command. +type HelpCmd struct { + Command *string +} + +// NewHelpCmd returns a new instance which can be used to issue a help JSON-RPC +// command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewHelpCmd(command *string) *HelpCmd { + return &HelpCmd{ + Command: command, + } +} + +// InvalidateBlockCmd defines the invalidateblock JSON-RPC command. +type InvalidateBlockCmd struct { + BlockHash string +} + +// NewInvalidateBlockCmd returns a new instance which can be used to issue a +// invalidateblock JSON-RPC command. +func NewInvalidateBlockCmd(blockHash string) *InvalidateBlockCmd { + return &InvalidateBlockCmd{ + BlockHash: blockHash, + } +} + +// PingCmd defines the ping JSON-RPC command. +type PingCmd struct{} + +// NewPingCmd returns a new instance which can be used to issue a ping JSON-RPC +// command. +func NewPingCmd() *PingCmd { + return &PingCmd{} +} + +// ReconsiderBlockCmd defines the reconsiderblock JSON-RPC command. +type ReconsiderBlockCmd struct { + BlockHash string +} + +// NewReconsiderBlockCmd returns a new instance which can be used to issue a +// reconsiderblock JSON-RPC command. +func NewReconsiderBlockCmd(blockHash string) *ReconsiderBlockCmd { + return &ReconsiderBlockCmd{ + BlockHash: blockHash, + } +} + +// SearchRawTransactionsCmd defines the searchrawtransactions JSON-RPC command. +type SearchRawTransactionsCmd struct { + Address string + Verbose *int `jsonrpcdefault:"1"` + Skip *int `jsonrpcdefault:"0"` + Count *int `jsonrpcdefault:"100"` + VinExtra *int `jsonrpcdefault:"0"` + Reverse *bool `jsonrpcdefault:"false"` + FilterAddrs *[]string +} + +// NewSearchRawTransactionsCmd returns a new instance which can be used to issue a +// sendrawtransaction JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewSearchRawTransactionsCmd(address string, verbose, skip, count *int, vinExtra *int, reverse *bool, filterAddrs *[]string) *SearchRawTransactionsCmd { + return &SearchRawTransactionsCmd{ + Address: address, + Verbose: verbose, + Skip: skip, + Count: count, + VinExtra: vinExtra, + Reverse: reverse, + FilterAddrs: filterAddrs, + } +} + +// SendRawTransactionCmd defines the sendrawtransaction JSON-RPC command. +type SendRawTransactionCmd struct { + HexTx string + AllowHighFees *bool `jsonrpcdefault:"false"` +} + +// NewSendRawTransactionCmd returns a new instance which can be used to issue a +// sendrawtransaction JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewSendRawTransactionCmd(hexTx string, allowHighFees *bool) *SendRawTransactionCmd { + return &SendRawTransactionCmd{ + HexTx: hexTx, + AllowHighFees: allowHighFees, + } +} + +// SetGenerateCmd defines the setgenerate JSON-RPC command. +type SetGenerateCmd struct { + Generate bool + GenProcLimit *int `jsonrpcdefault:"-1"` +} + +// NewSetGenerateCmd returns a new instance which can be used to issue a +// setgenerate JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewSetGenerateCmd(generate bool, genProcLimit *int) *SetGenerateCmd { + return &SetGenerateCmd{ + Generate: generate, + GenProcLimit: genProcLimit, + } +} + +// StopCmd defines the stop JSON-RPC command. +type StopCmd struct{} + +// NewStopCmd returns a new instance which can be used to issue a stop JSON-RPC +// command. +func NewStopCmd() *StopCmd { + return &StopCmd{} +} + +// SubmitBlockOptions represents the optional options struct provided with a +// SubmitBlockCmd command. +type SubmitBlockOptions struct { + // must be provided if server provided a workid with template. + WorkID string `json:"workid,omitempty"` +} + +// SubmitBlockCmd defines the submitblock JSON-RPC command. +type SubmitBlockCmd struct { + HexBlock string + Options *SubmitBlockOptions +} + +// NewSubmitBlockCmd returns a new instance which can be used to issue a +// submitblock JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewSubmitBlockCmd(hexBlock string, options *SubmitBlockOptions) *SubmitBlockCmd { + return &SubmitBlockCmd{ + HexBlock: hexBlock, + Options: options, + } +} + +// ValidateAddressCmd defines the validateaddress JSON-RPC command. +type ValidateAddressCmd struct { + Address string +} + +// NewValidateAddressCmd returns a new instance which can be used to issue a +// validateaddress JSON-RPC command. +func NewValidateAddressCmd(address string) *ValidateAddressCmd { + return &ValidateAddressCmd{ + Address: address, + } +} + +// VerifyChainCmd defines the verifychain JSON-RPC command. +type VerifyChainCmd struct { + CheckLevel *int32 `jsonrpcdefault:"3"` + CheckDepth *int32 `jsonrpcdefault:"288"` // 0 = all +} + +// NewVerifyChainCmd returns a new instance which can be used to issue a +// verifychain JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewVerifyChainCmd(checkLevel, checkDepth *int32) *VerifyChainCmd { + return &VerifyChainCmd{ + CheckLevel: checkLevel, + CheckDepth: checkDepth, + } +} + +// VerifyMessageCmd defines the verifymessage JSON-RPC command. +type VerifyMessageCmd struct { + Address string + Signature string + Message string +} + +// NewVerifyMessageCmd returns a new instance which can be used to issue a +// verifymessage JSON-RPC command. +func NewVerifyMessageCmd(address, signature, message string) *VerifyMessageCmd { + return &VerifyMessageCmd{ + Address: address, + Signature: signature, + Message: message, + } +} + +// VerifyTxOutProofCmd defines the verifytxoutproof JSON-RPC command. +type VerifyTxOutProofCmd struct { + Proof string +} + +// NewVerifyTxOutProofCmd returns a new instance which can be used to issue a +// verifytxoutproof JSON-RPC command. +func NewVerifyTxOutProofCmd(proof string) *VerifyTxOutProofCmd { + return &VerifyTxOutProofCmd{ + Proof: proof, + } +} + +func init() { + // No special flags for commands in this file. + flags := UsageFlag(0) + + MustRegisterCmd("addnode", (*AddNodeCmd)(nil), flags) + MustRegisterCmd("createrawtransaction", (*CreateRawTransactionCmd)(nil), flags) + MustRegisterCmd("decoderawtransaction", (*DecodeRawTransactionCmd)(nil), flags) + MustRegisterCmd("decodescript", (*DecodeScriptCmd)(nil), flags) + MustRegisterCmd("getaddednodeinfo", (*GetAddedNodeInfoCmd)(nil), flags) + MustRegisterCmd("getbestblockhash", (*GetBestBlockHashCmd)(nil), flags) + MustRegisterCmd("getblock", (*GetBlockCmd)(nil), flags) + MustRegisterCmd("getblockchaininfo", (*GetBlockChainInfoCmd)(nil), flags) + MustRegisterCmd("getblockcount", (*GetBlockCountCmd)(nil), flags) + MustRegisterCmd("getblockhash", (*GetBlockHashCmd)(nil), flags) + MustRegisterCmd("getblockheader", (*GetBlockHeaderCmd)(nil), flags) + MustRegisterCmd("getblocktemplate", (*GetBlockTemplateCmd)(nil), flags) + MustRegisterCmd("getchaintips", (*GetChainTipsCmd)(nil), flags) + MustRegisterCmd("getconnectioncount", (*GetConnectionCountCmd)(nil), flags) + MustRegisterCmd("getdifficulty", (*GetDifficultyCmd)(nil), flags) + MustRegisterCmd("getgenerate", (*GetGenerateCmd)(nil), flags) + MustRegisterCmd("gethashespersec", (*GetHashesPerSecCmd)(nil), flags) + MustRegisterCmd("getinfo", (*GetInfoCmd)(nil), flags) + MustRegisterCmd("getmempoolinfo", (*GetMempoolInfoCmd)(nil), flags) + MustRegisterCmd("getmininginfo", (*GetMiningInfoCmd)(nil), flags) + MustRegisterCmd("getnetworkinfo", (*GetNetworkInfoCmd)(nil), flags) + MustRegisterCmd("getnettotals", (*GetNetTotalsCmd)(nil), flags) + MustRegisterCmd("getnetworkhashps", (*GetNetworkHashPSCmd)(nil), flags) + MustRegisterCmd("getpeerinfo", (*GetPeerInfoCmd)(nil), flags) + MustRegisterCmd("getrawmempool", (*GetRawMempoolCmd)(nil), flags) + MustRegisterCmd("getrawtransaction", (*GetRawTransactionCmd)(nil), flags) + MustRegisterCmd("gettxout", (*GetTxOutCmd)(nil), flags) + MustRegisterCmd("gettxoutproof", (*GetTxOutProofCmd)(nil), flags) + MustRegisterCmd("gettxoutsetinfo", (*GetTxOutSetInfoCmd)(nil), flags) + MustRegisterCmd("getwork", (*GetWorkCmd)(nil), flags) + MustRegisterCmd("help", (*HelpCmd)(nil), flags) + MustRegisterCmd("invalidateblock", (*InvalidateBlockCmd)(nil), flags) + MustRegisterCmd("ping", (*PingCmd)(nil), flags) + MustRegisterCmd("reconsiderblock", (*ReconsiderBlockCmd)(nil), flags) + MustRegisterCmd("searchrawtransactions", (*SearchRawTransactionsCmd)(nil), flags) + MustRegisterCmd("sendrawtransaction", (*SendRawTransactionCmd)(nil), flags) + MustRegisterCmd("setgenerate", (*SetGenerateCmd)(nil), flags) + MustRegisterCmd("stop", (*StopCmd)(nil), flags) + MustRegisterCmd("submitblock", (*SubmitBlockCmd)(nil), flags) + MustRegisterCmd("validateaddress", (*ValidateAddressCmd)(nil), flags) + MustRegisterCmd("verifychain", (*VerifyChainCmd)(nil), flags) + MustRegisterCmd("verifymessage", (*VerifyMessageCmd)(nil), flags) + MustRegisterCmd("verifytxoutproof", (*VerifyTxOutProofCmd)(nil), flags) +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/chainsvrcmds_test.go b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrcmds_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a34d104723ad197a15e228c9a55f7b3b65f27ba1 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrcmds_test.go @@ -0,0 +1,1139 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestChainSvrCmds tests all of the chain server commands marshal and unmarshal +// into valid results include handling of optional fields being omitted in the +// marshalled command, while optional fields with defaults have the default +// assigned on unmarshalled commands. +func TestChainSvrCmds(t *testing.T) { + t.Parallel() + + testID := int(1) + tests := []struct { + name string + newCmd func() (interface{}, error) + staticCmd func() interface{} + marshalled string + unmarshalled interface{} + }{ + { + name: "addnode", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("addnode", "127.0.0.1", btcjson.ANRemove) + }, + staticCmd: func() interface{} { + return btcjson.NewAddNodeCmd("127.0.0.1", btcjson.ANRemove) + }, + marshalled: `{"jsonrpc":"1.0","method":"addnode","params":["127.0.0.1","remove"],"id":1}`, + unmarshalled: &btcjson.AddNodeCmd{Addr: "127.0.0.1", SubCmd: btcjson.ANRemove}, + }, + { + name: "createrawtransaction", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("createrawtransaction", `[{"txid":"123","vout":1}]`, + `{"456":0.0123}`) + }, + staticCmd: func() interface{} { + txInputs := []btcjson.TransactionInput{ + {Txid: "123", Vout: 1}, + } + amounts := map[string]float64{"456": .0123} + return btcjson.NewCreateRawTransactionCmd(txInputs, amounts, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[{"txid":"123","vout":1}],{"456":0.0123}],"id":1}`, + unmarshalled: &btcjson.CreateRawTransactionCmd{ + Inputs: []btcjson.TransactionInput{{Txid: "123", Vout: 1}}, + Amounts: map[string]float64{"456": .0123}, + }, + }, + { + name: "createrawtransaction optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("createrawtransaction", `[{"txid":"123","vout":1}]`, + `{"456":0.0123}`, int64(12312333333)) + }, + staticCmd: func() interface{} { + txInputs := []btcjson.TransactionInput{ + {Txid: "123", Vout: 1}, + } + amounts := map[string]float64{"456": .0123} + return btcjson.NewCreateRawTransactionCmd(txInputs, amounts, btcjson.Int64(12312333333)) + }, + marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[{"txid":"123","vout":1}],{"456":0.0123},12312333333],"id":1}`, + unmarshalled: &btcjson.CreateRawTransactionCmd{ + Inputs: []btcjson.TransactionInput{{Txid: "123", Vout: 1}}, + Amounts: map[string]float64{"456": .0123}, + LockTime: btcjson.Int64(12312333333), + }, + }, + + { + name: "decoderawtransaction", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("decoderawtransaction", "123") + }, + staticCmd: func() interface{} { + return btcjson.NewDecodeRawTransactionCmd("123") + }, + marshalled: `{"jsonrpc":"1.0","method":"decoderawtransaction","params":["123"],"id":1}`, + unmarshalled: &btcjson.DecodeRawTransactionCmd{HexTx: "123"}, + }, + { + name: "decodescript", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("decodescript", "00") + }, + staticCmd: func() interface{} { + return btcjson.NewDecodeScriptCmd("00") + }, + marshalled: `{"jsonrpc":"1.0","method":"decodescript","params":["00"],"id":1}`, + unmarshalled: &btcjson.DecodeScriptCmd{HexScript: "00"}, + }, + { + name: "getaddednodeinfo", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getaddednodeinfo", true) + }, + staticCmd: func() interface{} { + return btcjson.NewGetAddedNodeInfoCmd(true, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getaddednodeinfo","params":[true],"id":1}`, + unmarshalled: &btcjson.GetAddedNodeInfoCmd{DNS: true, Node: nil}, + }, + { + name: "getaddednodeinfo optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getaddednodeinfo", true, "127.0.0.1") + }, + staticCmd: func() interface{} { + return btcjson.NewGetAddedNodeInfoCmd(true, btcjson.String("127.0.0.1")) + }, + marshalled: `{"jsonrpc":"1.0","method":"getaddednodeinfo","params":[true,"127.0.0.1"],"id":1}`, + unmarshalled: &btcjson.GetAddedNodeInfoCmd{ + DNS: true, + Node: btcjson.String("127.0.0.1"), + }, + }, + { + name: "getbestblockhash", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getbestblockhash") + }, + staticCmd: func() interface{} { + return btcjson.NewGetBestBlockHashCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getbestblockhash","params":[],"id":1}`, + unmarshalled: &btcjson.GetBestBlockHashCmd{}, + }, + { + name: "getblock", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getblock", "123") + }, + staticCmd: func() interface{} { + return btcjson.NewGetBlockCmd("123", nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123"],"id":1}`, + unmarshalled: &btcjson.GetBlockCmd{ + Hash: "123", + Verbose: btcjson.Bool(true), + VerboseTx: btcjson.Bool(false), + }, + }, + { + name: "getblock required optional1", + newCmd: func() (interface{}, error) { + // Intentionally use a source param that is + // more pointers than the destination to + // exercise that path. + verbosePtr := btcjson.Bool(true) + return btcjson.NewCmd("getblock", "123", &verbosePtr) + }, + staticCmd: func() interface{} { + return btcjson.NewGetBlockCmd("123", btcjson.Bool(true), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",true],"id":1}`, + unmarshalled: &btcjson.GetBlockCmd{ + Hash: "123", + Verbose: btcjson.Bool(true), + VerboseTx: btcjson.Bool(false), + }, + }, + { + name: "getblock required optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getblock", "123", true, true) + }, + staticCmd: func() interface{} { + return btcjson.NewGetBlockCmd("123", btcjson.Bool(true), btcjson.Bool(true)) + }, + marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",true,true],"id":1}`, + unmarshalled: &btcjson.GetBlockCmd{ + Hash: "123", + Verbose: btcjson.Bool(true), + VerboseTx: btcjson.Bool(true), + }, + }, + { + name: "getblockchaininfo", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getblockchaininfo") + }, + staticCmd: func() interface{} { + return btcjson.NewGetBlockChainInfoCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getblockchaininfo","params":[],"id":1}`, + unmarshalled: &btcjson.GetBlockChainInfoCmd{}, + }, + { + name: "getblockcount", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getblockcount") + }, + staticCmd: func() interface{} { + return btcjson.NewGetBlockCountCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getblockcount","params":[],"id":1}`, + unmarshalled: &btcjson.GetBlockCountCmd{}, + }, + { + name: "getblockhash", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getblockhash", 123) + }, + staticCmd: func() interface{} { + return btcjson.NewGetBlockHashCmd(123) + }, + marshalled: `{"jsonrpc":"1.0","method":"getblockhash","params":[123],"id":1}`, + unmarshalled: &btcjson.GetBlockHashCmd{Index: 123}, + }, + { + name: "getblockheader", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getblockheader", "123") + }, + staticCmd: func() interface{} { + return btcjson.NewGetBlockHeaderCmd("123", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getblockheader","params":["123"],"id":1}`, + unmarshalled: &btcjson.GetBlockHeaderCmd{ + Hash: "123", + Verbose: btcjson.Bool(true), + }, + }, + { + name: "getblocktemplate", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getblocktemplate") + }, + staticCmd: func() interface{} { + return btcjson.NewGetBlockTemplateCmd(nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getblocktemplate","params":[],"id":1}`, + unmarshalled: &btcjson.GetBlockTemplateCmd{Request: nil}, + }, + { + name: "getblocktemplate optional - template request", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getblocktemplate", `{"mode":"template","capabilities":["longpoll","coinbasetxn"]}`) + }, + staticCmd: func() interface{} { + template := btcjson.TemplateRequest{ + Mode: "template", + Capabilities: []string{"longpoll", "coinbasetxn"}, + } + return btcjson.NewGetBlockTemplateCmd(&template) + }, + marshalled: `{"jsonrpc":"1.0","method":"getblocktemplate","params":[{"mode":"template","capabilities":["longpoll","coinbasetxn"]}],"id":1}`, + unmarshalled: &btcjson.GetBlockTemplateCmd{ + Request: &btcjson.TemplateRequest{ + Mode: "template", + Capabilities: []string{"longpoll", "coinbasetxn"}, + }, + }, + }, + { + name: "getblocktemplate optional - template request with tweaks", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getblocktemplate", `{"mode":"template","capabilities":["longpoll","coinbasetxn"],"sigoplimit":500,"sizelimit":100000000,"maxversion":2}`) + }, + staticCmd: func() interface{} { + template := btcjson.TemplateRequest{ + Mode: "template", + Capabilities: []string{"longpoll", "coinbasetxn"}, + SigOpLimit: 500, + SizeLimit: 100000000, + MaxVersion: 2, + } + return btcjson.NewGetBlockTemplateCmd(&template) + }, + marshalled: `{"jsonrpc":"1.0","method":"getblocktemplate","params":[{"mode":"template","capabilities":["longpoll","coinbasetxn"],"sigoplimit":500,"sizelimit":100000000,"maxversion":2}],"id":1}`, + unmarshalled: &btcjson.GetBlockTemplateCmd{ + Request: &btcjson.TemplateRequest{ + Mode: "template", + Capabilities: []string{"longpoll", "coinbasetxn"}, + SigOpLimit: int64(500), + SizeLimit: int64(100000000), + MaxVersion: 2, + }, + }, + }, + { + name: "getblocktemplate optional - template request with tweaks 2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getblocktemplate", `{"mode":"template","capabilities":["longpoll","coinbasetxn"],"sigoplimit":true,"sizelimit":100000000,"maxversion":2}`) + }, + staticCmd: func() interface{} { + template := btcjson.TemplateRequest{ + Mode: "template", + Capabilities: []string{"longpoll", "coinbasetxn"}, + SigOpLimit: true, + SizeLimit: 100000000, + MaxVersion: 2, + } + return btcjson.NewGetBlockTemplateCmd(&template) + }, + marshalled: `{"jsonrpc":"1.0","method":"getblocktemplate","params":[{"mode":"template","capabilities":["longpoll","coinbasetxn"],"sigoplimit":true,"sizelimit":100000000,"maxversion":2}],"id":1}`, + unmarshalled: &btcjson.GetBlockTemplateCmd{ + Request: &btcjson.TemplateRequest{ + Mode: "template", + Capabilities: []string{"longpoll", "coinbasetxn"}, + SigOpLimit: true, + SizeLimit: int64(100000000), + MaxVersion: 2, + }, + }, + }, + { + name: "getchaintips", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getchaintips") + }, + staticCmd: func() interface{} { + return btcjson.NewGetChainTipsCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getchaintips","params":[],"id":1}`, + unmarshalled: &btcjson.GetChainTipsCmd{}, + }, + { + name: "getconnectioncount", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getconnectioncount") + }, + staticCmd: func() interface{} { + return btcjson.NewGetConnectionCountCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getconnectioncount","params":[],"id":1}`, + unmarshalled: &btcjson.GetConnectionCountCmd{}, + }, + { + name: "getdifficulty", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getdifficulty") + }, + staticCmd: func() interface{} { + return btcjson.NewGetDifficultyCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getdifficulty","params":[],"id":1}`, + unmarshalled: &btcjson.GetDifficultyCmd{}, + }, + { + name: "getgenerate", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getgenerate") + }, + staticCmd: func() interface{} { + return btcjson.NewGetGenerateCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getgenerate","params":[],"id":1}`, + unmarshalled: &btcjson.GetGenerateCmd{}, + }, + { + name: "gethashespersec", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("gethashespersec") + }, + staticCmd: func() interface{} { + return btcjson.NewGetHashesPerSecCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"gethashespersec","params":[],"id":1}`, + unmarshalled: &btcjson.GetHashesPerSecCmd{}, + }, + { + name: "getinfo", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getinfo") + }, + staticCmd: func() interface{} { + return btcjson.NewGetInfoCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getinfo","params":[],"id":1}`, + unmarshalled: &btcjson.GetInfoCmd{}, + }, + { + name: "getmempoolinfo", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getmempoolinfo") + }, + staticCmd: func() interface{} { + return btcjson.NewGetMempoolInfoCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getmempoolinfo","params":[],"id":1}`, + unmarshalled: &btcjson.GetMempoolInfoCmd{}, + }, + { + name: "getmininginfo", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getmininginfo") + }, + staticCmd: func() interface{} { + return btcjson.NewGetMiningInfoCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getmininginfo","params":[],"id":1}`, + unmarshalled: &btcjson.GetMiningInfoCmd{}, + }, + { + name: "getnetworkinfo", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getnetworkinfo") + }, + staticCmd: func() interface{} { + return btcjson.NewGetNetworkInfoCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getnetworkinfo","params":[],"id":1}`, + unmarshalled: &btcjson.GetNetworkInfoCmd{}, + }, + { + name: "getnettotals", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getnettotals") + }, + staticCmd: func() interface{} { + return btcjson.NewGetNetTotalsCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getnettotals","params":[],"id":1}`, + unmarshalled: &btcjson.GetNetTotalsCmd{}, + }, + { + name: "getnetworkhashps", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getnetworkhashps") + }, + staticCmd: func() interface{} { + return btcjson.NewGetNetworkHashPSCmd(nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getnetworkhashps","params":[],"id":1}`, + unmarshalled: &btcjson.GetNetworkHashPSCmd{ + Blocks: btcjson.Int(120), + Height: btcjson.Int(-1), + }, + }, + { + name: "getnetworkhashps optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getnetworkhashps", 200) + }, + staticCmd: func() interface{} { + return btcjson.NewGetNetworkHashPSCmd(btcjson.Int(200), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getnetworkhashps","params":[200],"id":1}`, + unmarshalled: &btcjson.GetNetworkHashPSCmd{ + Blocks: btcjson.Int(200), + Height: btcjson.Int(-1), + }, + }, + { + name: "getnetworkhashps optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getnetworkhashps", 200, 123) + }, + staticCmd: func() interface{} { + return btcjson.NewGetNetworkHashPSCmd(btcjson.Int(200), btcjson.Int(123)) + }, + marshalled: `{"jsonrpc":"1.0","method":"getnetworkhashps","params":[200,123],"id":1}`, + unmarshalled: &btcjson.GetNetworkHashPSCmd{ + Blocks: btcjson.Int(200), + Height: btcjson.Int(123), + }, + }, + { + name: "getpeerinfo", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getpeerinfo") + }, + staticCmd: func() interface{} { + return btcjson.NewGetPeerInfoCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getpeerinfo","params":[],"id":1}`, + unmarshalled: &btcjson.GetPeerInfoCmd{}, + }, + { + name: "getrawmempool", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getrawmempool") + }, + staticCmd: func() interface{} { + return btcjson.NewGetRawMempoolCmd(nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getrawmempool","params":[],"id":1}`, + unmarshalled: &btcjson.GetRawMempoolCmd{ + Verbose: btcjson.Bool(false), + }, + }, + { + name: "getrawmempool optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getrawmempool", false) + }, + staticCmd: func() interface{} { + return btcjson.NewGetRawMempoolCmd(btcjson.Bool(false)) + }, + marshalled: `{"jsonrpc":"1.0","method":"getrawmempool","params":[false],"id":1}`, + unmarshalled: &btcjson.GetRawMempoolCmd{ + Verbose: btcjson.Bool(false), + }, + }, + { + name: "getrawtransaction", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getrawtransaction", "123") + }, + staticCmd: func() interface{} { + return btcjson.NewGetRawTransactionCmd("123", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getrawtransaction","params":["123"],"id":1}`, + unmarshalled: &btcjson.GetRawTransactionCmd{ + Txid: "123", + Verbose: btcjson.Int(0), + }, + }, + { + name: "getrawtransaction optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getrawtransaction", "123", 1) + }, + staticCmd: func() interface{} { + return btcjson.NewGetRawTransactionCmd("123", btcjson.Int(1)) + }, + marshalled: `{"jsonrpc":"1.0","method":"getrawtransaction","params":["123",1],"id":1}`, + unmarshalled: &btcjson.GetRawTransactionCmd{ + Txid: "123", + Verbose: btcjson.Int(1), + }, + }, + { + name: "gettxout", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("gettxout", "123", 1) + }, + staticCmd: func() interface{} { + return btcjson.NewGetTxOutCmd("123", 1, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"gettxout","params":["123",1],"id":1}`, + unmarshalled: &btcjson.GetTxOutCmd{ + Txid: "123", + Vout: 1, + IncludeMempool: btcjson.Bool(true), + }, + }, + { + name: "gettxout optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("gettxout", "123", 1, true) + }, + staticCmd: func() interface{} { + return btcjson.NewGetTxOutCmd("123", 1, btcjson.Bool(true)) + }, + marshalled: `{"jsonrpc":"1.0","method":"gettxout","params":["123",1,true],"id":1}`, + unmarshalled: &btcjson.GetTxOutCmd{ + Txid: "123", + Vout: 1, + IncludeMempool: btcjson.Bool(true), + }, + }, + { + name: "gettxoutproof", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("gettxoutproof", []string{"123", "456"}) + }, + staticCmd: func() interface{} { + return btcjson.NewGetTxOutProofCmd([]string{"123", "456"}, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"gettxoutproof","params":[["123","456"]],"id":1}`, + unmarshalled: &btcjson.GetTxOutProofCmd{ + TxIDs: []string{"123", "456"}, + }, + }, + { + name: "gettxoutproof optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("gettxoutproof", []string{"123", "456"}, + btcjson.String("000000000000034a7dedef4a161fa058a2d67a173a90155f3a2fe6fc132e0ebf")) + }, + staticCmd: func() interface{} { + return btcjson.NewGetTxOutProofCmd([]string{"123", "456"}, + btcjson.String("000000000000034a7dedef4a161fa058a2d67a173a90155f3a2fe6fc132e0ebf")) + }, + marshalled: `{"jsonrpc":"1.0","method":"gettxoutproof","params":[["123","456"],` + + `"000000000000034a7dedef4a161fa058a2d67a173a90155f3a2fe6fc132e0ebf"],"id":1}`, + unmarshalled: &btcjson.GetTxOutProofCmd{ + TxIDs: []string{"123", "456"}, + BlockHash: btcjson.String("000000000000034a7dedef4a161fa058a2d67a173a90155f3a2fe6fc132e0ebf"), + }, + }, + { + name: "gettxoutsetinfo", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("gettxoutsetinfo") + }, + staticCmd: func() interface{} { + return btcjson.NewGetTxOutSetInfoCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"gettxoutsetinfo","params":[],"id":1}`, + unmarshalled: &btcjson.GetTxOutSetInfoCmd{}, + }, + { + name: "getwork", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getwork") + }, + staticCmd: func() interface{} { + return btcjson.NewGetWorkCmd(nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getwork","params":[],"id":1}`, + unmarshalled: &btcjson.GetWorkCmd{ + Data: nil, + }, + }, + { + name: "getwork optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getwork", "00112233") + }, + staticCmd: func() interface{} { + return btcjson.NewGetWorkCmd(btcjson.String("00112233")) + }, + marshalled: `{"jsonrpc":"1.0","method":"getwork","params":["00112233"],"id":1}`, + unmarshalled: &btcjson.GetWorkCmd{ + Data: btcjson.String("00112233"), + }, + }, + { + name: "help", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("help") + }, + staticCmd: func() interface{} { + return btcjson.NewHelpCmd(nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"help","params":[],"id":1}`, + unmarshalled: &btcjson.HelpCmd{ + Command: nil, + }, + }, + { + name: "help optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("help", "getblock") + }, + staticCmd: func() interface{} { + return btcjson.NewHelpCmd(btcjson.String("getblock")) + }, + marshalled: `{"jsonrpc":"1.0","method":"help","params":["getblock"],"id":1}`, + unmarshalled: &btcjson.HelpCmd{ + Command: btcjson.String("getblock"), + }, + }, + { + name: "invalidateblock", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("invalidateblock", "123") + }, + staticCmd: func() interface{} { + return btcjson.NewInvalidateBlockCmd("123") + }, + marshalled: `{"jsonrpc":"1.0","method":"invalidateblock","params":["123"],"id":1}`, + unmarshalled: &btcjson.InvalidateBlockCmd{ + BlockHash: "123", + }, + }, + { + name: "ping", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("ping") + }, + staticCmd: func() interface{} { + return btcjson.NewPingCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"ping","params":[],"id":1}`, + unmarshalled: &btcjson.PingCmd{}, + }, + { + name: "reconsiderblock", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("reconsiderblock", "123") + }, + staticCmd: func() interface{} { + return btcjson.NewReconsiderBlockCmd("123") + }, + marshalled: `{"jsonrpc":"1.0","method":"reconsiderblock","params":["123"],"id":1}`, + unmarshalled: &btcjson.ReconsiderBlockCmd{ + BlockHash: "123", + }, + }, + { + name: "searchrawtransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("searchrawtransactions", "1Address") + }, + staticCmd: func() interface{} { + return btcjson.NewSearchRawTransactionsCmd("1Address", nil, nil, nil, nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address"],"id":1}`, + unmarshalled: &btcjson.SearchRawTransactionsCmd{ + Address: "1Address", + Verbose: btcjson.Int(1), + Skip: btcjson.Int(0), + Count: btcjson.Int(100), + VinExtra: btcjson.Int(0), + Reverse: btcjson.Bool(false), + FilterAddrs: nil, + }, + }, + { + name: "searchrawtransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("searchrawtransactions", "1Address", 0) + }, + staticCmd: func() interface{} { + return btcjson.NewSearchRawTransactionsCmd("1Address", + btcjson.Int(0), nil, nil, nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0],"id":1}`, + unmarshalled: &btcjson.SearchRawTransactionsCmd{ + Address: "1Address", + Verbose: btcjson.Int(0), + Skip: btcjson.Int(0), + Count: btcjson.Int(100), + VinExtra: btcjson.Int(0), + Reverse: btcjson.Bool(false), + FilterAddrs: nil, + }, + }, + { + name: "searchrawtransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("searchrawtransactions", "1Address", 0, 5) + }, + staticCmd: func() interface{} { + return btcjson.NewSearchRawTransactionsCmd("1Address", + btcjson.Int(0), btcjson.Int(5), nil, nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5],"id":1}`, + unmarshalled: &btcjson.SearchRawTransactionsCmd{ + Address: "1Address", + Verbose: btcjson.Int(0), + Skip: btcjson.Int(5), + Count: btcjson.Int(100), + VinExtra: btcjson.Int(0), + Reverse: btcjson.Bool(false), + FilterAddrs: nil, + }, + }, + { + name: "searchrawtransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("searchrawtransactions", "1Address", 0, 5, 10) + }, + staticCmd: func() interface{} { + return btcjson.NewSearchRawTransactionsCmd("1Address", + btcjson.Int(0), btcjson.Int(5), btcjson.Int(10), nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5,10],"id":1}`, + unmarshalled: &btcjson.SearchRawTransactionsCmd{ + Address: "1Address", + Verbose: btcjson.Int(0), + Skip: btcjson.Int(5), + Count: btcjson.Int(10), + VinExtra: btcjson.Int(0), + Reverse: btcjson.Bool(false), + FilterAddrs: nil, + }, + }, + { + name: "searchrawtransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("searchrawtransactions", "1Address", 0, 5, 10, 1) + }, + staticCmd: func() interface{} { + return btcjson.NewSearchRawTransactionsCmd("1Address", + btcjson.Int(0), btcjson.Int(5), btcjson.Int(10), btcjson.Int(1), nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5,10,1],"id":1}`, + unmarshalled: &btcjson.SearchRawTransactionsCmd{ + Address: "1Address", + Verbose: btcjson.Int(0), + Skip: btcjson.Int(5), + Count: btcjson.Int(10), + VinExtra: btcjson.Int(1), + Reverse: btcjson.Bool(false), + FilterAddrs: nil, + }, + }, + { + name: "searchrawtransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("searchrawtransactions", "1Address", 0, 5, 10, 1, true) + }, + staticCmd: func() interface{} { + return btcjson.NewSearchRawTransactionsCmd("1Address", + btcjson.Int(0), btcjson.Int(5), btcjson.Int(10), btcjson.Int(1), btcjson.Bool(true), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5,10,1,true],"id":1}`, + unmarshalled: &btcjson.SearchRawTransactionsCmd{ + Address: "1Address", + Verbose: btcjson.Int(0), + Skip: btcjson.Int(5), + Count: btcjson.Int(10), + VinExtra: btcjson.Int(1), + Reverse: btcjson.Bool(true), + FilterAddrs: nil, + }, + }, + { + name: "searchrawtransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("searchrawtransactions", "1Address", 0, 5, 10, 1, true, []string{"1Address"}) + }, + staticCmd: func() interface{} { + return btcjson.NewSearchRawTransactionsCmd("1Address", + btcjson.Int(0), btcjson.Int(5), btcjson.Int(10), btcjson.Int(1), btcjson.Bool(true), &[]string{"1Address"}) + }, + marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5,10,1,true,["1Address"]],"id":1}`, + unmarshalled: &btcjson.SearchRawTransactionsCmd{ + Address: "1Address", + Verbose: btcjson.Int(0), + Skip: btcjson.Int(5), + Count: btcjson.Int(10), + VinExtra: btcjson.Int(1), + Reverse: btcjson.Bool(true), + FilterAddrs: &[]string{"1Address"}, + }, + }, + { + name: "sendrawtransaction", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("sendrawtransaction", "1122") + }, + staticCmd: func() interface{} { + return btcjson.NewSendRawTransactionCmd("1122", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"sendrawtransaction","params":["1122"],"id":1}`, + unmarshalled: &btcjson.SendRawTransactionCmd{ + HexTx: "1122", + AllowHighFees: btcjson.Bool(false), + }, + }, + { + name: "sendrawtransaction optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("sendrawtransaction", "1122", false) + }, + staticCmd: func() interface{} { + return btcjson.NewSendRawTransactionCmd("1122", btcjson.Bool(false)) + }, + marshalled: `{"jsonrpc":"1.0","method":"sendrawtransaction","params":["1122",false],"id":1}`, + unmarshalled: &btcjson.SendRawTransactionCmd{ + HexTx: "1122", + AllowHighFees: btcjson.Bool(false), + }, + }, + { + name: "setgenerate", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("setgenerate", true) + }, + staticCmd: func() interface{} { + return btcjson.NewSetGenerateCmd(true, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"setgenerate","params":[true],"id":1}`, + unmarshalled: &btcjson.SetGenerateCmd{ + Generate: true, + GenProcLimit: btcjson.Int(-1), + }, + }, + { + name: "setgenerate optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("setgenerate", true, 6) + }, + staticCmd: func() interface{} { + return btcjson.NewSetGenerateCmd(true, btcjson.Int(6)) + }, + marshalled: `{"jsonrpc":"1.0","method":"setgenerate","params":[true,6],"id":1}`, + unmarshalled: &btcjson.SetGenerateCmd{ + Generate: true, + GenProcLimit: btcjson.Int(6), + }, + }, + { + name: "stop", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("stop") + }, + staticCmd: func() interface{} { + return btcjson.NewStopCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"stop","params":[],"id":1}`, + unmarshalled: &btcjson.StopCmd{}, + }, + { + name: "submitblock", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("submitblock", "112233") + }, + staticCmd: func() interface{} { + return btcjson.NewSubmitBlockCmd("112233", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"submitblock","params":["112233"],"id":1}`, + unmarshalled: &btcjson.SubmitBlockCmd{ + HexBlock: "112233", + Options: nil, + }, + }, + { + name: "submitblock optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("submitblock", "112233", `{"workid":"12345"}`) + }, + staticCmd: func() interface{} { + options := btcjson.SubmitBlockOptions{ + WorkID: "12345", + } + return btcjson.NewSubmitBlockCmd("112233", &options) + }, + marshalled: `{"jsonrpc":"1.0","method":"submitblock","params":["112233",{"workid":"12345"}],"id":1}`, + unmarshalled: &btcjson.SubmitBlockCmd{ + HexBlock: "112233", + Options: &btcjson.SubmitBlockOptions{ + WorkID: "12345", + }, + }, + }, + { + name: "validateaddress", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("validateaddress", "1Address") + }, + staticCmd: func() interface{} { + return btcjson.NewValidateAddressCmd("1Address") + }, + marshalled: `{"jsonrpc":"1.0","method":"validateaddress","params":["1Address"],"id":1}`, + unmarshalled: &btcjson.ValidateAddressCmd{ + Address: "1Address", + }, + }, + { + name: "verifychain", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("verifychain") + }, + staticCmd: func() interface{} { + return btcjson.NewVerifyChainCmd(nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"verifychain","params":[],"id":1}`, + unmarshalled: &btcjson.VerifyChainCmd{ + CheckLevel: btcjson.Int32(3), + CheckDepth: btcjson.Int32(288), + }, + }, + { + name: "verifychain optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("verifychain", 2) + }, + staticCmd: func() interface{} { + return btcjson.NewVerifyChainCmd(btcjson.Int32(2), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"verifychain","params":[2],"id":1}`, + unmarshalled: &btcjson.VerifyChainCmd{ + CheckLevel: btcjson.Int32(2), + CheckDepth: btcjson.Int32(288), + }, + }, + { + name: "verifychain optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("verifychain", 2, 500) + }, + staticCmd: func() interface{} { + return btcjson.NewVerifyChainCmd(btcjson.Int32(2), btcjson.Int32(500)) + }, + marshalled: `{"jsonrpc":"1.0","method":"verifychain","params":[2,500],"id":1}`, + unmarshalled: &btcjson.VerifyChainCmd{ + CheckLevel: btcjson.Int32(2), + CheckDepth: btcjson.Int32(500), + }, + }, + { + name: "verifymessage", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("verifymessage", "1Address", "301234", "test") + }, + staticCmd: func() interface{} { + return btcjson.NewVerifyMessageCmd("1Address", "301234", "test") + }, + marshalled: `{"jsonrpc":"1.0","method":"verifymessage","params":["1Address","301234","test"],"id":1}`, + unmarshalled: &btcjson.VerifyMessageCmd{ + Address: "1Address", + Signature: "301234", + Message: "test", + }, + }, + { + name: "verifytxoutproof", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("verifytxoutproof", "test") + }, + staticCmd: func() interface{} { + return btcjson.NewVerifyTxOutProofCmd("test") + }, + marshalled: `{"jsonrpc":"1.0","method":"verifytxoutproof","params":["test"],"id":1}`, + unmarshalled: &btcjson.VerifyTxOutProofCmd{ + Proof: "test", + }, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Marshal the command as created by the new static command + // creation function. + marshalled, err := btcjson.MarshalCmd(testID, test.staticCmd()) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + t.Errorf("\n%s\n%s", marshalled, test.marshalled) + continue + } + + // Ensure the command is created without error via the generic + // new command creation function. + cmd, err := test.newCmd() + if err != nil { + t.Errorf("Test #%d (%s) unexpected NewCmd error: %v ", + i, test.name, err) + } + + // Marshal the command as created by the generic new command + // creation function. + marshalled, err = btcjson.MarshalCmd(testID, cmd) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + var request btcjson.Request + if err := json.Unmarshal(marshalled, &request); err != nil { + t.Errorf("Test #%d (%s) unexpected error while "+ + "unmarshalling JSON-RPC request: %v", i, + test.name, err) + continue + } + + cmd, err = btcjson.UnmarshalCmd(&request) + if err != nil { + t.Errorf("UnmarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !reflect.DeepEqual(cmd, test.unmarshalled) { + t.Errorf("Test #%d (%s) unexpected unmarshalled command "+ + "- got %s, want %s", i, test.name, + fmt.Sprintf("(%T) %+[1]v", cmd), + fmt.Sprintf("(%T) %+[1]v\n", test.unmarshalled)) + continue + } + } +} + +// TestChainSvrCmdErrors ensures any errors that occur in the command during +// custom mashal and unmarshal are as expected. +func TestChainSvrCmdErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + result interface{} + marshalled string + err error + }{ + { + name: "template request with invalid type", + result: &btcjson.TemplateRequest{}, + marshalled: `{"mode":1}`, + err: &json.UnmarshalTypeError{}, + }, + { + name: "invalid template request sigoplimit field", + result: &btcjson.TemplateRequest{}, + marshalled: `{"sigoplimit":"invalid"}`, + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "invalid template request sizelimit field", + result: &btcjson.TemplateRequest{}, + marshalled: `{"sizelimit":"invalid"}`, + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + err := json.Unmarshal([]byte(test.marshalled), &test.result) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+ + "want %T", i, test.name, err, test.err) + continue + } + + if terr, ok := test.err.(btcjson.Error); ok { + gotErrorCode := err.(btcjson.Error).ErrorCode + if gotErrorCode != terr.ErrorCode { + t.Errorf("Test #%d (%s) mismatched error code "+ + "- got %v (%v), want %v", i, test.name, + gotErrorCode, terr, terr.ErrorCode) + continue + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/chainsvrresults.go b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrresults.go new file mode 100644 index 0000000000000000000000000000000000000000..56728429868a03c4016bf72d81f31b146b832398 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrresults.go @@ -0,0 +1,429 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +import "encoding/json" + +// GetBlockHeaderVerboseResult models the data from the getblockheader command when +// the verbose flag is set. When the verbose flag is not set, getblockheader +// returns a hex-encoded string. +type GetBlockHeaderVerboseResult struct { + Hash string `json:"hash"` + Confirmations uint64 `json:"confirmations"` + Height int32 `json:"height"` + Version int32 `json:"version"` + MerkleRoot string `json:"merkleroot"` + Time int64 `json:"time"` + Nonce uint64 `json:"nonce"` + Bits string `json:"bits"` + Difficulty float64 `json:"difficulty"` + PreviousHash string `json:"previousblockhash,omitempty"` + NextHash string `json:"nextblockhash,omitempty"` +} + +// GetBlockVerboseResult models the data from the getblock command when the +// verbose flag is set. When the verbose flag is not set, getblock returns a +// hex-encoded string. +type GetBlockVerboseResult struct { + Hash string `json:"hash"` + Confirmations uint64 `json:"confirmations"` + Size int32 `json:"size"` + Height int64 `json:"height"` + Version int32 `json:"version"` + MerkleRoot string `json:"merkleroot"` + Tx []string `json:"tx,omitempty"` + RawTx []TxRawResult `json:"rawtx,omitempty"` + Time int64 `json:"time"` + Nonce uint32 `json:"nonce"` + Bits string `json:"bits"` + Difficulty float64 `json:"difficulty"` + PreviousHash string `json:"previousblockhash"` + NextHash string `json:"nextblockhash,omitempty"` +} + +// CreateMultiSigResult models the data returned from the createmultisig +// command. +type CreateMultiSigResult struct { + Address string `json:"address"` + RedeemScript string `json:"redeemScript"` +} + +// DecodeScriptResult models the data returned from the decodescript command. +type DecodeScriptResult struct { + Asm string `json:"asm"` + ReqSigs int32 `json:"reqSigs,omitempty"` + Type string `json:"type"` + Addresses []string `json:"addresses,omitempty"` + P2sh string `json:"p2sh"` +} + +// GetAddedNodeInfoResultAddr models the data of the addresses portion of the +// getaddednodeinfo command. +type GetAddedNodeInfoResultAddr struct { + Address string `json:"address"` + Connected string `json:"connected"` +} + +// GetAddedNodeInfoResult models the data from the getaddednodeinfo command. +type GetAddedNodeInfoResult struct { + AddedNode string `json:"addednode"` + Connected *bool `json:"connected,omitempty"` + Addresses *[]GetAddedNodeInfoResultAddr `json:"addresses,omitempty"` +} + +// GetBlockChainInfoResult models the data returned from the getblockchaininfo +// command. +type GetBlockChainInfoResult struct { + Chain string `json:"chain"` + Blocks int32 `json:"blocks"` + Headers int32 `json:"headers"` + BestBlockHash string `json:"bestblockhash"` + Difficulty float64 `json:"difficulty"` + VerificationProgress float64 `json:"verificationprogress"` + ChainWork string `json:"chainwork"` +} + +// GetBlockTemplateResultTx models the transactions field of the +// getblocktemplate command. +type GetBlockTemplateResultTx struct { + Data string `json:"data"` + Hash string `json:"hash"` + Depends []int64 `json:"depends"` + Fee int64 `json:"fee"` + SigOps int64 `json:"sigops"` +} + +// GetBlockTemplateResultAux models the coinbaseaux field of the +// getblocktemplate command. +type GetBlockTemplateResultAux struct { + Flags string `json:"flags"` +} + +// GetBlockTemplateResult models the data returned from the getblocktemplate +// command. +type GetBlockTemplateResult struct { + // Base fields from BIP 0022. CoinbaseAux is optional. One of + // CoinbaseTxn or CoinbaseValue must be specified, but not both. + Bits string `json:"bits"` + CurTime int64 `json:"curtime"` + Height int64 `json:"height"` + PreviousHash string `json:"previousblockhash"` + SigOpLimit int64 `json:"sigoplimit,omitempty"` + SizeLimit int64 `json:"sizelimit,omitempty"` + Transactions []GetBlockTemplateResultTx `json:"transactions"` + Version int32 `json:"version"` + CoinbaseAux *GetBlockTemplateResultAux `json:"coinbaseaux,omitempty"` + CoinbaseTxn *GetBlockTemplateResultTx `json:"coinbasetxn,omitempty"` + CoinbaseValue *int64 `json:"coinbasevalue,omitempty"` + WorkID string `json:"workid,omitempty"` + + // Optional long polling from BIP 0022. + LongPollID string `json:"longpollid,omitempty"` + LongPollURI string `json:"longpolluri,omitempty"` + SubmitOld *bool `json:"submitold,omitempty"` + + // Basic pool extension from BIP 0023. + Target string `json:"target,omitempty"` + Expires int64 `json:"expires,omitempty"` + + // Mutations from BIP 0023. + MaxTime int64 `json:"maxtime,omitempty"` + MinTime int64 `json:"mintime,omitempty"` + Mutable []string `json:"mutable,omitempty"` + NonceRange string `json:"noncerange,omitempty"` + + // Block proposal from BIP 0023. + Capabilities []string `json:"capabilities,omitempty"` + RejectReasion string `json:"reject-reason,omitempty"` +} + +// GetMempoolInfoResult models the data returned from the getmempoolinfo +// command. +type GetMempoolInfoResult struct { + Size int64 `json:"size"` + Bytes int64 `json:"bytes"` +} + +// GetNetworkInfoResult models the data returned from the getnetworkinfo +// command. +type GetNetworkInfoResult struct { + Version int32 `json:"version"` + ProtocolVersion int32 `json:"protocolversion"` + TimeOffset int64 `json:"timeoffset"` + Connections int32 `json:"connections"` + Networks []NetworksResult `json:"networks"` + RelayFee float64 `json:"relayfee"` + LocalAddresses []LocalAddressesResult `json:"localaddresses"` +} + +// GetPeerInfoResult models the data returned from the getpeerinfo command. +type GetPeerInfoResult struct { + ID int32 `json:"id"` + Addr string `json:"addr"` + AddrLocal string `json:"addrlocal,omitempty"` + Services string `json:"services"` + LastSend int64 `json:"lastsend"` + LastRecv int64 `json:"lastrecv"` + BytesSent uint64 `json:"bytessent"` + BytesRecv uint64 `json:"bytesrecv"` + ConnTime int64 `json:"conntime"` + TimeOffset int64 `json:"timeoffset"` + PingTime float64 `json:"pingtime"` + PingWait float64 `json:"pingwait,omitempty"` + Version uint32 `json:"version"` + SubVer string `json:"subver"` + Inbound bool `json:"inbound"` + StartingHeight int32 `json:"startingheight"` + CurrentHeight int32 `json:"currentheight,omitempty"` + BanScore int32 `json:"banscore"` + SyncNode bool `json:"syncnode"` +} + +// GetRawMempoolVerboseResult models the data returned from the getrawmempool +// command when the verbose flag is set. When the verbose flag is not set, +// getrawmempool returns an array of transaction hashes. +type GetRawMempoolVerboseResult struct { + Size int32 `json:"size"` + Fee float64 `json:"fee"` + Time int64 `json:"time"` + Height int64 `json:"height"` + StartingPriority float64 `json:"startingpriority"` + CurrentPriority float64 `json:"currentpriority"` + Depends []string `json:"depends"` +} + +// ScriptPubKeyResult models the scriptPubKey data of a tx script. It is +// defined separately since it is used by multiple commands. +type ScriptPubKeyResult struct { + Asm string `json:"asm"` + Hex string `json:"hex,omitempty"` + ReqSigs int32 `json:"reqSigs,omitempty"` + Type string `json:"type"` + Addresses []string `json:"addresses,omitempty"` +} + +// GetTxOutResult models the data from the gettxout command. +type GetTxOutResult struct { + BestBlock string `json:"bestblock"` + Confirmations int64 `json:"confirmations"` + Value float64 `json:"value"` + ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"` + Version int32 `json:"version"` + Coinbase bool `json:"coinbase"` +} + +// GetNetTotalsResult models the data returned from the getnettotals command. +type GetNetTotalsResult struct { + TotalBytesRecv uint64 `json:"totalbytesrecv"` + TotalBytesSent uint64 `json:"totalbytessent"` + TimeMillis int64 `json:"timemillis"` +} + +// ScriptSig models a signature script. It is defined separately since it only +// applies to non-coinbase. Therefore the field in the Vin structure needs +// to be a pointer. +type ScriptSig struct { + Asm string `json:"asm"` + Hex string `json:"hex"` +} + +// Vin models parts of the tx data. It is defined separately since +// getrawtransaction, decoderawtransaction, and searchrawtransaction use the +// same structure. +type Vin struct { + Coinbase string `json:"coinbase"` + Txid string `json:"txid"` + Vout uint32 `json:"vout"` + ScriptSig *ScriptSig `json:"scriptSig"` + Sequence uint32 `json:"sequence"` +} + +// IsCoinBase returns a bool to show if a Vin is a Coinbase one or not. +func (v *Vin) IsCoinBase() bool { + return len(v.Coinbase) > 0 +} + +// MarshalJSON provides a custom Marshal method for Vin. +func (v *Vin) MarshalJSON() ([]byte, error) { + if v.IsCoinBase() { + coinbaseStruct := struct { + Coinbase string `json:"coinbase"` + Sequence uint32 `json:"sequence"` + }{ + Coinbase: v.Coinbase, + Sequence: v.Sequence, + } + return json.Marshal(coinbaseStruct) + } + + txStruct := struct { + Txid string `json:"txid"` + Vout uint32 `json:"vout"` + ScriptSig *ScriptSig `json:"scriptSig"` + Sequence uint32 `json:"sequence"` + }{ + Txid: v.Txid, + Vout: v.Vout, + ScriptSig: v.ScriptSig, + Sequence: v.Sequence, + } + return json.Marshal(txStruct) +} + +// PrevOut represents previous output for an input Vin. +type PrevOut struct { + Addresses []string `json:"addresses,omitempty"` + Value float64 `json:"value"` +} + +// VinPrevOut is like Vin except it includes PrevOut. It is used by searchrawtransaction +type VinPrevOut struct { + Coinbase string `json:"coinbase"` + Txid string `json:"txid"` + Vout uint32 `json:"vout"` + ScriptSig *ScriptSig `json:"scriptSig"` + PrevOut *PrevOut `json:"prevOut"` + Sequence uint32 `json:"sequence"` +} + +// IsCoinBase returns a bool to show if a Vin is a Coinbase one or not. +func (v *VinPrevOut) IsCoinBase() bool { + return len(v.Coinbase) > 0 +} + +// MarshalJSON provides a custom Marshal method for VinPrevOut. +func (v *VinPrevOut) MarshalJSON() ([]byte, error) { + if v.IsCoinBase() { + coinbaseStruct := struct { + Coinbase string `json:"coinbase"` + Sequence uint32 `json:"sequence"` + }{ + Coinbase: v.Coinbase, + Sequence: v.Sequence, + } + return json.Marshal(coinbaseStruct) + } + + txStruct := struct { + Txid string `json:"txid"` + Vout uint32 `json:"vout"` + ScriptSig *ScriptSig `json:"scriptSig"` + PrevOut *PrevOut `json:"prevOut,omitempty"` + Sequence uint32 `json:"sequence"` + }{ + Txid: v.Txid, + Vout: v.Vout, + ScriptSig: v.ScriptSig, + PrevOut: v.PrevOut, + Sequence: v.Sequence, + } + return json.Marshal(txStruct) +} + +// Vout models parts of the tx data. It is defined separately since both +// getrawtransaction and decoderawtransaction use the same structure. +type Vout struct { + Value float64 `json:"value"` + N uint32 `json:"n"` + ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"` +} + +// GetMiningInfoResult models the data from the getmininginfo command. +type GetMiningInfoResult struct { + Blocks int64 `json:"blocks"` + CurrentBlockSize uint64 `json:"currentblocksize"` + CurrentBlockTx uint64 `json:"currentblocktx"` + Difficulty float64 `json:"difficulty"` + Errors string `json:"errors"` + Generate bool `json:"generate"` + GenProcLimit int32 `json:"genproclimit"` + HashesPerSec int64 `json:"hashespersec"` + NetworkHashPS int64 `json:"networkhashps"` + PooledTx uint64 `json:"pooledtx"` + TestNet bool `json:"testnet"` +} + +// GetWorkResult models the data from the getwork command. +type GetWorkResult struct { + Data string `json:"data"` + Hash1 string `json:"hash1"` + Midstate string `json:"midstate"` + Target string `json:"target"` +} + +// InfoChainResult models the data returned by the chain server getinfo command. +type InfoChainResult struct { + Version int32 `json:"version"` + ProtocolVersion int32 `json:"protocolversion"` + Blocks int32 `json:"blocks"` + TimeOffset int64 `json:"timeoffset"` + Connections int32 `json:"connections"` + Proxy string `json:"proxy"` + Difficulty float64 `json:"difficulty"` + TestNet bool `json:"testnet"` + RelayFee float64 `json:"relayfee"` + Errors string `json:"errors"` +} + +// LocalAddressesResult models the localaddresses data from the getnetworkinfo +// command. +type LocalAddressesResult struct { + Address string `json:"address"` + Port uint16 `json:"port"` + Score int32 `json:"score"` +} + +// NetworksResult models the networks data from the getnetworkinfo command. +type NetworksResult struct { + Name string `json:"name"` + Limited bool `json:"limited"` + Reachable bool `json:"reachable"` + Proxy string `json:"proxy"` +} + +// TxRawResult models the data from the getrawtransaction command. +type TxRawResult struct { + Hex string `json:"hex"` + Txid string `json:"txid"` + Version int32 `json:"version"` + LockTime uint32 `json:"locktime"` + Vin []Vin `json:"vin"` + Vout []Vout `json:"vout"` + BlockHash string `json:"blockhash,omitempty"` + Confirmations uint64 `json:"confirmations,omitempty"` + Time int64 `json:"time,omitempty"` + Blocktime int64 `json:"blocktime,omitempty"` +} + +// SearchRawTransactionsResult models the data from the searchrawtransaction +// command. +type SearchRawTransactionsResult struct { + Hex string `json:"hex,omitempty"` + Txid string `json:"txid"` + Version int32 `json:"version"` + LockTime uint32 `json:"locktime"` + Vin []VinPrevOut `json:"vin"` + Vout []Vout `json:"vout"` + BlockHash string `json:"blockhash,omitempty"` + Confirmations uint64 `json:"confirmations,omitempty"` + Time int64 `json:"time,omitempty"` + Blocktime int64 `json:"blocktime,omitempty"` +} + +// TxRawDecodeResult models the data from the decoderawtransaction command. +type TxRawDecodeResult struct { + Txid string `json:"txid"` + Version int32 `json:"version"` + Locktime uint32 `json:"locktime"` + Vin []Vin `json:"vin"` + Vout []Vout `json:"vout"` +} + +// ValidateAddressChainResult models the data returned by the chain server +// validateaddress command. +type ValidateAddressChainResult struct { + IsValid bool `json:"isvalid"` + Address string `json:"address,omitempty"` +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/chainsvrresults_test.go b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrresults_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1d568e2658ac0bb8a949f72523545bea863f28f8 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrresults_test.go @@ -0,0 +1,88 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "encoding/json" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestChainSvrCustomResults ensures any results that have custom marshalling +// work as inteded. +// and unmarshal code of results are as expected. +func TestChainSvrCustomResults(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + result interface{} + expected string + }{ + { + name: "custom vin marshal with coinbase", + result: &btcjson.Vin{ + Coinbase: "021234", + Sequence: 4294967295, + }, + expected: `{"coinbase":"021234","sequence":4294967295}`, + }, + { + name: "custom vin marshal without coinbase", + result: &btcjson.Vin{ + Txid: "123", + Vout: 1, + ScriptSig: &btcjson.ScriptSig{ + Asm: "0", + Hex: "00", + }, + Sequence: 4294967295, + }, + expected: `{"txid":"123","vout":1,"scriptSig":{"asm":"0","hex":"00"},"sequence":4294967295}`, + }, + { + name: "custom vinprevout marshal with coinbase", + result: &btcjson.VinPrevOut{ + Coinbase: "021234", + Sequence: 4294967295, + }, + expected: `{"coinbase":"021234","sequence":4294967295}`, + }, + { + name: "custom vinprevout marshal without coinbase", + result: &btcjson.VinPrevOut{ + Txid: "123", + Vout: 1, + ScriptSig: &btcjson.ScriptSig{ + Asm: "0", + Hex: "00", + }, + PrevOut: &btcjson.PrevOut{ + Addresses: []string{"addr1"}, + Value: 0, + }, + Sequence: 4294967295, + }, + expected: `{"txid":"123","vout":1,"scriptSig":{"asm":"0","hex":"00"},"prevOut":{"addresses":["addr1"],"value":0},"sequence":4294967295}`, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + marshalled, err := json.Marshal(test.result) + if err != nil { + t.Errorf("Test #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + if string(marshalled) != test.expected { + t.Errorf("Test #%d (%s) unexpected marhsalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.expected) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwscmds.go b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwscmds.go new file mode 100644 index 0000000000000000000000000000000000000000..007dd83bd2d15c230fa5da817d62233115bc1e43 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwscmds.go @@ -0,0 +1,176 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// NOTE: This file is intended to house the RPC commands that are supported by +// a chain server, but are only available via websockets. + +package btcjson + +// AuthenticateCmd defines the authenticate JSON-RPC command. +type AuthenticateCmd struct { + Username string + Passphrase string +} + +// NewAuthenticateCmd returns a new instance which can be used to issue an +// authenticate JSON-RPC command. +func NewAuthenticateCmd(username, passphrase string) *AuthenticateCmd { + return &AuthenticateCmd{ + Username: username, + Passphrase: passphrase, + } +} + +// NotifyBlocksCmd defines the notifyblocks JSON-RPC command. +type NotifyBlocksCmd struct{} + +// NewNotifyBlocksCmd returns a new instance which can be used to issue a +// notifyblocks JSON-RPC command. +func NewNotifyBlocksCmd() *NotifyBlocksCmd { + return &NotifyBlocksCmd{} +} + +// StopNotifyBlocksCmd defines the stopnotifyblocks JSON-RPC command. +type StopNotifyBlocksCmd struct{} + +// NewStopNotifyBlocksCmd returns a new instance which can be used to issue a +// stopnotifyblocks JSON-RPC command. +func NewStopNotifyBlocksCmd() *StopNotifyBlocksCmd { + return &StopNotifyBlocksCmd{} +} + +// NotifyNewTransactionsCmd defines the notifynewtransactions JSON-RPC command. +type NotifyNewTransactionsCmd struct { + Verbose *bool `jsonrpcdefault:"false"` +} + +// NewNotifyNewTransactionsCmd returns a new instance which can be used to issue +// a notifynewtransactions JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewNotifyNewTransactionsCmd(verbose *bool) *NotifyNewTransactionsCmd { + return &NotifyNewTransactionsCmd{ + Verbose: verbose, + } +} + +// SessionCmd defines the session JSON-RPC command. +type SessionCmd struct{} + +// NewSessionCmd returns a new instance which can be used to issue a session +// JSON-RPC command. +func NewSessionCmd() *SessionCmd { + return &SessionCmd{} +} + +// StopNotifyNewTransactionsCmd defines the stopnotifynewtransactions JSON-RPC command. +type StopNotifyNewTransactionsCmd struct{} + +// NewStopNotifyNewTransactionsCmd returns a new instance which can be used to issue +// a stopnotifynewtransactions JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewStopNotifyNewTransactionsCmd() *StopNotifyNewTransactionsCmd { + return &StopNotifyNewTransactionsCmd{} +} + +// NotifyReceivedCmd defines the notifyreceived JSON-RPC command. +type NotifyReceivedCmd struct { + Addresses []string +} + +// NewNotifyReceivedCmd returns a new instance which can be used to issue a +// notifyreceived JSON-RPC command. +func NewNotifyReceivedCmd(addresses []string) *NotifyReceivedCmd { + return &NotifyReceivedCmd{ + Addresses: addresses, + } +} + +// OutPoint describes a transaction outpoint that will be marshalled to and +// from JSON. +type OutPoint struct { + Hash string `json:"hash"` + Index uint32 `json:"index"` +} + +// NotifySpentCmd defines the notifyspent JSON-RPC command. +type NotifySpentCmd struct { + OutPoints []OutPoint +} + +// NewNotifySpentCmd returns a new instance which can be used to issue a +// notifyspent JSON-RPC command. +func NewNotifySpentCmd(outPoints []OutPoint) *NotifySpentCmd { + return &NotifySpentCmd{ + OutPoints: outPoints, + } +} + +// StopNotifyReceivedCmd defines the stopnotifyreceived JSON-RPC command. +type StopNotifyReceivedCmd struct { + Addresses []string +} + +// NewStopNotifyReceivedCmd returns a new instance which can be used to issue a +// stopnotifyreceived JSON-RPC command. +func NewStopNotifyReceivedCmd(addresses []string) *StopNotifyReceivedCmd { + return &StopNotifyReceivedCmd{ + Addresses: addresses, + } +} + +// StopNotifySpentCmd defines the stopnotifyspent JSON-RPC command. +type StopNotifySpentCmd struct { + OutPoints []OutPoint +} + +// NewStopNotifySpentCmd returns a new instance which can be used to issue a +// stopnotifyspent JSON-RPC command. +func NewStopNotifySpentCmd(outPoints []OutPoint) *StopNotifySpentCmd { + return &StopNotifySpentCmd{ + OutPoints: outPoints, + } +} + +// RescanCmd defines the rescan JSON-RPC command. +type RescanCmd struct { + BeginBlock string + Addresses []string + OutPoints []OutPoint + EndBlock *string +} + +// NewRescanCmd returns a new instance which can be used to issue a rescan +// JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewRescanCmd(beginBlock string, addresses []string, outPoints []OutPoint, endBlock *string) *RescanCmd { + return &RescanCmd{ + BeginBlock: beginBlock, + Addresses: addresses, + OutPoints: outPoints, + EndBlock: endBlock, + } +} + +func init() { + // The commands in this file are only usable by websockets. + flags := UFWebsocketOnly + + MustRegisterCmd("authenticate", (*AuthenticateCmd)(nil), flags) + MustRegisterCmd("notifyblocks", (*NotifyBlocksCmd)(nil), flags) + MustRegisterCmd("notifynewtransactions", (*NotifyNewTransactionsCmd)(nil), flags) + MustRegisterCmd("notifyreceived", (*NotifyReceivedCmd)(nil), flags) + MustRegisterCmd("notifyspent", (*NotifySpentCmd)(nil), flags) + MustRegisterCmd("session", (*SessionCmd)(nil), flags) + MustRegisterCmd("stopnotifyblocks", (*StopNotifyBlocksCmd)(nil), flags) + MustRegisterCmd("stopnotifynewtransactions", (*StopNotifyNewTransactionsCmd)(nil), flags) + MustRegisterCmd("stopnotifyspent", (*StopNotifySpentCmd)(nil), flags) + MustRegisterCmd("stopnotifyreceived", (*StopNotifyReceivedCmd)(nil), flags) + MustRegisterCmd("rescan", (*RescanCmd)(nil), flags) +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwscmds_test.go b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwscmds_test.go new file mode 100644 index 0000000000000000000000000000000000000000..27d73c2bf60372dca317a99a35f9daba899de02e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwscmds_test.go @@ -0,0 +1,262 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestChainSvrWsCmds tests all of the chain server websocket-specific commands +// marshal and unmarshal into valid results include handling of optional fields +// being omitted in the marshalled command, while optional fields with defaults +// have the default assigned on unmarshalled commands. +func TestChainSvrWsCmds(t *testing.T) { + t.Parallel() + + testID := int(1) + tests := []struct { + name string + newCmd func() (interface{}, error) + staticCmd func() interface{} + marshalled string + unmarshalled interface{} + }{ + { + name: "authenticate", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("authenticate", "user", "pass") + }, + staticCmd: func() interface{} { + return btcjson.NewAuthenticateCmd("user", "pass") + }, + marshalled: `{"jsonrpc":"1.0","method":"authenticate","params":["user","pass"],"id":1}`, + unmarshalled: &btcjson.AuthenticateCmd{Username: "user", Passphrase: "pass"}, + }, + { + name: "notifyblocks", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("notifyblocks") + }, + staticCmd: func() interface{} { + return btcjson.NewNotifyBlocksCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"notifyblocks","params":[],"id":1}`, + unmarshalled: &btcjson.NotifyBlocksCmd{}, + }, + { + name: "stopnotifyblocks", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("stopnotifyblocks") + }, + staticCmd: func() interface{} { + return btcjson.NewStopNotifyBlocksCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"stopnotifyblocks","params":[],"id":1}`, + unmarshalled: &btcjson.StopNotifyBlocksCmd{}, + }, + { + name: "notifynewtransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("notifynewtransactions") + }, + staticCmd: func() interface{} { + return btcjson.NewNotifyNewTransactionsCmd(nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"notifynewtransactions","params":[],"id":1}`, + unmarshalled: &btcjson.NotifyNewTransactionsCmd{ + Verbose: btcjson.Bool(false), + }, + }, + { + name: "notifynewtransactions optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("notifynewtransactions", true) + }, + staticCmd: func() interface{} { + return btcjson.NewNotifyNewTransactionsCmd(btcjson.Bool(true)) + }, + marshalled: `{"jsonrpc":"1.0","method":"notifynewtransactions","params":[true],"id":1}`, + unmarshalled: &btcjson.NotifyNewTransactionsCmd{ + Verbose: btcjson.Bool(true), + }, + }, + { + name: "stopnotifynewtransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("stopnotifynewtransactions") + }, + staticCmd: func() interface{} { + return btcjson.NewStopNotifyNewTransactionsCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"stopnotifynewtransactions","params":[],"id":1}`, + unmarshalled: &btcjson.StopNotifyNewTransactionsCmd{}, + }, + { + name: "notifyreceived", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("notifyreceived", []string{"1Address"}) + }, + staticCmd: func() interface{} { + return btcjson.NewNotifyReceivedCmd([]string{"1Address"}) + }, + marshalled: `{"jsonrpc":"1.0","method":"notifyreceived","params":[["1Address"]],"id":1}`, + unmarshalled: &btcjson.NotifyReceivedCmd{ + Addresses: []string{"1Address"}, + }, + }, + { + name: "stopnotifyreceived", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("stopnotifyreceived", []string{"1Address"}) + }, + staticCmd: func() interface{} { + return btcjson.NewStopNotifyReceivedCmd([]string{"1Address"}) + }, + marshalled: `{"jsonrpc":"1.0","method":"stopnotifyreceived","params":[["1Address"]],"id":1}`, + unmarshalled: &btcjson.StopNotifyReceivedCmd{ + Addresses: []string{"1Address"}, + }, + }, + { + name: "notifyspent", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("notifyspent", `[{"hash":"123","index":0}]`) + }, + staticCmd: func() interface{} { + ops := []btcjson.OutPoint{{Hash: "123", Index: 0}} + return btcjson.NewNotifySpentCmd(ops) + }, + marshalled: `{"jsonrpc":"1.0","method":"notifyspent","params":[[{"hash":"123","index":0}]],"id":1}`, + unmarshalled: &btcjson.NotifySpentCmd{ + OutPoints: []btcjson.OutPoint{{Hash: "123", Index: 0}}, + }, + }, + { + name: "stopnotifyspent", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("stopnotifyspent", `[{"hash":"123","index":0}]`) + }, + staticCmd: func() interface{} { + ops := []btcjson.OutPoint{{Hash: "123", Index: 0}} + return btcjson.NewStopNotifySpentCmd(ops) + }, + marshalled: `{"jsonrpc":"1.0","method":"stopnotifyspent","params":[[{"hash":"123","index":0}]],"id":1}`, + unmarshalled: &btcjson.StopNotifySpentCmd{ + OutPoints: []btcjson.OutPoint{{Hash: "123", Index: 0}}, + }, + }, + { + name: "rescan", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("rescan", "123", `["1Address"]`, `[{"hash":"0000000000000000000000000000000000000000000000000000000000000123","index":0}]`) + }, + staticCmd: func() interface{} { + addrs := []string{"1Address"} + ops := []btcjson.OutPoint{{ + Hash: "0000000000000000000000000000000000000000000000000000000000000123", + Index: 0, + }} + return btcjson.NewRescanCmd("123", addrs, ops, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"rescan","params":["123",["1Address"],[{"hash":"0000000000000000000000000000000000000000000000000000000000000123","index":0}]],"id":1}`, + unmarshalled: &btcjson.RescanCmd{ + BeginBlock: "123", + Addresses: []string{"1Address"}, + OutPoints: []btcjson.OutPoint{{Hash: "0000000000000000000000000000000000000000000000000000000000000123", Index: 0}}, + EndBlock: nil, + }, + }, + { + name: "rescan optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("rescan", "123", `["1Address"]`, `[{"hash":"123","index":0}]`, "456") + }, + staticCmd: func() interface{} { + addrs := []string{"1Address"} + ops := []btcjson.OutPoint{{Hash: "123", Index: 0}} + return btcjson.NewRescanCmd("123", addrs, ops, btcjson.String("456")) + }, + marshalled: `{"jsonrpc":"1.0","method":"rescan","params":["123",["1Address"],[{"hash":"123","index":0}],"456"],"id":1}`, + unmarshalled: &btcjson.RescanCmd{ + BeginBlock: "123", + Addresses: []string{"1Address"}, + OutPoints: []btcjson.OutPoint{{Hash: "123", Index: 0}}, + EndBlock: btcjson.String("456"), + }, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Marshal the command as created by the new static command + // creation function. + marshalled, err := btcjson.MarshalCmd(testID, test.staticCmd()) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + // Ensure the command is created without error via the generic + // new command creation function. + cmd, err := test.newCmd() + if err != nil { + t.Errorf("Test #%d (%s) unexpected NewCmd error: %v ", + i, test.name, err) + } + + // Marshal the command as created by the generic new command + // creation function. + marshalled, err = btcjson.MarshalCmd(testID, cmd) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + var request btcjson.Request + if err := json.Unmarshal(marshalled, &request); err != nil { + t.Errorf("Test #%d (%s) unexpected error while "+ + "unmarshalling JSON-RPC request: %v", i, + test.name, err) + continue + } + + cmd, err = btcjson.UnmarshalCmd(&request) + if err != nil { + t.Errorf("UnmarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !reflect.DeepEqual(cmd, test.unmarshalled) { + t.Errorf("Test #%d (%s) unexpected unmarshalled command "+ + "- got %s, want %s", i, test.name, + fmt.Sprintf("(%T) %+[1]v", cmd), + fmt.Sprintf("(%T) %+[1]v\n", test.unmarshalled)) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwsntfns.go b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwsntfns.go new file mode 100644 index 0000000000000000000000000000000000000000..c7e9b5aaff3f5d54453326efd6389107e93cb489 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwsntfns.go @@ -0,0 +1,196 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// NOTE: This file is intended to house the RPC websocket notifications that are +// supported by a chain server. + +package btcjson + +const ( + // BlockConnectedNtfnMethod is the method used for notifications from + // the chain server that a block has been connected. + BlockConnectedNtfnMethod = "blockconnected" + + // BlockDisconnectedNtfnMethod is the method used for notifications from + // the chain server that a block has been disconnected. + BlockDisconnectedNtfnMethod = "blockdisconnected" + + // RecvTxNtfnMethod is the method used for notifications from the chain + // server that a transaction which pays to a registered address has been + // processed. + RecvTxNtfnMethod = "recvtx" + + // RedeemingTxNtfnMethod is the method used for notifications from the + // chain server that a transaction which spends a registered outpoint + // has been processed. + RedeemingTxNtfnMethod = "redeemingtx" + + // RescanFinishedNtfnMethod is the method used for notifications from + // the chain server that a rescan operation has finished. + RescanFinishedNtfnMethod = "rescanfinished" + + // RescanProgressNtfnMethod is the method used for notifications from + // the chain server that a rescan operation this is underway has made + // progress. + RescanProgressNtfnMethod = "rescanprogress" + + // TxAcceptedNtfnMethod is the method used for notifications from the + // chain server that a transaction has been accepted into the mempool. + TxAcceptedNtfnMethod = "txaccepted" + + // TxAcceptedVerboseNtfnMethod is the method used for notifications from + // the chain server that a transaction has been accepted into the + // mempool. This differs from TxAcceptedNtfnMethod in that it provides + // more details in the notification. + TxAcceptedVerboseNtfnMethod = "txacceptedverbose" +) + +// BlockConnectedNtfn defines the blockconnected JSON-RPC notification. +type BlockConnectedNtfn struct { + Hash string + Height int32 + Time int64 +} + +// NewBlockConnectedNtfn returns a new instance which can be used to issue a +// blockconnected JSON-RPC notification. +func NewBlockConnectedNtfn(hash string, height int32, time int64) *BlockConnectedNtfn { + return &BlockConnectedNtfn{ + Hash: hash, + Height: height, + Time: time, + } +} + +// BlockDisconnectedNtfn defines the blockdisconnected JSON-RPC notification. +type BlockDisconnectedNtfn struct { + Hash string + Height int32 + Time int64 +} + +// NewBlockDisconnectedNtfn returns a new instance which can be used to issue a +// blockdisconnected JSON-RPC notification. +func NewBlockDisconnectedNtfn(hash string, height int32, time int64) *BlockDisconnectedNtfn { + return &BlockDisconnectedNtfn{ + Hash: hash, + Height: height, + Time: time, + } +} + +// BlockDetails describes details of a tx in a block. +type BlockDetails struct { + Height int32 `json:"height"` + Hash string `json:"hash"` + Index int `json:"index"` + Time int64 `json:"time"` +} + +// RecvTxNtfn defines the recvtx JSON-RPC notification. +type RecvTxNtfn struct { + HexTx string + Block *BlockDetails +} + +// NewRecvTxNtfn returns a new instance which can be used to issue a recvtx +// JSON-RPC notification. +func NewRecvTxNtfn(hexTx string, block *BlockDetails) *RecvTxNtfn { + return &RecvTxNtfn{ + HexTx: hexTx, + Block: block, + } +} + +// RedeemingTxNtfn defines the redeemingtx JSON-RPC notification. +type RedeemingTxNtfn struct { + HexTx string + Block *BlockDetails +} + +// NewRedeemingTxNtfn returns a new instance which can be used to issue a +// redeemingtx JSON-RPC notification. +func NewRedeemingTxNtfn(hexTx string, block *BlockDetails) *RedeemingTxNtfn { + return &RedeemingTxNtfn{ + HexTx: hexTx, + Block: block, + } +} + +// RescanFinishedNtfn defines the rescanfinished JSON-RPC notification. +type RescanFinishedNtfn struct { + Hash string + Height int32 + Time int64 +} + +// NewRescanFinishedNtfn returns a new instance which can be used to issue a +// rescanfinished JSON-RPC notification. +func NewRescanFinishedNtfn(hash string, height int32, time int64) *RescanFinishedNtfn { + return &RescanFinishedNtfn{ + Hash: hash, + Height: height, + Time: time, + } +} + +// RescanProgressNtfn defines the rescanprogress JSON-RPC notification. +type RescanProgressNtfn struct { + Hash string + Height int32 + Time int64 +} + +// NewRescanProgressNtfn returns a new instance which can be used to issue a +// rescanprogress JSON-RPC notification. +func NewRescanProgressNtfn(hash string, height int32, time int64) *RescanProgressNtfn { + return &RescanProgressNtfn{ + Hash: hash, + Height: height, + Time: time, + } +} + +// TxAcceptedNtfn defines the txaccepted JSON-RPC notification. +type TxAcceptedNtfn struct { + TxID string + Amount float64 +} + +// NewTxAcceptedNtfn returns a new instance which can be used to issue a +// txaccepted JSON-RPC notification. +func NewTxAcceptedNtfn(txHash string, amount float64) *TxAcceptedNtfn { + return &TxAcceptedNtfn{ + TxID: txHash, + Amount: amount, + } +} + +// TxAcceptedVerboseNtfn defines the txacceptedverbose JSON-RPC notification. +type TxAcceptedVerboseNtfn struct { + RawTx TxRawResult +} + +// NewTxAcceptedVerboseNtfn returns a new instance which can be used to issue a +// txacceptedverbose JSON-RPC notification. +func NewTxAcceptedVerboseNtfn(rawTx TxRawResult) *TxAcceptedVerboseNtfn { + return &TxAcceptedVerboseNtfn{ + RawTx: rawTx, + } +} + +func init() { + // The commands in this file are only usable by websockets and are + // notifications. + flags := UFWebsocketOnly | UFNotification + + MustRegisterCmd(BlockConnectedNtfnMethod, (*BlockConnectedNtfn)(nil), flags) + MustRegisterCmd(BlockDisconnectedNtfnMethod, (*BlockDisconnectedNtfn)(nil), flags) + MustRegisterCmd(RecvTxNtfnMethod, (*RecvTxNtfn)(nil), flags) + MustRegisterCmd(RedeemingTxNtfnMethod, (*RedeemingTxNtfn)(nil), flags) + MustRegisterCmd(RescanFinishedNtfnMethod, (*RescanFinishedNtfn)(nil), flags) + MustRegisterCmd(RescanProgressNtfnMethod, (*RescanProgressNtfn)(nil), flags) + MustRegisterCmd(TxAcceptedNtfnMethod, (*TxAcceptedNtfn)(nil), flags) + MustRegisterCmd(TxAcceptedVerboseNtfnMethod, (*TxAcceptedVerboseNtfn)(nil), flags) +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwsntfns_test.go b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwsntfns_test.go new file mode 100644 index 0000000000000000000000000000000000000000..70c0241ac21ba6ecad3a7a1f5d886007832d6c4b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwsntfns_test.go @@ -0,0 +1,253 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestChainSvrWsNtfns tests all of the chain server websocket-specific +// notifications marshal and unmarshal into valid results include handling of +// optional fields being omitted in the marshalled command, while optional +// fields with defaults have the default assigned on unmarshalled commands. +func TestChainSvrWsNtfns(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + newNtfn func() (interface{}, error) + staticNtfn func() interface{} + marshalled string + unmarshalled interface{} + }{ + { + name: "blockconnected", + newNtfn: func() (interface{}, error) { + return btcjson.NewCmd("blockconnected", "123", 100000, 123456789) + }, + staticNtfn: func() interface{} { + return btcjson.NewBlockConnectedNtfn("123", 100000, 123456789) + }, + marshalled: `{"jsonrpc":"1.0","method":"blockconnected","params":["123",100000,123456789],"id":null}`, + unmarshalled: &btcjson.BlockConnectedNtfn{ + Hash: "123", + Height: 100000, + Time: 123456789, + }, + }, + { + name: "blockdisconnected", + newNtfn: func() (interface{}, error) { + return btcjson.NewCmd("blockdisconnected", "123", 100000, 123456789) + }, + staticNtfn: func() interface{} { + return btcjson.NewBlockDisconnectedNtfn("123", 100000, 123456789) + }, + marshalled: `{"jsonrpc":"1.0","method":"blockdisconnected","params":["123",100000,123456789],"id":null}`, + unmarshalled: &btcjson.BlockDisconnectedNtfn{ + Hash: "123", + Height: 100000, + Time: 123456789, + }, + }, + { + name: "recvtx", + newNtfn: func() (interface{}, error) { + return btcjson.NewCmd("recvtx", "001122", `{"height":100000,"hash":"123","index":0,"time":12345678}`) + }, + staticNtfn: func() interface{} { + blockDetails := btcjson.BlockDetails{ + Height: 100000, + Hash: "123", + Index: 0, + Time: 12345678, + } + return btcjson.NewRecvTxNtfn("001122", &blockDetails) + }, + marshalled: `{"jsonrpc":"1.0","method":"recvtx","params":["001122",{"height":100000,"hash":"123","index":0,"time":12345678}],"id":null}`, + unmarshalled: &btcjson.RecvTxNtfn{ + HexTx: "001122", + Block: &btcjson.BlockDetails{ + Height: 100000, + Hash: "123", + Index: 0, + Time: 12345678, + }, + }, + }, + { + name: "redeemingtx", + newNtfn: func() (interface{}, error) { + return btcjson.NewCmd("redeemingtx", "001122", `{"height":100000,"hash":"123","index":0,"time":12345678}`) + }, + staticNtfn: func() interface{} { + blockDetails := btcjson.BlockDetails{ + Height: 100000, + Hash: "123", + Index: 0, + Time: 12345678, + } + return btcjson.NewRedeemingTxNtfn("001122", &blockDetails) + }, + marshalled: `{"jsonrpc":"1.0","method":"redeemingtx","params":["001122",{"height":100000,"hash":"123","index":0,"time":12345678}],"id":null}`, + unmarshalled: &btcjson.RedeemingTxNtfn{ + HexTx: "001122", + Block: &btcjson.BlockDetails{ + Height: 100000, + Hash: "123", + Index: 0, + Time: 12345678, + }, + }, + }, + { + name: "rescanfinished", + newNtfn: func() (interface{}, error) { + return btcjson.NewCmd("rescanfinished", "123", 100000, 12345678) + }, + staticNtfn: func() interface{} { + return btcjson.NewRescanFinishedNtfn("123", 100000, 12345678) + }, + marshalled: `{"jsonrpc":"1.0","method":"rescanfinished","params":["123",100000,12345678],"id":null}`, + unmarshalled: &btcjson.RescanFinishedNtfn{ + Hash: "123", + Height: 100000, + Time: 12345678, + }, + }, + { + name: "rescanprogress", + newNtfn: func() (interface{}, error) { + return btcjson.NewCmd("rescanprogress", "123", 100000, 12345678) + }, + staticNtfn: func() interface{} { + return btcjson.NewRescanProgressNtfn("123", 100000, 12345678) + }, + marshalled: `{"jsonrpc":"1.0","method":"rescanprogress","params":["123",100000,12345678],"id":null}`, + unmarshalled: &btcjson.RescanProgressNtfn{ + Hash: "123", + Height: 100000, + Time: 12345678, + }, + }, + { + name: "txaccepted", + newNtfn: func() (interface{}, error) { + return btcjson.NewCmd("txaccepted", "123", 1.5) + }, + staticNtfn: func() interface{} { + return btcjson.NewTxAcceptedNtfn("123", 1.5) + }, + marshalled: `{"jsonrpc":"1.0","method":"txaccepted","params":["123",1.5],"id":null}`, + unmarshalled: &btcjson.TxAcceptedNtfn{ + TxID: "123", + Amount: 1.5, + }, + }, + { + name: "txacceptedverbose", + newNtfn: func() (interface{}, error) { + return btcjson.NewCmd("txacceptedverbose", `{"hex":"001122","txid":"123","version":1,"locktime":4294967295,"vin":null,"vout":null,"confirmations":0}`) + }, + staticNtfn: func() interface{} { + txResult := btcjson.TxRawResult{ + Hex: "001122", + Txid: "123", + Version: 1, + LockTime: 4294967295, + Vin: nil, + Vout: nil, + Confirmations: 0, + } + return btcjson.NewTxAcceptedVerboseNtfn(txResult) + }, + marshalled: `{"jsonrpc":"1.0","method":"txacceptedverbose","params":[{"hex":"001122","txid":"123","version":1,"locktime":4294967295,"vin":null,"vout":null}],"id":null}`, + unmarshalled: &btcjson.TxAcceptedVerboseNtfn{ + RawTx: btcjson.TxRawResult{ + Hex: "001122", + Txid: "123", + Version: 1, + LockTime: 4294967295, + Vin: nil, + Vout: nil, + Confirmations: 0, + }, + }, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Marshal the notification as created by the new static + // creation function. The ID is nil for notifications. + marshalled, err := btcjson.MarshalCmd(nil, test.staticNtfn()) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + // Ensure the notification is created without error via the + // generic new notification creation function. + cmd, err := test.newNtfn() + if err != nil { + t.Errorf("Test #%d (%s) unexpected NewCmd error: %v ", + i, test.name, err) + } + + // Marshal the notification as created by the generic new + // notification creation function. The ID is nil for + // notifications. + marshalled, err = btcjson.MarshalCmd(nil, cmd) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + var request btcjson.Request + if err := json.Unmarshal(marshalled, &request); err != nil { + t.Errorf("Test #%d (%s) unexpected error while "+ + "unmarshalling JSON-RPC request: %v", i, + test.name, err) + continue + } + + cmd, err = btcjson.UnmarshalCmd(&request) + if err != nil { + t.Errorf("UnmarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !reflect.DeepEqual(cmd, test.unmarshalled) { + t.Errorf("Test #%d (%s) unexpected unmarshalled command "+ + "- got %s, want %s", i, test.name, + fmt.Sprintf("(%T) %+[1]v", cmd), + fmt.Sprintf("(%T) %+[1]v\n", test.unmarshalled)) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwsresults.go b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwsresults.go new file mode 100644 index 0000000000000000000000000000000000000000..12968496c44762d815f014fde101c7f6a8b0f24d --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/chainsvrwsresults.go @@ -0,0 +1,10 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +// SessionResult models the data from the session command. +type SessionResult struct { + SessionID uint64 `json:"sessionid"` +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/cmdinfo.go b/vendor/github.com/btcsuite/btcd/btcjson/cmdinfo.go new file mode 100644 index 0000000000000000000000000000000000000000..1e78e2862ac52e55c01e8d37dd2eb7ce30d11e97 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/cmdinfo.go @@ -0,0 +1,249 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +import ( + "fmt" + "reflect" + "strings" +) + +// CmdMethod returns the method for the passed command. The provided command +// type must be a registered type. All commands provided by this package are +// registered by default. +func CmdMethod(cmd interface{}) (string, error) { + // Look up the cmd type and error out if not registered. + rt := reflect.TypeOf(cmd) + registerLock.RLock() + method, ok := concreteTypeToMethod[rt] + registerLock.RUnlock() + if !ok { + str := fmt.Sprintf("%q is not registered", method) + return "", makeError(ErrUnregisteredMethod, str) + } + + return method, nil +} + +// MethodUsageFlags returns the usage flags for the passed command method. The +// provided method must be associated with a registered type. All commands +// provided by this package are registered by default. +func MethodUsageFlags(method string) (UsageFlag, error) { + // Look up details about the provided method and error out if not + // registered. + registerLock.RLock() + info, ok := methodToInfo[method] + registerLock.RUnlock() + if !ok { + str := fmt.Sprintf("%q is not registered", method) + return 0, makeError(ErrUnregisteredMethod, str) + } + + return info.flags, nil +} + +// subStructUsage returns a string for use in the one-line usage for the given +// sub struct. Note that this is specifically for fields which consist of +// structs (or an array/slice of structs) as opposed to the top-level command +// struct. +// +// Any fields that include a jsonrpcusage struct tag will use that instead of +// being automatically generated. +func subStructUsage(structType reflect.Type) string { + numFields := structType.NumField() + fieldUsages := make([]string, 0, numFields) + for i := 0; i < structType.NumField(); i++ { + rtf := structType.Field(i) + + // When the field has a jsonrpcusage struct tag specified use + // that instead of automatically generating it. + if tag := rtf.Tag.Get("jsonrpcusage"); tag != "" { + fieldUsages = append(fieldUsages, tag) + continue + } + + // Create the name/value entry for the field while considering + // the type of the field. Not all possible types are covered + // here and when one of the types not specifically covered is + // encountered, the field name is simply reused for the value. + fieldName := strings.ToLower(rtf.Name) + fieldValue := fieldName + fieldKind := rtf.Type.Kind() + switch { + case isNumeric(fieldKind): + if fieldKind == reflect.Float32 || fieldKind == reflect.Float64 { + fieldValue = "n.nnn" + } else { + fieldValue = "n" + } + case fieldKind == reflect.String: + fieldValue = `"value"` + + case fieldKind == reflect.Struct: + fieldValue = subStructUsage(rtf.Type) + + case fieldKind == reflect.Array || fieldKind == reflect.Slice: + fieldValue = subArrayUsage(rtf.Type, fieldName) + } + + usage := fmt.Sprintf("%q:%s", fieldName, fieldValue) + fieldUsages = append(fieldUsages, usage) + } + + return fmt.Sprintf("{%s}", strings.Join(fieldUsages, ",")) +} + +// subArrayUsage returns a string for use in the one-line usage for the given +// array or slice. It also contains logic to convert plural field names to +// singular so the generated usage string reads better. +func subArrayUsage(arrayType reflect.Type, fieldName string) string { + // Convert plural field names to singular. Only works for English. + singularFieldName := fieldName + if strings.HasSuffix(fieldName, "ies") { + singularFieldName = strings.TrimSuffix(fieldName, "ies") + singularFieldName = singularFieldName + "y" + } else if strings.HasSuffix(fieldName, "es") { + singularFieldName = strings.TrimSuffix(fieldName, "es") + } else if strings.HasSuffix(fieldName, "s") { + singularFieldName = strings.TrimSuffix(fieldName, "s") + } + + elemType := arrayType.Elem() + switch elemType.Kind() { + case reflect.String: + return fmt.Sprintf("[%q,...]", singularFieldName) + + case reflect.Struct: + return fmt.Sprintf("[%s,...]", subStructUsage(elemType)) + } + + // Fall back to simply showing the field name in array syntax. + return fmt.Sprintf(`[%s,...]`, singularFieldName) +} + +// fieldUsage returns a string for use in the one-line usage for the struct +// field of a command. +// +// Any fields that include a jsonrpcusage struct tag will use that instead of +// being automatically generated. +func fieldUsage(structField reflect.StructField, defaultVal *reflect.Value) string { + // When the field has a jsonrpcusage struct tag specified use that + // instead of automatically generating it. + if tag := structField.Tag.Get("jsonrpcusage"); tag != "" { + return tag + } + + // Indirect the pointer if needed. + fieldType := structField.Type + if fieldType.Kind() == reflect.Ptr { + fieldType = fieldType.Elem() + } + + // When there is a default value, it must also be a pointer due to the + // rules enforced by RegisterCmd. + if defaultVal != nil { + indirect := defaultVal.Elem() + defaultVal = &indirect + } + + // Handle certain types uniquely to provide nicer usage. + fieldName := strings.ToLower(structField.Name) + switch fieldType.Kind() { + case reflect.String: + if defaultVal != nil { + return fmt.Sprintf("%s=%q", fieldName, + defaultVal.Interface()) + } + + return fmt.Sprintf("%q", fieldName) + + case reflect.Array, reflect.Slice: + return subArrayUsage(fieldType, fieldName) + + case reflect.Struct: + return subStructUsage(fieldType) + } + + // Simply return the field name when none of the above special cases + // apply. + if defaultVal != nil { + return fmt.Sprintf("%s=%v", fieldName, defaultVal.Interface()) + } + return fieldName +} + +// methodUsageText returns a one-line usage string for the provided command and +// method info. This is the main work horse for the exported MethodUsageText +// function. +func methodUsageText(rtp reflect.Type, defaults map[int]reflect.Value, method string) string { + // Generate the individual usage for each field in the command. Several + // simplifying assumptions are made here because the RegisterCmd + // function has already rigorously enforced the layout. + rt := rtp.Elem() + numFields := rt.NumField() + reqFieldUsages := make([]string, 0, numFields) + optFieldUsages := make([]string, 0, numFields) + for i := 0; i < numFields; i++ { + rtf := rt.Field(i) + var isOptional bool + if kind := rtf.Type.Kind(); kind == reflect.Ptr { + isOptional = true + } + + var defaultVal *reflect.Value + if defVal, ok := defaults[i]; ok { + defaultVal = &defVal + } + + // Add human-readable usage to the appropriate slice that is + // later used to generate the one-line usage. + usage := fieldUsage(rtf, defaultVal) + if isOptional { + optFieldUsages = append(optFieldUsages, usage) + } else { + reqFieldUsages = append(reqFieldUsages, usage) + } + } + + // Generate and return the one-line usage string. + usageStr := method + if len(reqFieldUsages) > 0 { + usageStr += " " + strings.Join(reqFieldUsages, " ") + } + if len(optFieldUsages) > 0 { + usageStr += fmt.Sprintf(" (%s)", strings.Join(optFieldUsages, " ")) + } + return usageStr +} + +// MethodUsageText returns a one-line usage string for the provided method. The +// provided method must be associated with a registered type. All commands +// provided by this package are registered by default. +func MethodUsageText(method string) (string, error) { + // Look up details about the provided method and error out if not + // registered. + registerLock.RLock() + rtp, ok := methodToConcreteType[method] + info := methodToInfo[method] + registerLock.RUnlock() + if !ok { + str := fmt.Sprintf("%q is not registered", method) + return "", makeError(ErrUnregisteredMethod, str) + } + + // When the usage for this method has already been generated, simply + // return it. + if info.usage != "" { + return info.usage, nil + } + + // Generate and store the usage string for future calls and return it. + usage := methodUsageText(rtp, info.defaults, method) + registerLock.Lock() + info.usage = usage + methodToInfo[method] = info + registerLock.Unlock() + return usage, nil +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/cmdinfo_test.go b/vendor/github.com/btcsuite/btcd/btcjson/cmdinfo_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b3336e646ffe42503ef1e01c3ee6333ee1122ad2 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/cmdinfo_test.go @@ -0,0 +1,430 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestCmdMethod tests the CmdMethod function to ensure it retunrs the expected +// methods and errors. +func TestCmdMethod(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + cmd interface{} + method string + err error + }{ + { + name: "unregistered type", + cmd: (*int)(nil), + err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod}, + }, + { + name: "nil pointer of registered type", + cmd: (*btcjson.GetBlockCmd)(nil), + method: "getblock", + }, + { + name: "nil instance of registered type", + cmd: &btcjson.GetBlockCountCmd{}, + method: "getblockcount", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + method, err := btcjson.CmdMethod(test.cmd) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+ + "want %T", i, test.name, err, test.err) + continue + } + if err != nil { + gotErrorCode := err.(btcjson.Error).ErrorCode + if gotErrorCode != test.err.(btcjson.Error).ErrorCode { + t.Errorf("Test #%d (%s) mismatched error code "+ + "- got %v (%v), want %v", i, test.name, + gotErrorCode, err, + test.err.(btcjson.Error).ErrorCode) + continue + } + + continue + } + + // Ensure method matches the expected value. + if method != test.method { + t.Errorf("Test #%d (%s) mismatched method - got %v, "+ + "want %v", i, test.name, method, test.method) + continue + } + } +} + +// TestMethodUsageFlags tests the MethodUsage function ensure it returns the +// expected flags and errors. +func TestMethodUsageFlags(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + method string + err error + flags btcjson.UsageFlag + }{ + { + name: "unregistered type", + method: "bogusmethod", + err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod}, + }, + { + name: "getblock", + method: "getblock", + flags: 0, + }, + { + name: "walletpassphrase", + method: "walletpassphrase", + flags: btcjson.UFWalletOnly, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + flags, err := btcjson.MethodUsageFlags(test.method) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+ + "want %T", i, test.name, err, test.err) + continue + } + if err != nil { + gotErrorCode := err.(btcjson.Error).ErrorCode + if gotErrorCode != test.err.(btcjson.Error).ErrorCode { + t.Errorf("Test #%d (%s) mismatched error code "+ + "- got %v (%v), want %v", i, test.name, + gotErrorCode, err, + test.err.(btcjson.Error).ErrorCode) + continue + } + + continue + } + + // Ensure flags match the expected value. + if flags != test.flags { + t.Errorf("Test #%d (%s) mismatched flags - got %v, "+ + "want %v", i, test.name, flags, test.flags) + continue + } + } +} + +// TestMethodUsageText tests the MethodUsageText function ensure it returns the +// expected text. +func TestMethodUsageText(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + method string + err error + expected string + }{ + { + name: "unregistered type", + method: "bogusmethod", + err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod}, + }, + { + name: "getblockcount", + method: "getblockcount", + expected: "getblockcount", + }, + { + name: "getblock", + method: "getblock", + expected: `getblock "hash" (verbose=true verbosetx=false)`, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + usage, err := btcjson.MethodUsageText(test.method) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+ + "want %T", i, test.name, err, test.err) + continue + } + if err != nil { + gotErrorCode := err.(btcjson.Error).ErrorCode + if gotErrorCode != test.err.(btcjson.Error).ErrorCode { + t.Errorf("Test #%d (%s) mismatched error code "+ + "- got %v (%v), want %v", i, test.name, + gotErrorCode, err, + test.err.(btcjson.Error).ErrorCode) + continue + } + + continue + } + + // Ensure usage matches the expected value. + if usage != test.expected { + t.Errorf("Test #%d (%s) mismatched usage - got %v, "+ + "want %v", i, test.name, usage, test.expected) + continue + } + + // Get the usage again to excerise caching. + usage, err = btcjson.MethodUsageText(test.method) + if err != nil { + t.Errorf("Test #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + // Ensure usage still matches the expected value. + if usage != test.expected { + t.Errorf("Test #%d (%s) mismatched usage - got %v, "+ + "want %v", i, test.name, usage, test.expected) + continue + } + } +} + +// TestFieldUsage tests the internal fieldUsage function ensure it returns the +// expected text. +func TestFieldUsage(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + field reflect.StructField + defValue *reflect.Value + expected string + }{ + { + name: "jsonrpcusage tag override", + field: func() reflect.StructField { + type s struct { + Test int `jsonrpcusage:"testvalue"` + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: "testvalue", + }, + { + name: "generic interface", + field: func() reflect.StructField { + type s struct { + Test interface{} + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `test`, + }, + { + name: "string without default value", + field: func() reflect.StructField { + type s struct { + Test string + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `"test"`, + }, + { + name: "string with default value", + field: func() reflect.StructField { + type s struct { + Test string + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: func() *reflect.Value { + value := "default" + rv := reflect.ValueOf(&value) + return &rv + }(), + expected: `test="default"`, + }, + { + name: "array of strings", + field: func() reflect.StructField { + type s struct { + Test []string + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `["test",...]`, + }, + { + name: "array of strings with plural field name 1", + field: func() reflect.StructField { + type s struct { + Keys []string + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `["key",...]`, + }, + { + name: "array of strings with plural field name 2", + field: func() reflect.StructField { + type s struct { + Addresses []string + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `["address",...]`, + }, + { + name: "array of strings with plural field name 3", + field: func() reflect.StructField { + type s struct { + Capabilities []string + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `["capability",...]`, + }, + { + name: "array of structs", + field: func() reflect.StructField { + type s2 struct { + Txid string + } + type s struct { + Capabilities []s2 + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `[{"txid":"value"},...]`, + }, + { + name: "array of ints", + field: func() reflect.StructField { + type s struct { + Test []int + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `[test,...]`, + }, + { + name: "sub struct with jsonrpcusage tag override", + field: func() reflect.StructField { + type s2 struct { + Test string `jsonrpcusage:"testusage"` + } + type s struct { + Test s2 + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `{testusage}`, + }, + { + name: "sub struct with string", + field: func() reflect.StructField { + type s2 struct { + Txid string + } + type s struct { + Test s2 + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `{"txid":"value"}`, + }, + { + name: "sub struct with int", + field: func() reflect.StructField { + type s2 struct { + Vout int + } + type s struct { + Test s2 + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `{"vout":n}`, + }, + { + name: "sub struct with float", + field: func() reflect.StructField { + type s2 struct { + Amount float64 + } + type s struct { + Test s2 + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `{"amount":n.nnn}`, + }, + { + name: "sub struct with sub struct", + field: func() reflect.StructField { + type s3 struct { + Amount float64 + } + type s2 struct { + Template s3 + } + type s struct { + Test s2 + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `{"template":{"amount":n.nnn}}`, + }, + { + name: "sub struct with slice", + field: func() reflect.StructField { + type s2 struct { + Capabilities []string + } + type s struct { + Test s2 + } + return reflect.TypeOf((*s)(nil)).Elem().Field(0) + }(), + defValue: nil, + expected: `{"capabilities":["capability",...]}`, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Ensure usage matches the expected value. + usage := btcjson.TstFieldUsage(test.field, test.defValue) + if usage != test.expected { + t.Errorf("Test #%d (%s) mismatched usage - got %v, "+ + "want %v", i, test.name, usage, test.expected) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/cmdparse.go b/vendor/github.com/btcsuite/btcd/btcjson/cmdparse.go new file mode 100644 index 0000000000000000000000000000000000000000..6e21b94611c56af3efe5567c930d1e3260ed3729 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/cmdparse.go @@ -0,0 +1,550 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" + "strings" +) + +// makeParams creates a slice of interface values for the given struct. +func makeParams(rt reflect.Type, rv reflect.Value) []interface{} { + numFields := rt.NumField() + params := make([]interface{}, 0, numFields) + for i := 0; i < numFields; i++ { + rtf := rt.Field(i) + rvf := rv.Field(i) + if rtf.Type.Kind() == reflect.Ptr { + if rvf.IsNil() { + break + } + rvf.Elem() + } + params = append(params, rvf.Interface()) + } + + return params +} + +// MarshalCmd marshals the passed command to a JSON-RPC request byte slice that +// is suitable for transmission to an RPC server. The provided command type +// must be a registered type. All commands provided by this package are +// registered by default. +func MarshalCmd(id interface{}, cmd interface{}) ([]byte, error) { + // Look up the cmd type and error out if not registered. + rt := reflect.TypeOf(cmd) + registerLock.RLock() + method, ok := concreteTypeToMethod[rt] + registerLock.RUnlock() + if !ok { + str := fmt.Sprintf("%q is not registered", method) + return nil, makeError(ErrUnregisteredMethod, str) + } + + // The provided command must not be nil. + rv := reflect.ValueOf(cmd) + if rv.IsNil() { + str := fmt.Sprint("the specified command is nil") + return nil, makeError(ErrInvalidType, str) + } + + // Create a slice of interface values in the order of the struct fields + // while respecting pointer fields as optional params and only adding + // them if they are non-nil. + params := makeParams(rt.Elem(), rv.Elem()) + + // Generate and marshal the final JSON-RPC request. + rawCmd, err := NewRequest(id, method, params) + if err != nil { + return nil, err + } + return json.Marshal(rawCmd) +} + +// checkNumParams ensures the supplied number of params is at least the minimum +// required number for the command and less than the maximum allowed. +func checkNumParams(numParams int, info *methodInfo) error { + if numParams < info.numReqParams || numParams > info.maxParams { + if info.numReqParams == info.maxParams { + str := fmt.Sprintf("wrong number of params (expected "+ + "%d, received %d)", info.numReqParams, + numParams) + return makeError(ErrNumParams, str) + } + + str := fmt.Sprintf("wrong number of params (expected "+ + "between %d and %d, received %d)", info.numReqParams, + info.maxParams, numParams) + return makeError(ErrNumParams, str) + } + + return nil +} + +// populateDefaults populates default values into any remaining optional struct +// fields that did not have parameters explicitly provided. The caller should +// have previously checked that the number of parameters being passed is at +// least the required number of parameters to avoid unnecessary work in this +// function, but since required fields never have default values, it will work +// properly even without the check. +func populateDefaults(numParams int, info *methodInfo, rv reflect.Value) { + // When there are no more parameters left in the supplied parameters, + // any remaining struct fields must be optional. Thus, populate them + // with their associated default value as needed. + for i := numParams; i < info.maxParams; i++ { + rvf := rv.Field(i) + if defaultVal, ok := info.defaults[i]; ok { + rvf.Set(defaultVal) + } + } +} + +// UnmarshalCmd unmarshals a JSON-RPC request into a suitable concrete command +// so long as the method type contained within the marshalled request is +// registered. +func UnmarshalCmd(r *Request) (interface{}, error) { + registerLock.RLock() + rtp, ok := methodToConcreteType[r.Method] + info := methodToInfo[r.Method] + registerLock.RUnlock() + if !ok { + str := fmt.Sprintf("%q is not registered", r.Method) + return nil, makeError(ErrUnregisteredMethod, str) + } + rt := rtp.Elem() + rvp := reflect.New(rt) + rv := rvp.Elem() + + // Ensure the number of parameters are correct. + numParams := len(r.Params) + if err := checkNumParams(numParams, &info); err != nil { + return nil, err + } + + // Loop through each of the struct fields and unmarshal the associated + // parameter into them. + for i := 0; i < numParams; i++ { + rvf := rv.Field(i) + // Unmarshal the parameter into the struct field. + concreteVal := rvf.Addr().Interface() + if err := json.Unmarshal(r.Params[i], &concreteVal); err != nil { + // The most common error is the wrong type, so + // explicitly detect that error and make it nicer. + fieldName := strings.ToLower(rt.Field(i).Name) + if jerr, ok := err.(*json.UnmarshalTypeError); ok { + str := fmt.Sprintf("parameter #%d '%s' must "+ + "be type %v (got %v)", i+1, fieldName, + jerr.Type, jerr.Value) + return nil, makeError(ErrInvalidType, str) + } + + // Fallback to showing the underlying error. + str := fmt.Sprintf("parameter #%d '%s' failed to "+ + "unmarshal: %v", i+1, fieldName, err) + return nil, makeError(ErrInvalidType, str) + } + } + + // When there are less supplied parameters than the total number of + // params, any remaining struct fields must be optional. Thus, populate + // them with their associated default value as needed. + if numParams < info.maxParams { + populateDefaults(numParams, &info, rv) + } + + return rvp.Interface(), nil +} + +// isNumeric returns whether the passed reflect kind is a signed or unsigned +// integer of any magnitude or a float of any magnitude. +func isNumeric(kind reflect.Kind) bool { + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64, reflect.Float32, reflect.Float64: + + return true + } + + return false +} + +// typesMaybeCompatible returns whether the source type can possibly be +// assigned to the destination type. This is intended as a relatively quick +// check to weed out obviously invalid conversions. +func typesMaybeCompatible(dest reflect.Type, src reflect.Type) bool { + // The same types are obviously compatible. + if dest == src { + return true + } + + // When both types are numeric, they are potentially compatibile. + srcKind := src.Kind() + destKind := dest.Kind() + if isNumeric(destKind) && isNumeric(srcKind) { + return true + } + + if srcKind == reflect.String { + // Strings can potentially be converted to numeric types. + if isNumeric(destKind) { + return true + } + + switch destKind { + // Strings can potentially be converted to bools by + // strconv.ParseBool. + case reflect.Bool: + return true + + // Strings can be converted to any other type which has as + // underlying type of string. + case reflect.String: + return true + + // Strings can potentially be converted to arrays, slice, + // structs, and maps via json.Unmarshal. + case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map: + return true + } + } + + return false +} + +// baseType returns the type of the argument after indirecting through all +// pointers along with how many indirections were necessary. +func baseType(arg reflect.Type) (reflect.Type, int) { + var numIndirects int + for arg.Kind() == reflect.Ptr { + arg = arg.Elem() + numIndirects++ + } + return arg, numIndirects +} + +// assignField is the main workhorse for the NewCmd function which handles +// assigning the provided source value to the destination field. It supports +// direct type assignments, indirection, conversion of numeric types, and +// unmarshaling of strings into arrays, slices, structs, and maps via +// json.Unmarshal. +func assignField(paramNum int, fieldName string, dest reflect.Value, src reflect.Value) error { + // Just error now when the types have no chance of being compatible. + destBaseType, destIndirects := baseType(dest.Type()) + srcBaseType, srcIndirects := baseType(src.Type()) + if !typesMaybeCompatible(destBaseType, srcBaseType) { + str := fmt.Sprintf("parameter #%d '%s' must be type %v (got "+ + "%v)", paramNum, fieldName, destBaseType, srcBaseType) + return makeError(ErrInvalidType, str) + } + + // Check if it's possible to simply set the dest to the provided source. + // This is the case when the base types are the same or they are both + // pointers that can be indirected to be the same without needing to + // create pointers for the destination field. + if destBaseType == srcBaseType && srcIndirects >= destIndirects { + for i := 0; i < srcIndirects-destIndirects; i++ { + src = src.Elem() + } + dest.Set(src) + return nil + } + + // When the destination has more indirects than the source, the extra + // pointers have to be created. Only create enough pointers to reach + // the same level of indirection as the source so the dest can simply be + // set to the provided source when the types are the same. + destIndirectsRemaining := destIndirects + if destIndirects > srcIndirects { + indirectDiff := destIndirects - srcIndirects + for i := 0; i < indirectDiff; i++ { + dest.Set(reflect.New(dest.Type().Elem())) + dest = dest.Elem() + destIndirectsRemaining-- + } + } + + if destBaseType == srcBaseType { + dest.Set(src) + return nil + } + + // Make any remaining pointers needed to get to the base dest type since + // the above direct assign was not possible and conversions are done + // against the base types. + for i := 0; i < destIndirectsRemaining; i++ { + dest.Set(reflect.New(dest.Type().Elem())) + dest = dest.Elem() + } + + // Indirect through to the base source value. + for src.Kind() == reflect.Ptr { + src = src.Elem() + } + + // Perform supported type conversions. + switch src.Kind() { + // Source value is a signed integer of various magnitude. + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64: + + switch dest.Kind() { + // Destination is a signed integer of various magnitude. + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64: + + srcInt := src.Int() + if dest.OverflowInt(srcInt) { + str := fmt.Sprintf("parameter #%d '%s' "+ + "overflows destination type %v", + paramNum, fieldName, destBaseType) + return makeError(ErrInvalidType, str) + } + + dest.SetInt(srcInt) + + // Destination is an unsigned integer of various magnitude. + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64: + + srcInt := src.Int() + if srcInt < 0 || dest.OverflowUint(uint64(srcInt)) { + str := fmt.Sprintf("parameter #%d '%s' "+ + "overflows destination type %v", + paramNum, fieldName, destBaseType) + return makeError(ErrInvalidType, str) + } + dest.SetUint(uint64(srcInt)) + + default: + str := fmt.Sprintf("parameter #%d '%s' must be type "+ + "%v (got %v)", paramNum, fieldName, destBaseType, + srcBaseType) + return makeError(ErrInvalidType, str) + } + + // Source value is an unsigned integer of various magnitude. + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64: + + switch dest.Kind() { + // Destination is a signed integer of various magnitude. + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64: + + srcUint := src.Uint() + if srcUint > uint64(1<<63)-1 { + str := fmt.Sprintf("parameter #%d '%s' "+ + "overflows destination type %v", + paramNum, fieldName, destBaseType) + return makeError(ErrInvalidType, str) + } + if dest.OverflowInt(int64(srcUint)) { + str := fmt.Sprintf("parameter #%d '%s' "+ + "overflows destination type %v", + paramNum, fieldName, destBaseType) + return makeError(ErrInvalidType, str) + } + dest.SetInt(int64(srcUint)) + + // Destination is an unsigned integer of various magnitude. + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64: + + srcUint := src.Uint() + if dest.OverflowUint(srcUint) { + str := fmt.Sprintf("parameter #%d '%s' "+ + "overflows destination type %v", + paramNum, fieldName, destBaseType) + return makeError(ErrInvalidType, str) + } + dest.SetUint(srcUint) + + default: + str := fmt.Sprintf("parameter #%d '%s' must be type "+ + "%v (got %v)", paramNum, fieldName, destBaseType, + srcBaseType) + return makeError(ErrInvalidType, str) + } + + // Source value is a float. + case reflect.Float32, reflect.Float64: + destKind := dest.Kind() + if destKind != reflect.Float32 && destKind != reflect.Float64 { + str := fmt.Sprintf("parameter #%d '%s' must be type "+ + "%v (got %v)", paramNum, fieldName, destBaseType, + srcBaseType) + return makeError(ErrInvalidType, str) + } + + srcFloat := src.Float() + if dest.OverflowFloat(srcFloat) { + str := fmt.Sprintf("parameter #%d '%s' overflows "+ + "destination type %v", paramNum, fieldName, + destBaseType) + return makeError(ErrInvalidType, str) + } + dest.SetFloat(srcFloat) + + // Source value is a string. + case reflect.String: + switch dest.Kind() { + // String -> bool + case reflect.Bool: + b, err := strconv.ParseBool(src.String()) + if err != nil { + str := fmt.Sprintf("parameter #%d '%s' must "+ + "parse to a %v", paramNum, fieldName, + destBaseType) + return makeError(ErrInvalidType, str) + } + dest.SetBool(b) + + // String -> signed integer of varying size. + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64: + + srcInt, err := strconv.ParseInt(src.String(), 0, 0) + if err != nil { + str := fmt.Sprintf("parameter #%d '%s' must "+ + "parse to a %v", paramNum, fieldName, + destBaseType) + return makeError(ErrInvalidType, str) + } + if dest.OverflowInt(srcInt) { + str := fmt.Sprintf("parameter #%d '%s' "+ + "overflows destination type %v", + paramNum, fieldName, destBaseType) + return makeError(ErrInvalidType, str) + } + dest.SetInt(srcInt) + + // String -> unsigned integer of varying size. + case reflect.Uint, reflect.Uint8, reflect.Uint16, + reflect.Uint32, reflect.Uint64: + + srcUint, err := strconv.ParseUint(src.String(), 0, 0) + if err != nil { + str := fmt.Sprintf("parameter #%d '%s' must "+ + "parse to a %v", paramNum, fieldName, + destBaseType) + return makeError(ErrInvalidType, str) + } + if dest.OverflowUint(srcUint) { + str := fmt.Sprintf("parameter #%d '%s' "+ + "overflows destination type %v", + paramNum, fieldName, destBaseType) + return makeError(ErrInvalidType, str) + } + dest.SetUint(srcUint) + + // String -> float of varying size. + case reflect.Float32, reflect.Float64: + srcFloat, err := strconv.ParseFloat(src.String(), 0) + if err != nil { + str := fmt.Sprintf("parameter #%d '%s' must "+ + "parse to a %v", paramNum, fieldName, + destBaseType) + return makeError(ErrInvalidType, str) + } + if dest.OverflowFloat(srcFloat) { + str := fmt.Sprintf("parameter #%d '%s' "+ + "overflows destination type %v", + paramNum, fieldName, destBaseType) + return makeError(ErrInvalidType, str) + } + dest.SetFloat(srcFloat) + + // String -> string (typecast). + case reflect.String: + dest.SetString(src.String()) + + // String -> arrays, slices, structs, and maps via + // json.Unmarshal. + case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map: + concreteVal := dest.Addr().Interface() + err := json.Unmarshal([]byte(src.String()), &concreteVal) + if err != nil { + str := fmt.Sprintf("parameter #%d '%s' must "+ + "be valid JSON which unsmarshals to a %v", + paramNum, fieldName, destBaseType) + return makeError(ErrInvalidType, str) + } + dest.Set(reflect.ValueOf(concreteVal).Elem()) + } + } + + return nil +} + +// NewCmd provides a generic mechanism to create a new command that can marshal +// to a JSON-RPC request while respecting the requirements of the provided +// method. The method must have been registered with the package already along +// with its type definition. All methods associated with the commands exported +// by this package are already registered by default. +// +// The arguments are most efficient when they are the exact same type as the +// underlying field in the command struct associated with the the method, +// however this function also will perform a variety of conversions to make it +// more flexible. This allows, for example, command line args which are strings +// to be passed unaltered. In particular, the following conversions are +// supported: +// +// - Conversion between any size signed or unsigned integer so long as the +// value does not overflow the destination type +// - Conversion between float32 and float64 so long as the value does not +// overflow the destination type +// - Conversion from string to boolean for everything strconv.ParseBool +// recognizes +// - Conversion from string to any size integer for everything +// strconv.ParseInt and strconv.ParseUint recognizes +// - Conversion from string to any size float for everything +// strconv.ParseFloat recognizes +// - Conversion from string to arrays, slices, structs, and maps by treating +// the string as marshalled JSON and calling json.Unmarshal into the +// destination field +func NewCmd(method string, args ...interface{}) (interface{}, error) { + // Look up details about the provided method. Any methods that aren't + // registered are an error. + registerLock.RLock() + rtp, ok := methodToConcreteType[method] + info := methodToInfo[method] + registerLock.RUnlock() + if !ok { + str := fmt.Sprintf("%q is not registered", method) + return nil, makeError(ErrUnregisteredMethod, str) + } + + // Ensure the number of parameters are correct. + numParams := len(args) + if err := checkNumParams(numParams, &info); err != nil { + return nil, err + } + + // Create the appropriate command type for the method. Since all types + // are enforced to be a pointer to a struct at registration time, it's + // safe to indirect to the struct now. + rvp := reflect.New(rtp.Elem()) + rv := rvp.Elem() + rt := rtp.Elem() + + // Loop through each of the struct fields and assign the associated + // parameter into them after checking its type validity. + for i := 0; i < numParams; i++ { + // Attempt to assign each of the arguments to the according + // struct field. + rvf := rv.Field(i) + fieldName := strings.ToLower(rt.Field(i).Name) + err := assignField(i+1, fieldName, rvf, reflect.ValueOf(args[i])) + if err != nil { + return nil, err + } + } + + return rvp.Interface(), nil +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/cmdparse_test.go b/vendor/github.com/btcsuite/btcd/btcjson/cmdparse_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2f8fa1fb9670e379bd24a7388b1675ca2f585e7f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/cmdparse_test.go @@ -0,0 +1,519 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "encoding/json" + "math" + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestAssignField tests the assignField function handles supported combinations +// properly. +func TestAssignField(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + dest interface{} + src interface{} + expected interface{} + }{ + { + name: "same types", + dest: int8(0), + src: int8(100), + expected: int8(100), + }, + { + name: "same types - more source pointers", + dest: int8(0), + src: func() interface{} { + i := int8(100) + return &i + }(), + expected: int8(100), + }, + { + name: "same types - more dest pointers", + dest: func() interface{} { + i := int8(0) + return &i + }(), + src: int8(100), + expected: int8(100), + }, + { + name: "convertible types - more source pointers", + dest: int16(0), + src: func() interface{} { + i := int8(100) + return &i + }(), + expected: int16(100), + }, + { + name: "convertible types - both pointers", + dest: func() interface{} { + i := int8(0) + return &i + }(), + src: func() interface{} { + i := int16(100) + return &i + }(), + expected: int8(100), + }, + { + name: "convertible types - int16 -> int8", + dest: int8(0), + src: int16(100), + expected: int8(100), + }, + { + name: "convertible types - int16 -> uint8", + dest: uint8(0), + src: int16(100), + expected: uint8(100), + }, + { + name: "convertible types - uint16 -> int8", + dest: int8(0), + src: uint16(100), + expected: int8(100), + }, + { + name: "convertible types - uint16 -> uint8", + dest: uint8(0), + src: uint16(100), + expected: uint8(100), + }, + { + name: "convertible types - float32 -> float64", + dest: float64(0), + src: float32(1.5), + expected: float64(1.5), + }, + { + name: "convertible types - float64 -> float32", + dest: float32(0), + src: float64(1.5), + expected: float32(1.5), + }, + { + name: "convertible types - string -> bool", + dest: false, + src: "true", + expected: true, + }, + { + name: "convertible types - string -> int8", + dest: int8(0), + src: "100", + expected: int8(100), + }, + { + name: "convertible types - string -> uint8", + dest: uint8(0), + src: "100", + expected: uint8(100), + }, + { + name: "convertible types - string -> float32", + dest: float32(0), + src: "1.5", + expected: float32(1.5), + }, + { + name: "convertible types - typecase string -> string", + dest: "", + src: func() interface{} { + type foo string + return foo("foo") + }(), + expected: "foo", + }, + { + name: "convertible types - string -> array", + dest: [2]string{}, + src: `["test","test2"]`, + expected: [2]string{"test", "test2"}, + }, + { + name: "convertible types - string -> slice", + dest: []string{}, + src: `["test","test2"]`, + expected: []string{"test", "test2"}, + }, + { + name: "convertible types - string -> struct", + dest: struct{ A int }{}, + src: `{"A":100}`, + expected: struct{ A int }{100}, + }, + { + name: "convertible types - string -> map", + dest: map[string]float64{}, + src: `{"1Address":1.5}`, + expected: map[string]float64{"1Address": 1.5}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + dst := reflect.New(reflect.TypeOf(test.dest)).Elem() + src := reflect.ValueOf(test.src) + err := btcjson.TstAssignField(1, "testField", dst, src) + if err != nil { + t.Errorf("Test #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + // Inidirect through to the base types to ensure their values + // are the same. + for dst.Kind() == reflect.Ptr { + dst = dst.Elem() + } + if !reflect.DeepEqual(dst.Interface(), test.expected) { + t.Errorf("Test #%d (%s) unexpected value - got %v, "+ + "want %v", i, test.name, dst.Interface(), + test.expected) + continue + } + } +} + +// TestAssignFieldErrors tests the assignField function error paths. +func TestAssignFieldErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + dest interface{} + src interface{} + err btcjson.Error + }{ + { + name: "general incompatible int -> string", + dest: string(0), + src: int(0), + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "overflow source int -> dest int", + dest: int8(0), + src: int(128), + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "overflow source int -> dest uint", + dest: uint8(0), + src: int(256), + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "int -> float", + dest: float32(0), + src: int(256), + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "overflow source uint64 -> dest int64", + dest: int64(0), + src: uint64(1 << 63), + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "overflow source uint -> dest int", + dest: int8(0), + src: uint(128), + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "overflow source uint -> dest uint", + dest: uint8(0), + src: uint(256), + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "uint -> float", + dest: float32(0), + src: uint(256), + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "float -> int", + dest: int(0), + src: float32(1.0), + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "overflow float64 -> float32", + dest: float32(0), + src: float64(math.MaxFloat64), + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "invalid string -> bool", + dest: true, + src: "foo", + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "invalid string -> int", + dest: int8(0), + src: "foo", + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "overflow string -> int", + dest: int8(0), + src: "128", + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "invalid string -> uint", + dest: uint8(0), + src: "foo", + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "overflow string -> uint", + dest: uint8(0), + src: "256", + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "invalid string -> float", + dest: float32(0), + src: "foo", + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "overflow string -> float", + dest: float32(0), + src: "1.7976931348623157e+308", + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "invalid string -> array", + dest: [3]int{}, + src: "foo", + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "invalid string -> slice", + dest: []int{}, + src: "foo", + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "invalid string -> struct", + dest: struct{ A int }{}, + src: "foo", + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "invalid string -> map", + dest: map[string]int{}, + src: "foo", + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + dst := reflect.New(reflect.TypeOf(test.dest)).Elem() + src := reflect.ValueOf(test.src) + err := btcjson.TstAssignField(1, "testField", dst, src) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+ + "want %T", i, test.name, err, test.err) + continue + } + gotErrorCode := err.(btcjson.Error).ErrorCode + if gotErrorCode != test.err.ErrorCode { + t.Errorf("Test #%d (%s) mismatched error code - got "+ + "%v (%v), want %v", i, test.name, gotErrorCode, + err, test.err.ErrorCode) + continue + } + } +} + +// TestNewCmdErrors ensures the error paths of NewCmd behave as expected. +func TestNewCmdErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + method string + args []interface{} + err btcjson.Error + }{ + { + name: "unregistered command", + method: "boguscommand", + args: []interface{}{}, + err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod}, + }, + { + name: "too few parameters to command with required + optional", + method: "getblock", + args: []interface{}{}, + err: btcjson.Error{ErrorCode: btcjson.ErrNumParams}, + }, + { + name: "too many parameters to command with no optional", + method: "getblockcount", + args: []interface{}{"123"}, + err: btcjson.Error{ErrorCode: btcjson.ErrNumParams}, + }, + { + name: "incorrect parameter type", + method: "getblock", + args: []interface{}{1}, + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + _, err := btcjson.NewCmd(test.method, test.args...) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+ + "want %T", i, test.name, err, test.err) + continue + } + gotErrorCode := err.(btcjson.Error).ErrorCode + if gotErrorCode != test.err.ErrorCode { + t.Errorf("Test #%d (%s) mismatched error code - got "+ + "%v (%v), want %v", i, test.name, gotErrorCode, + err, test.err.ErrorCode) + continue + } + } +} + +// TestMarshalCmdErrors tests the error paths of the MarshalCmd function. +func TestMarshalCmdErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + id interface{} + cmd interface{} + err btcjson.Error + }{ + { + name: "unregistered type", + id: 1, + cmd: (*int)(nil), + err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod}, + }, + { + name: "nil instance of registered type", + id: 1, + cmd: (*btcjson.GetBlockCmd)(nil), + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "nil instance of registered type", + id: []int{0, 1}, + cmd: &btcjson.GetBlockCountCmd{}, + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + _, err := btcjson.MarshalCmd(test.id, test.cmd) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+ + "want %T", i, test.name, err, test.err) + continue + } + gotErrorCode := err.(btcjson.Error).ErrorCode + if gotErrorCode != test.err.ErrorCode { + t.Errorf("Test #%d (%s) mismatched error code - got "+ + "%v (%v), want %v", i, test.name, gotErrorCode, + err, test.err.ErrorCode) + continue + } + } +} + +// TestUnmarshalCmdErrors tests the error paths of the UnmarshalCmd function. +func TestUnmarshalCmdErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + request btcjson.Request + err btcjson.Error + }{ + { + name: "unregistered type", + request: btcjson.Request{ + Jsonrpc: "1.0", + Method: "bogusmethod", + Params: nil, + ID: nil, + }, + err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod}, + }, + { + name: "incorrect number of params", + request: btcjson.Request{ + Jsonrpc: "1.0", + Method: "getblockcount", + Params: []json.RawMessage{[]byte(`"bogusparam"`)}, + ID: nil, + }, + err: btcjson.Error{ErrorCode: btcjson.ErrNumParams}, + }, + { + name: "invalid type for a parameter", + request: btcjson.Request{ + Jsonrpc: "1.0", + Method: "getblock", + Params: []json.RawMessage{[]byte("1")}, + ID: nil, + }, + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "invalid JSON for a parameter", + request: btcjson.Request{ + Jsonrpc: "1.0", + Method: "getblock", + Params: []json.RawMessage{[]byte(`"1`)}, + ID: nil, + }, + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + _, err := btcjson.UnmarshalCmd(&test.request) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+ + "want %T", i, test.name, err, test.err) + continue + } + gotErrorCode := err.(btcjson.Error).ErrorCode + if gotErrorCode != test.err.ErrorCode { + t.Errorf("Test #%d (%s) mismatched error code - got "+ + "%v (%v), want %v", i, test.name, gotErrorCode, + err, test.err.ErrorCode) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/doc.go b/vendor/github.com/btcsuite/btcd/btcjson/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..165b9ef91ce08b3b1c6f225a1d3f2c195de216f1 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/doc.go @@ -0,0 +1,146 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package btcjson provides primitives for working with the bitcoin JSON-RPC API. + +Overview + +When communicating via the JSON-RPC protocol, all of the commands need to be +marshalled to and from the the wire in the appropriate format. This package +provides data structures and primitives to ease this process. + +In addition, it also provides some additional features such as custom command +registration, command categorization, and reflection-based help generation. + +JSON-RPC Protocol Overview + +This information is not necessary in order to use this package, but it does +provide some intuition into what the marshalling and unmarshalling that is +discussed below is doing under the hood. + +As defined by the JSON-RPC spec, there are effectively two forms of messages on +the wire: + + - Request Objects + {"jsonrpc":"1.0","id":"SOMEID","method":"SOMEMETHOD","params":[SOMEPARAMS]} + NOTE: Notifications are the same format except the id field is null. + + - Response Objects + {"result":SOMETHING,"error":null,"id":"SOMEID"} + {"result":null,"error":{"code":SOMEINT,"message":SOMESTRING},"id":"SOMEID"} + +For requests, the params field can vary in what it contains depending on the +method (a.k.a. command) being sent. Each parameter can be as simple as an int +or a complex structure containing many nested fields. The id field is used to +identify a request and will be included in the associated response. + +When working with asynchronous transports, such as websockets, spontaneous +notifications are also possible. As indicated, they are the same as a request +object, except they have the id field set to null. Therefore, servers will +ignore requests with the id field set to null, while clients can choose to +consume or ignore them. + +Unfortunately, the original Bitcoin JSON-RPC API (and hence anything compatible +with it) doesn't always follow the spec and will sometimes return an error +string in the result field with a null error for certain commands. However, +for the most part, the error field will be set as described on failure. + +Marshalling and Unmarshalling + +Based upon the discussion above, it should be easy to see how the types of this +package map into the required parts of the protocol + + - Request Objects (type Request) + - Commands (type <Foo>Cmd) + - Notifications (type <Foo>Ntfn) + - Response Objects (type Response) + - Result (type <Foo>Result) + +To simplify the marshalling of the requests and responses, the MarshalCmd and +MarshalResponse functions are provided. They return the raw bytes ready to be +sent across the wire. + +Unmarshalling a received Request object is a two step process: + 1) Unmarshal the raw bytes into a Request struct instance via json.Unmarshal + 2) Use UnmarshalCmd on the Result field of the unmarshalled Request to create + a concrete command or notification instance with all struct fields set + accordingly + +This approach is used since it provides the caller with access to the additional +fields in the request that are not part of the command such as the ID. + +Unmarshalling a received Response object is also a two step process: + 1) Unmarhsal the raw bytes into a Response struct instance via json.Unmarshal + 2) Depending on the ID, unmarshal the Result field of the unmarshalled + Response to create a concrete type instance + +As above, this approach is used since it provides the caller with access to the +fields in the response such as the ID and Error. + +Command Creation + +This package provides two approaches for creating a new command. This first, +and preferred, method is to use one of the New<Foo>Cmd functions. This allows +static compile-time checking to help ensure the parameters stay in sync with +the struct definitions. + +The second approach is the NewCmd function which takes a method (command) name +and variable arguments. The function includes full checking to ensure the +parameters are accurate according to provided method, however these checks are, +obviously, run-time which means any mistakes won't be found until the code is +actually executed. However, it is quite useful for user-supplied commands +that are intentionally dynamic. + +Custom Command Registration + +The command handling of this package is built around the concept of registered +commands. This is true for the wide variety of commands already provided by the +package, but it also means caller can easily provide custom commands with all +of the same functionality as the built-in commands. Use the RegisterCmd +function for this purpose. + +A list of all registered methods can be obtained with the RegisteredCmdMethods +function. + +Command Inspection + +All registered commands are registered with flags that identify information such +as whether the command applies to a chain server, wallet server, or is a +notification along with the method name to use. These flags can be obtained +with the MethodUsageFlags flags, and the method can be obtained with the +CmdMethod function. + +Help Generation + +To facilitate providing consistent help to users of the RPC server, this package +exposes the GenerateHelp and function which uses reflection on registered +commands or notifications, as well as the provided expected result types, to +generate the final help text. + +In addition, the MethodUsageText function is provided to generate consistent +one-line usage for registered commands and notifications using reflection. + +Errors + +There are 2 distinct type of errors supported by this package: + + - General errors related to marshalling or unmarshalling or improper use of + the package (type Error) + - RPC errors which are intended to be returned across the wire as a part of + the JSON-RPC response (type RPCError) + +The first category of errors (type Error) typically indicates a programmer error +and can be avoided by properly using the API. Errors of this type will be +returned from the various functions available in this package. They identify +issues such as unsupported field types, attempts to register malformed commands, +and attempting to create a new command with an improper number of parameters. +The specific reason for the error can be detected by type asserting it to a +*btcjson.Error and accessing the ErrorCode field. + +The second category of errors (type RPCError), on the other hand, are useful for +returning errors to RPC clients. Consequently, they are used in the previously +described Response type. +*/ +package btcjson diff --git a/vendor/github.com/btcsuite/btcd/btcjson/error.go b/vendor/github.com/btcsuite/btcd/btcjson/error.go new file mode 100644 index 0000000000000000000000000000000000000000..3d72329f910b078cd4dde85ccf40b9fe9b80178f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/error.go @@ -0,0 +1,111 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +import ( + "fmt" +) + +// ErrorCode identifies a kind of error. These error codes are NOT used for +// JSON-RPC response errors. +type ErrorCode int + +// These constants are used to identify a specific RuleError. +const ( + // ErrDuplicateMethod indicates a command with the specified method + // already exists. + ErrDuplicateMethod ErrorCode = iota + + // ErrInvalidUsageFlags indicates one or more unrecognized flag bits + // were specified. + ErrInvalidUsageFlags + + // ErrInvalidType indicates a type was passed that is not the required + // type. + ErrInvalidType + + // ErrEmbeddedType indicates the provided command struct contains an + // embedded type which is not not supported. + ErrEmbeddedType + + // ErrUnexportedField indiciates the provided command struct contains an + // unexported field which is not supported. + ErrUnexportedField + + // ErrUnsupportedFieldType indicates the type of a field in the provided + // command struct is not one of the supported types. + ErrUnsupportedFieldType + + // ErrNonOptionalField indicates a non-optional field was specified + // after an optional field. + ErrNonOptionalField + + // ErrNonOptionalDefault indicates a 'jsonrpcdefault' struct tag was + // specified for a non-optional field. + ErrNonOptionalDefault + + // ErrMismatchedDefault indicates a 'jsonrpcdefault' struct tag contains + // a value that doesn't match the type of the field. + ErrMismatchedDefault + + // ErrUnregisteredMethod indicates a method was specified that has not + // been registered. + ErrUnregisteredMethod + + // ErrMissingDescription indicates a description required to generate + // help is missing. + ErrMissingDescription + + // ErrNumParams inidcates the number of params supplied do not + // match the requirements of the associated command. + ErrNumParams + + // numErrorCodes is the maximum error code number used in tests. + numErrorCodes +) + +// Map of ErrorCode values back to their constant names for pretty printing. +var errorCodeStrings = map[ErrorCode]string{ + ErrDuplicateMethod: "ErrDuplicateMethod", + ErrInvalidUsageFlags: "ErrInvalidUsageFlags", + ErrInvalidType: "ErrInvalidType", + ErrEmbeddedType: "ErrEmbeddedType", + ErrUnexportedField: "ErrUnexportedField", + ErrUnsupportedFieldType: "ErrUnsupportedFieldType", + ErrNonOptionalField: "ErrNonOptionalField", + ErrNonOptionalDefault: "ErrNonOptionalDefault", + ErrMismatchedDefault: "ErrMismatchedDefault", + ErrUnregisteredMethod: "ErrUnregisteredMethod", + ErrMissingDescription: "ErrMissingDescription", + ErrNumParams: "ErrNumParams", +} + +// String returns the ErrorCode as a human-readable name. +func (e ErrorCode) String() string { + if s := errorCodeStrings[e]; s != "" { + return s + } + return fmt.Sprintf("Unknown ErrorCode (%d)", int(e)) +} + +// Error identifies a general error. This differs from an RPCError in that this +// error typically is used more by the consumers of the package as opposed to +// RPCErrors which are intended to be returned to the client across the wire via +// a JSON-RPC Response. The caller can use type assertions to determine the +// specific error and access the ErrorCode field. +type Error struct { + ErrorCode ErrorCode // Describes the kind of error + Description string // Human readable description of the issue +} + +// Error satisfies the error interface and prints human-readable errors. +func (e Error) Error() string { + return e.Description +} + +// makeError creates an Error given a set of arguments. +func makeError(c ErrorCode, desc string) Error { + return Error{ErrorCode: c, Description: desc} +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/error_test.go b/vendor/github.com/btcsuite/btcd/btcjson/error_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8eb93c7590c7c6daf44e0dd3e1c79467e38b0e1e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/error_test.go @@ -0,0 +1,80 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestErrorCodeStringer tests the stringized output for the ErrorCode type. +func TestErrorCodeStringer(t *testing.T) { + t.Parallel() + + tests := []struct { + in btcjson.ErrorCode + want string + }{ + {btcjson.ErrDuplicateMethod, "ErrDuplicateMethod"}, + {btcjson.ErrInvalidUsageFlags, "ErrInvalidUsageFlags"}, + {btcjson.ErrInvalidType, "ErrInvalidType"}, + {btcjson.ErrEmbeddedType, "ErrEmbeddedType"}, + {btcjson.ErrUnexportedField, "ErrUnexportedField"}, + {btcjson.ErrUnsupportedFieldType, "ErrUnsupportedFieldType"}, + {btcjson.ErrNonOptionalField, "ErrNonOptionalField"}, + {btcjson.ErrNonOptionalDefault, "ErrNonOptionalDefault"}, + {btcjson.ErrMismatchedDefault, "ErrMismatchedDefault"}, + {btcjson.ErrUnregisteredMethod, "ErrUnregisteredMethod"}, + {btcjson.ErrNumParams, "ErrNumParams"}, + {btcjson.ErrMissingDescription, "ErrMissingDescription"}, + {0xffff, "Unknown ErrorCode (65535)"}, + } + + // Detect additional error codes that don't have the stringer added. + if len(tests)-1 != int(btcjson.TstNumErrorCodes) { + t.Errorf("It appears an error code was added without adding an " + + "associated stringer test") + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.in.String() + if result != test.want { + t.Errorf("String #%d\n got: %s want: %s", i, result, + test.want) + continue + } + } +} + +// TestError tests the error output for the Error type. +func TestError(t *testing.T) { + t.Parallel() + + tests := []struct { + in btcjson.Error + want string + }{ + { + btcjson.Error{Description: "some error"}, + "some error", + }, + { + btcjson.Error{Description: "human-readable error"}, + "human-readable error", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.in.Error() + if result != test.want { + t.Errorf("Error #%d\n got: %s want: %s", i, result, + test.want) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/example_test.go b/vendor/github.com/btcsuite/btcd/btcjson/example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..527252c7fb6abddd8fadbceea93d3142058ff97a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/example_test.go @@ -0,0 +1,148 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "encoding/json" + "fmt" + + "github.com/btcsuite/btcd/btcjson" +) + +// This example demonstrates how to create and marshal a command into a JSON-RPC +// request. +func ExampleMarshalCmd() { + // Create a new getblock command. Notice the nil parameter indicates + // to use the default parameter for that fields. This is a common + // pattern used in all of the New<Foo>Cmd functions in this package for + // optional fields. Also, notice the call to btcjson.Bool which is a + // convenience function for creating a pointer out of a primitive for + // optional parameters. + blockHash := "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" + gbCmd := btcjson.NewGetBlockCmd(blockHash, btcjson.Bool(false), nil) + + // Marshal the command to the format suitable for sending to the RPC + // server. Typically the client would increment the id here which is + // request so the response can be identified. + id := 1 + marshalledBytes, err := btcjson.MarshalCmd(id, gbCmd) + if err != nil { + fmt.Println(err) + return + } + + // Display the marshalled command. Ordinarily this would be sent across + // the wire to the RPC server, but for this example, just display it. + fmt.Printf("%s\n", marshalledBytes) + + // Output: + // {"jsonrpc":"1.0","method":"getblock","params":["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",false],"id":1} +} + +// This example demonstrates how to unmarshal a JSON-RPC request and then +// unmarshal the concrete request into a concrete command. +func ExampleUnmarshalCmd() { + // Ordinarily this would be read from the wire, but for this example, + // it is hard coded here for clarity. + data := []byte(`{"jsonrpc":"1.0","method":"getblock","params":["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",false],"id":1}`) + + // Unmarshal the raw bytes from the wire into a JSON-RPC request. + var request btcjson.Request + if err := json.Unmarshal(data, &request); err != nil { + fmt.Println(err) + return + } + + // Typically there isn't any need to examine the request fields directly + // like this as the caller already knows what response to expect based + // on the command it sent. However, this is done here to demonstrate + // why the unmarshal process is two steps. + if request.ID == nil { + fmt.Println("Unexpected notification") + return + } + if request.Method != "getblock" { + fmt.Println("Unexpected method") + return + } + + // Unmarshal the request into a concrete command. + cmd, err := btcjson.UnmarshalCmd(&request) + if err != nil { + fmt.Println(err) + return + } + + // Type assert the command to the appropriate type. + gbCmd, ok := cmd.(*btcjson.GetBlockCmd) + if !ok { + fmt.Printf("Incorrect command type: %T\n", cmd) + return + } + + // Display the fields in the concrete command. + fmt.Println("Hash:", gbCmd.Hash) + fmt.Println("Verbose:", *gbCmd.Verbose) + fmt.Println("VerboseTx:", *gbCmd.VerboseTx) + + // Output: + // Hash: 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f + // Verbose: false + // VerboseTx: false +} + +// This example demonstrates how to marshal a JSON-RPC response. +func ExampleMarshalResponse() { + // Marshal a new JSON-RPC response. For example, this is a response + // to a getblockheight request. + marshalledBytes, err := btcjson.MarshalResponse(1, 350001, nil) + if err != nil { + fmt.Println(err) + return + } + + // Display the marshalled response. Ordinarily this would be sent + // across the wire to the RPC client, but for this example, just display + // it. + fmt.Printf("%s\n", marshalledBytes) + + // Output: + // {"result":350001,"error":null,"id":1} +} + +// This example demonstrates how to unmarshal a JSON-RPC response and then +// unmarshal the result field in the response to a concrete type. +func Example_unmarshalResponse() { + // Ordinarily this would be read from the wire, but for this example, + // it is hard coded here for clarity. This is an example response to a + // getblockheight request. + data := []byte(`{"result":350001,"error":null,"id":1}`) + + // Unmarshal the raw bytes from the wire into a JSON-RPC response. + var response btcjson.Response + if err := json.Unmarshal(data, &response); err != nil { + fmt.Println("Malformed JSON-RPC response:", err) + return + } + + // Check the response for an error from the server. For example, the + // server might return an error if an invalid/unknown block hash is + // requested. + if response.Error != nil { + fmt.Println(response.Error) + return + } + + // Unmarshal the result into the expected type for the response. + var blockHeight int32 + if err := json.Unmarshal(response.Result, &blockHeight); err != nil { + fmt.Printf("Unexpected result type: %T\n", response.Result) + return + } + fmt.Println("Block height:", blockHeight) + + // Output: + // Block height: 350001 +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/export_test.go b/vendor/github.com/btcsuite/btcd/btcjson/export_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e0246fc2220a175dda4c753be399e43fd0f31d93 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/export_test.go @@ -0,0 +1,48 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +// TstHighestUsageFlagBit makes the internal highestUsageFlagBit parameter +// available to the test package. +var TstHighestUsageFlagBit = highestUsageFlagBit + +// TstNumErrorCodes makes the internal numErrorCodes parameter available to the +// test package. +var TstNumErrorCodes = numErrorCodes + +// TstAssignField makes the internal assignField function available to the test +// package. +var TstAssignField = assignField + +// TstFieldUsage makes the internal fieldUsage function available to the test +// package. +var TstFieldUsage = fieldUsage + +// TstReflectTypeToJSONType makes the internal reflectTypeToJSONType function +// available to the test package. +var TstReflectTypeToJSONType = reflectTypeToJSONType + +// TstResultStructHelp makes the internal resultStructHelp function available to +// the test package. +var TstResultStructHelp = resultStructHelp + +// TstReflectTypeToJSONExample makes the internal reflectTypeToJSONExample +// function available to the test package. +var TstReflectTypeToJSONExample = reflectTypeToJSONExample + +// TstResultTypeHelp makes the internal resultTypeHelp function available to the +// test package. +var TstResultTypeHelp = resultTypeHelp + +// TstArgHelp makes the internal argHelp function available to the test package. +var TstArgHelp = argHelp + +// TestMethodHelp makes the internal methodHelp function available to the test +// package. +var TestMethodHelp = methodHelp + +// TstIsValidResultType makes the internal isValidResultType function available +// to the test package. +var TstIsValidResultType = isValidResultType diff --git a/vendor/github.com/btcsuite/btcd/btcjson/help.go b/vendor/github.com/btcsuite/btcd/btcjson/help.go new file mode 100644 index 0000000000000000000000000000000000000000..113960fd5b91160e8cfbf446dc5800ccbfa8654c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/help.go @@ -0,0 +1,562 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +import ( + "bytes" + "fmt" + "reflect" + "strings" + "text/tabwriter" +) + +// baseHelpDescs house the various help labels, types, and example values used +// when generating help. The per-command synopsis, field descriptions, +// conditions, and result descriptions are to be provided by the caller. +var baseHelpDescs = map[string]string{ + // Misc help labels and output. + "help-arguments": "Arguments", + "help-arguments-none": "None", + "help-result": "Result", + "help-result-nothing": "Nothing", + "help-default": "default", + "help-optional": "optional", + "help-required": "required", + + // JSON types. + "json-type-numeric": "numeric", + "json-type-string": "string", + "json-type-bool": "boolean", + "json-type-array": "array of ", + "json-type-object": "object", + "json-type-value": "value", + + // JSON examples. + "json-example-string": "value", + "json-example-bool": "true|false", + "json-example-map-data": "data", + "json-example-unknown": "unknown", +} + +// descLookupFunc is a function which is used to lookup a description given +// a key. +type descLookupFunc func(string) string + +// reflectTypeToJSONType returns a string that represents the JSON type +// associated with the provided Go type. +func reflectTypeToJSONType(xT descLookupFunc, rt reflect.Type) string { + kind := rt.Kind() + if isNumeric(kind) { + return xT("json-type-numeric") + } + + switch kind { + case reflect.String: + return xT("json-type-string") + + case reflect.Bool: + return xT("json-type-bool") + + case reflect.Array, reflect.Slice: + return xT("json-type-array") + reflectTypeToJSONType(xT, + rt.Elem()) + + case reflect.Struct: + return xT("json-type-object") + + case reflect.Map: + return xT("json-type-object") + } + + return xT("json-type-value") +} + +// resultStructHelp returns a slice of strings containing the result help output +// for a struct. Each line makes use of tabs to separate the relevant pieces so +// a tabwriter can be used later to line everything up. The descriptions are +// pulled from the active help descriptions map based on the lowercase version +// of the provided reflect type and json name (or the lowercase version of the +// field name if no json tag was specified). +func resultStructHelp(xT descLookupFunc, rt reflect.Type, indentLevel int) []string { + indent := strings.Repeat(" ", indentLevel) + typeName := strings.ToLower(rt.Name()) + + // Generate the help for each of the fields in the result struct. + numField := rt.NumField() + results := make([]string, 0, numField) + for i := 0; i < numField; i++ { + rtf := rt.Field(i) + + // The field name to display is the json name when it's + // available, otherwise use the lowercase field name. + var fieldName string + if tag := rtf.Tag.Get("json"); tag != "" { + fieldName = strings.Split(tag, ",")[0] + } else { + fieldName = strings.ToLower(rtf.Name) + } + + // Deference pointer if needed. + rtfType := rtf.Type + if rtfType.Kind() == reflect.Ptr { + rtfType = rtf.Type.Elem() + } + + // Generate the JSON example for the result type of this struct + // field. When it is a complex type, examine the type and + // adjust the opening bracket and brace combination accordingly. + fieldType := reflectTypeToJSONType(xT, rtfType) + fieldDescKey := typeName + "-" + fieldName + fieldExamples, isComplex := reflectTypeToJSONExample(xT, + rtfType, indentLevel, fieldDescKey) + if isComplex { + var brace string + kind := rtfType.Kind() + if kind == reflect.Array || kind == reflect.Slice { + brace = "[{" + } else { + brace = "{" + } + result := fmt.Sprintf("%s\"%s\": %s\t(%s)\t%s", indent, + fieldName, brace, fieldType, xT(fieldDescKey)) + results = append(results, result) + for _, example := range fieldExamples { + results = append(results, example) + } + } else { + result := fmt.Sprintf("%s\"%s\": %s,\t(%s)\t%s", indent, + fieldName, fieldExamples[0], fieldType, + xT(fieldDescKey)) + results = append(results, result) + } + } + + return results +} + +// reflectTypeToJSONExample generates example usage in the format used by the +// help output. It handles arrays, slices and structs recursively. The output +// is returned as a slice of lines so the final help can be nicely aligned via +// a tab writer. A bool is also returned which specifies whether or not the +// type results in a complex JSON object since they need to be handled +// differently. +func reflectTypeToJSONExample(xT descLookupFunc, rt reflect.Type, indentLevel int, fieldDescKey string) ([]string, bool) { + // Indirect pointer if needed. + if rt.Kind() == reflect.Ptr { + rt = rt.Elem() + } + kind := rt.Kind() + if isNumeric(kind) { + if kind == reflect.Float32 || kind == reflect.Float64 { + return []string{"n.nnn"}, false + } + + return []string{"n"}, false + } + + switch kind { + case reflect.String: + return []string{`"` + xT("json-example-string") + `"`}, false + + case reflect.Bool: + return []string{xT("json-example-bool")}, false + + case reflect.Struct: + indent := strings.Repeat(" ", indentLevel) + results := resultStructHelp(xT, rt, indentLevel+1) + + // An opening brace is needed for the first indent level. For + // all others, it will be included as a part of the previous + // field. + if indentLevel == 0 { + newResults := make([]string, len(results)+1) + newResults[0] = "{" + copy(newResults[1:], results) + results = newResults + } + + // The closing brace has a comma after it except for the first + // indent level. The final tabs are necessary so the tab writer + // lines things up properly. + closingBrace := indent + "}" + if indentLevel > 0 { + closingBrace += "," + } + results = append(results, closingBrace+"\t\t") + return results, true + + case reflect.Array, reflect.Slice: + results, isComplex := reflectTypeToJSONExample(xT, rt.Elem(), + indentLevel, fieldDescKey) + + // When the result is complex, it is because this is an array of + // objects. + if isComplex { + // When this is at indent level zero, there is no + // previous field to house the opening array bracket, so + // replace the opening object brace with the array + // syntax. Also, replace the final closing object brace + // with the variadiac array closing syntax. + indent := strings.Repeat(" ", indentLevel) + if indentLevel == 0 { + results[0] = indent + "[{" + results[len(results)-1] = indent + "},...]" + return results, true + } + + // At this point, the indent level is greater than 0, so + // the opening array bracket and object brace are + // already a part of the previous field. However, the + // closing entry is a simple object brace, so replace it + // with the variadiac array closing syntax. The final + // tabs are necessary so the tab writer lines things up + // properly. + results[len(results)-1] = indent + "},...],\t\t" + return results, true + } + + // It's an array of primitives, so return the formatted text + // accordingly. + return []string{fmt.Sprintf("[%s,...]", results[0])}, false + + case reflect.Map: + indent := strings.Repeat(" ", indentLevel) + results := make([]string, 0, 3) + + // An opening brace is needed for the first indent level. For + // all others, it will be included as a part of the previous + // field. + if indentLevel == 0 { + results = append(results, indent+"{") + } + + // Maps are a bit special in that they need to have the key, + // value, and description of the object entry specifically + // called out. + innerIndent := strings.Repeat(" ", indentLevel+1) + result := fmt.Sprintf("%s%q: %s, (%s) %s", innerIndent, + xT(fieldDescKey+"--key"), xT(fieldDescKey+"--value"), + reflectTypeToJSONType(xT, rt), xT(fieldDescKey+"--desc")) + results = append(results, result) + results = append(results, innerIndent+"...") + + results = append(results, indent+"}") + return results, true + } + + return []string{xT("json-example-unknown")}, false +} + +// resultTypeHelp generates and returns formatted help for the provided result +// type. +func resultTypeHelp(xT descLookupFunc, rt reflect.Type, fieldDescKey string) string { + // Generate the JSON example for the result type. + results, isComplex := reflectTypeToJSONExample(xT, rt, 0, fieldDescKey) + + // When this is a primitive type, add the associated JSON type and + // result description into the final string, format it accordingly, + // and return it. + if !isComplex { + return fmt.Sprintf("%s (%s) %s", results[0], + reflectTypeToJSONType(xT, rt), xT(fieldDescKey)) + } + + // At this point, this is a complex type that already has the JSON types + // and descriptions in the results. Thus, use a tab writer to nicely + // align the help text. + var formatted bytes.Buffer + w := new(tabwriter.Writer) + w.Init(&formatted, 0, 4, 1, ' ', 0) + for i, text := range results { + if i == len(results)-1 { + fmt.Fprintf(w, text) + } else { + fmt.Fprintln(w, text) + } + } + w.Flush() + return formatted.String() +} + +// argTypeHelp returns the type of provided command argument as a string in the +// format used by the help output. In particular, it includes the JSON type +// (boolean, numeric, string, array, object) along with optional and the default +// value if applicable. +func argTypeHelp(xT descLookupFunc, structField reflect.StructField, defaultVal *reflect.Value) string { + // Indirect the pointer if needed and track if it's an optional field. + fieldType := structField.Type + var isOptional bool + if fieldType.Kind() == reflect.Ptr { + fieldType = fieldType.Elem() + isOptional = true + } + + // When there is a default value, it must also be a pointer due to the + // rules enforced by RegisterCmd. + if defaultVal != nil { + indirect := defaultVal.Elem() + defaultVal = &indirect + } + + // Convert the field type to a JSON type. + details := make([]string, 0, 3) + details = append(details, reflectTypeToJSONType(xT, fieldType)) + + // Add optional and default value to the details if needed. + if isOptional { + details = append(details, xT("help-optional")) + + // Add the default value if there is one. This is only checked + // when the field is optional since a non-optional field can't + // have a default value. + if defaultVal != nil { + val := defaultVal.Interface() + if defaultVal.Kind() == reflect.String { + val = fmt.Sprintf(`"%s"`, val) + } + str := fmt.Sprintf("%s=%v", xT("help-default"), val) + details = append(details, str) + } + } else { + details = append(details, xT("help-required")) + } + + return strings.Join(details, ", ") +} + +// argHelp generates and returns formatted help for the provided command. +func argHelp(xT descLookupFunc, rtp reflect.Type, defaults map[int]reflect.Value, method string) string { + // Return now if the command has no arguments. + rt := rtp.Elem() + numFields := rt.NumField() + if numFields == 0 { + return "" + } + + // Generate the help for each argument in the command. Several + // simplifying assumptions are made here because the RegisterCmd + // function has already rigorously enforced the layout. + args := make([]string, 0, numFields) + for i := 0; i < numFields; i++ { + rtf := rt.Field(i) + var defaultVal *reflect.Value + if defVal, ok := defaults[i]; ok { + defaultVal = &defVal + } + + fieldName := strings.ToLower(rtf.Name) + helpText := fmt.Sprintf("%d.\t%s\t(%s)\t%s", i+1, fieldName, + argTypeHelp(xT, rtf, defaultVal), + xT(method+"-"+fieldName)) + args = append(args, helpText) + + // For types which require a JSON object, or an array of JSON + // objects, generate the full syntax for the argument. + fieldType := rtf.Type + if fieldType.Kind() == reflect.Ptr { + fieldType = fieldType.Elem() + } + kind := fieldType.Kind() + switch kind { + case reflect.Struct: + fieldDescKey := fmt.Sprintf("%s-%s", method, fieldName) + resultText := resultTypeHelp(xT, fieldType, fieldDescKey) + args = append(args, resultText) + + case reflect.Map: + fieldDescKey := fmt.Sprintf("%s-%s", method, fieldName) + resultText := resultTypeHelp(xT, fieldType, fieldDescKey) + args = append(args, resultText) + + case reflect.Array, reflect.Slice: + fieldDescKey := fmt.Sprintf("%s-%s", method, fieldName) + if rtf.Type.Elem().Kind() == reflect.Struct { + resultText := resultTypeHelp(xT, fieldType, + fieldDescKey) + args = append(args, resultText) + } + } + } + + // Add argument names, types, and descriptions if there are any. Use a + // tab writer to nicely align the help text. + var formatted bytes.Buffer + w := new(tabwriter.Writer) + w.Init(&formatted, 0, 4, 1, ' ', 0) + for _, text := range args { + fmt.Fprintln(w, text) + } + w.Flush() + return formatted.String() +} + +// methodHelp generates and returns the help output for the provided command +// and method info. This is the main work horse for the exported MethodHelp +// function. +func methodHelp(xT descLookupFunc, rtp reflect.Type, defaults map[int]reflect.Value, method string, resultTypes []interface{}) string { + // Start off with the method usage and help synopsis. + help := fmt.Sprintf("%s\n\n%s\n", methodUsageText(rtp, defaults, method), + xT(method+"--synopsis")) + + // Generate the help for each argument in the command. + if argText := argHelp(xT, rtp, defaults, method); argText != "" { + help += fmt.Sprintf("\n%s:\n%s", xT("help-arguments"), + argText) + } else { + help += fmt.Sprintf("\n%s:\n%s\n", xT("help-arguments"), + xT("help-arguments-none")) + } + + // Generate the help text for each result type. + resultTexts := make([]string, 0, len(resultTypes)) + for i := range resultTypes { + rtp := reflect.TypeOf(resultTypes[i]) + fieldDescKey := fmt.Sprintf("%s--result%d", method, i) + if resultTypes[i] == nil { + resultText := xT("help-result-nothing") + resultTexts = append(resultTexts, resultText) + continue + } + + resultText := resultTypeHelp(xT, rtp.Elem(), fieldDescKey) + resultTexts = append(resultTexts, resultText) + } + + // Add result types and descriptions. When there is more than one + // result type, also add the condition which triggers it. + if len(resultTexts) > 1 { + for i, resultText := range resultTexts { + condKey := fmt.Sprintf("%s--condition%d", method, i) + help += fmt.Sprintf("\n%s (%s):\n%s\n", + xT("help-result"), xT(condKey), resultText) + } + } else if len(resultTexts) > 0 { + help += fmt.Sprintf("\n%s:\n%s\n", xT("help-result"), + resultTexts[0]) + } else { + help += fmt.Sprintf("\n%s:\n%s\n", xT("help-result"), + xT("help-result-nothing")) + } + return help +} + +// isValidResultType returns whether the passed reflect kind is one of the +// acceptable types for results. +func isValidResultType(kind reflect.Kind) bool { + if isNumeric(kind) { + return true + } + + switch kind { + case reflect.String, reflect.Struct, reflect.Array, reflect.Slice, + reflect.Bool, reflect.Map: + + return true + } + + return false +} + +// GenerateHelp generates and returns help output for the provided method and +// result types given a map to provide the appropriate keys for the method +// synopsis, field descriptions, conditions, and result descriptions. The +// method must be associated with a registered type. All commands provided by +// this package are registered by default. +// +// The resultTypes must be pointer-to-types which represent the specific types +// of values the command returns. For example, if the command only returns a +// boolean value, there should only be a single entry of (*bool)(nil). Note +// that each type must be a single pointer to the type. Therefore, it is +// recommended to simply pass a nil pointer cast to the appropriate type as +// previously shown. +// +// The provided descriptions map must contain all of the keys or an error will +// be returned which includes the missing key, or the final missing key when +// there is more than one key missing. The generated help in the case of such +// an error will use the key in place of the description. +// +// The following outlines the required keys: +// "<method>--synopsis" Synopsis for the command +// "<method>-<lowerfieldname>" Description for each command argument +// "<typename>-<lowerfieldname>" Description for each object field +// "<method>--condition<#>" Description for each result condition +// "<method>--result<#>" Description for each primitive result num +// +// Notice that the "special" keys synopsis, condition<#>, and result<#> are +// preceded by a double dash to ensure they don't conflict with field names. +// +// The condition keys are only required when there is more than on result type, +// and the result key for a given result type is only required if it's not an +// object. +// +// For example, consider the 'help' command itself. There are two possible +// returns depending on the provided parameters. So, the help would be +// generated by calling the function as follows: +// GenerateHelp("help", descs, (*string)(nil), (*string)(nil)). +// +// The following keys would then be required in the provided descriptions map: +// +// "help--synopsis": "Returns a list of all commands or help for ...." +// "help-command": "The command to retrieve help for", +// "help--condition0": "no command provided" +// "help--condition1": "command specified" +// "help--result0": "List of commands" +// "help--result1": "Help for specified command" +func GenerateHelp(method string, descs map[string]string, resultTypes ...interface{}) (string, error) { + // Look up details about the provided method and error out if not + // registered. + registerLock.RLock() + rtp, ok := methodToConcreteType[method] + info := methodToInfo[method] + registerLock.RUnlock() + if !ok { + str := fmt.Sprintf("%q is not registered", method) + return "", makeError(ErrUnregisteredMethod, str) + } + + // Validate each result type is a pointer to a supported type (or nil). + for i, resultType := range resultTypes { + if resultType == nil { + continue + } + + rtp := reflect.TypeOf(resultType) + if rtp.Kind() != reflect.Ptr { + str := fmt.Sprintf("result #%d (%v) is not a pointer", + i, rtp.Kind()) + return "", makeError(ErrInvalidType, str) + } + + elemKind := rtp.Elem().Kind() + if !isValidResultType(elemKind) { + str := fmt.Sprintf("result #%d (%v) is not an allowed "+ + "type", i, elemKind) + return "", makeError(ErrInvalidType, str) + } + } + + // Create a closure for the description lookup function which falls back + // to the base help descritptions map for unrecognized keys and tracks + // and missing keys. + var missingKey string + xT := func(key string) string { + if desc, ok := descs[key]; ok { + return desc + } + if desc, ok := baseHelpDescs[key]; ok { + return desc + } + + missingKey = key + return key + } + + // Generate and return the help for the method. + help := methodHelp(xT, rtp, info.defaults, method, resultTypes) + if missingKey != "" { + return help, makeError(ErrMissingDescription, missingKey) + } + return help, nil +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/help_test.go b/vendor/github.com/btcsuite/btcd/btcjson/help_test.go new file mode 100644 index 0000000000000000000000000000000000000000..265e0c2c60a0288d73add32ad1589920e06ef28c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/help_test.go @@ -0,0 +1,737 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestHelpReflectInternals ensures the various help functions which deal with +// reflect types work as expected for various Go types. +func TestHelpReflectInternals(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + reflectType reflect.Type + indentLevel int + key string + examples []string + isComplex bool + help string + isInvalid bool + }{ + { + name: "int", + reflectType: reflect.TypeOf(int(0)), + key: "json-type-numeric", + examples: []string{"n"}, + help: "n (json-type-numeric) fdk", + }, + { + name: "*int", + reflectType: reflect.TypeOf((*int)(nil)), + key: "json-type-value", + examples: []string{"n"}, + help: "n (json-type-value) fdk", + isInvalid: true, + }, + { + name: "int8", + reflectType: reflect.TypeOf(int8(0)), + key: "json-type-numeric", + examples: []string{"n"}, + help: "n (json-type-numeric) fdk", + }, + { + name: "int16", + reflectType: reflect.TypeOf(int16(0)), + key: "json-type-numeric", + examples: []string{"n"}, + help: "n (json-type-numeric) fdk", + }, + { + name: "int32", + reflectType: reflect.TypeOf(int32(0)), + key: "json-type-numeric", + examples: []string{"n"}, + help: "n (json-type-numeric) fdk", + }, + { + name: "int64", + reflectType: reflect.TypeOf(int64(0)), + key: "json-type-numeric", + examples: []string{"n"}, + help: "n (json-type-numeric) fdk", + }, + { + name: "uint", + reflectType: reflect.TypeOf(uint(0)), + key: "json-type-numeric", + examples: []string{"n"}, + help: "n (json-type-numeric) fdk", + }, + { + name: "uint8", + reflectType: reflect.TypeOf(uint8(0)), + key: "json-type-numeric", + examples: []string{"n"}, + help: "n (json-type-numeric) fdk", + }, + { + name: "uint16", + reflectType: reflect.TypeOf(uint16(0)), + key: "json-type-numeric", + examples: []string{"n"}, + help: "n (json-type-numeric) fdk", + }, + { + name: "uint32", + reflectType: reflect.TypeOf(uint32(0)), + key: "json-type-numeric", + examples: []string{"n"}, + help: "n (json-type-numeric) fdk", + }, + { + name: "uint64", + reflectType: reflect.TypeOf(uint64(0)), + key: "json-type-numeric", + examples: []string{"n"}, + help: "n (json-type-numeric) fdk", + }, + { + name: "float32", + reflectType: reflect.TypeOf(float32(0)), + key: "json-type-numeric", + examples: []string{"n.nnn"}, + help: "n.nnn (json-type-numeric) fdk", + }, + { + name: "float64", + reflectType: reflect.TypeOf(float64(0)), + key: "json-type-numeric", + examples: []string{"n.nnn"}, + help: "n.nnn (json-type-numeric) fdk", + }, + { + name: "string", + reflectType: reflect.TypeOf(""), + key: "json-type-string", + examples: []string{`"json-example-string"`}, + help: "\"json-example-string\" (json-type-string) fdk", + }, + { + name: "bool", + reflectType: reflect.TypeOf(true), + key: "json-type-bool", + examples: []string{"json-example-bool"}, + help: "json-example-bool (json-type-bool) fdk", + }, + { + name: "array of int", + reflectType: reflect.TypeOf([1]int{0}), + key: "json-type-arrayjson-type-numeric", + examples: []string{"[n,...]"}, + help: "[n,...] (json-type-arrayjson-type-numeric) fdk", + }, + { + name: "slice of int", + reflectType: reflect.TypeOf([]int{0}), + key: "json-type-arrayjson-type-numeric", + examples: []string{"[n,...]"}, + help: "[n,...] (json-type-arrayjson-type-numeric) fdk", + }, + { + name: "struct", + reflectType: reflect.TypeOf(struct{}{}), + key: "json-type-object", + examples: []string{"{", "}\t\t"}, + isComplex: true, + help: "{\n} ", + }, + { + name: "struct indent level 1", + reflectType: reflect.TypeOf(struct{ field int }{}), + indentLevel: 1, + key: "json-type-object", + examples: []string{ + " \"field\": n,\t(json-type-numeric)\t-field", + " },\t\t", + }, + help: "{\n" + + " \"field\": n, (json-type-numeric) -field\n" + + "} ", + isComplex: true, + }, + { + name: "array of struct indent level 0", + reflectType: func() reflect.Type { + type s struct { + field int + } + return reflect.TypeOf([]s{}) + }(), + key: "json-type-arrayjson-type-object", + examples: []string{ + "[{", + " \"field\": n,\t(json-type-numeric)\ts-field", + "},...]", + }, + help: "[{\n" + + " \"field\": n, (json-type-numeric) s-field\n" + + "},...]", + isComplex: true, + }, + { + name: "array of struct indent level 1", + reflectType: func() reflect.Type { + type s struct { + field int + } + return reflect.TypeOf([]s{}) + }(), + indentLevel: 1, + key: "json-type-arrayjson-type-object", + examples: []string{ + " \"field\": n,\t(json-type-numeric)\ts-field", + " },...],\t\t", + }, + help: "[{\n" + + " \"field\": n, (json-type-numeric) s-field\n" + + "},...]", + isComplex: true, + }, + { + name: "map", + reflectType: reflect.TypeOf(map[string]string{}), + key: "json-type-object", + examples: []string{"{", + " \"fdk--key\": fdk--value, (json-type-object) fdk--desc", + " ...", "}", + }, + help: "{\n" + + " \"fdk--key\": fdk--value, (json-type-object) fdk--desc\n" + + " ...\n" + + "}", + isComplex: true, + }, + { + name: "complex", + reflectType: reflect.TypeOf(complex64(0)), + key: "json-type-value", + examples: []string{"json-example-unknown"}, + help: "json-example-unknown (json-type-value) fdk", + isInvalid: true, + }, + } + + xT := func(key string) string { + return key + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Ensure the description key is the expected value. + key := btcjson.TstReflectTypeToJSONType(xT, test.reflectType) + if key != test.key { + t.Errorf("Test #%d (%s) unexpected key - got: %v, "+ + "want: %v", i, test.name, key, test.key) + continue + } + + // Ensure the generated example is as expected. + examples, isComplex := btcjson.TstReflectTypeToJSONExample(xT, + test.reflectType, test.indentLevel, "fdk") + if isComplex != test.isComplex { + t.Errorf("Test #%d (%s) unexpected isComplex - got: %v, "+ + "want: %v", i, test.name, isComplex, + test.isComplex) + continue + } + if len(examples) != len(test.examples) { + t.Errorf("Test #%d (%s) unexpected result length - "+ + "got: %v, want: %v", i, test.name, len(examples), + len(test.examples)) + continue + } + for j, example := range examples { + if example != test.examples[j] { + t.Errorf("Test #%d (%s) example #%d unexpected "+ + "example - got: %v, want: %v", i, + test.name, j, example, test.examples[j]) + continue + } + } + + // Ensure the generated result type help is as expected. + helpText := btcjson.TstResultTypeHelp(xT, test.reflectType, "fdk") + if helpText != test.help { + t.Errorf("Test #%d (%s) unexpected result help - "+ + "got: %v, want: %v", i, test.name, helpText, + test.help) + continue + } + + isValid := btcjson.TstIsValidResultType(test.reflectType.Kind()) + if isValid != !test.isInvalid { + t.Errorf("Test #%d (%s) unexpected result type validity "+ + "- got: %v", i, test.name, isValid) + continue + } + } +} + +// TestResultStructHelp ensures the expected help text format is returned for +// various Go struct types. +func TestResultStructHelp(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + reflectType reflect.Type + expected []string + }{ + { + name: "empty struct", + reflectType: func() reflect.Type { + type s struct{} + return reflect.TypeOf(s{}) + }(), + expected: nil, + }, + { + name: "struct with primitive field", + reflectType: func() reflect.Type { + type s struct { + field int + } + return reflect.TypeOf(s{}) + }(), + expected: []string{ + "\"field\": n,\t(json-type-numeric)\ts-field", + }, + }, + { + name: "struct with primitive field and json tag", + reflectType: func() reflect.Type { + type s struct { + Field int `json:"f"` + } + return reflect.TypeOf(s{}) + }(), + expected: []string{ + "\"f\": n,\t(json-type-numeric)\ts-f", + }, + }, + { + name: "struct with array of primitive field", + reflectType: func() reflect.Type { + type s struct { + field []int + } + return reflect.TypeOf(s{}) + }(), + expected: []string{ + "\"field\": [n,...],\t(json-type-arrayjson-type-numeric)\ts-field", + }, + }, + { + name: "struct with sub-struct field", + reflectType: func() reflect.Type { + type s2 struct { + subField int + } + type s struct { + field s2 + } + return reflect.TypeOf(s{}) + }(), + expected: []string{ + "\"field\": {\t(json-type-object)\ts-field", + "{", + " \"subfield\": n,\t(json-type-numeric)\ts2-subfield", + "}\t\t", + }, + }, + { + name: "struct with sub-struct field pointer", + reflectType: func() reflect.Type { + type s2 struct { + subField int + } + type s struct { + field *s2 + } + return reflect.TypeOf(s{}) + }(), + expected: []string{ + "\"field\": {\t(json-type-object)\ts-field", + "{", + " \"subfield\": n,\t(json-type-numeric)\ts2-subfield", + "}\t\t", + }, + }, + { + name: "struct with array of structs field", + reflectType: func() reflect.Type { + type s2 struct { + subField int + } + type s struct { + field []s2 + } + return reflect.TypeOf(s{}) + }(), + expected: []string{ + "\"field\": [{\t(json-type-arrayjson-type-object)\ts-field", + "[{", + " \"subfield\": n,\t(json-type-numeric)\ts2-subfield", + "},...]", + }, + }, + } + + xT := func(key string) string { + return key + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + results := btcjson.TstResultStructHelp(xT, test.reflectType, 0) + if len(results) != len(test.expected) { + t.Errorf("Test #%d (%s) unexpected result length - "+ + "got: %v, want: %v", i, test.name, len(results), + len(test.expected)) + continue + } + for j, result := range results { + if result != test.expected[j] { + t.Errorf("Test #%d (%s) result #%d unexpected "+ + "result - got: %v, want: %v", i, + test.name, j, result, test.expected[j]) + continue + } + } + } +} + +// TestHelpArgInternals ensures the various help functions which deal with +// arguments work as expected for various argument types. +func TestHelpArgInternals(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + method string + reflectType reflect.Type + defaults map[int]reflect.Value + help string + }{ + { + name: "command with no args", + method: "test", + reflectType: func() reflect.Type { + type s struct{} + return reflect.TypeOf((*s)(nil)) + }(), + defaults: nil, + help: "", + }, + { + name: "command with one required arg", + method: "test", + reflectType: func() reflect.Type { + type s struct { + Field int + } + return reflect.TypeOf((*s)(nil)) + }(), + defaults: nil, + help: "1. field (json-type-numeric, help-required) test-field\n", + }, + { + name: "command with one optional arg, no default", + method: "test", + reflectType: func() reflect.Type { + type s struct { + Optional *int + } + return reflect.TypeOf((*s)(nil)) + }(), + defaults: nil, + help: "1. optional (json-type-numeric, help-optional) test-optional\n", + }, + { + name: "command with one optional arg with default", + method: "test", + reflectType: func() reflect.Type { + type s struct { + Optional *string + } + return reflect.TypeOf((*s)(nil)) + }(), + defaults: func() map[int]reflect.Value { + defVal := "test" + return map[int]reflect.Value{ + 0: reflect.ValueOf(&defVal), + } + }(), + help: "1. optional (json-type-string, help-optional, help-default=\"test\") test-optional\n", + }, + { + name: "command with struct field", + method: "test", + reflectType: func() reflect.Type { + type s2 struct { + F int8 + } + type s struct { + Field s2 + } + return reflect.TypeOf((*s)(nil)) + }(), + defaults: nil, + help: "1. field (json-type-object, help-required) test-field\n" + + "{\n" + + " \"f\": n, (json-type-numeric) s2-f\n" + + "} \n", + }, + { + name: "command with map field", + method: "test", + reflectType: func() reflect.Type { + type s struct { + Field map[string]float64 + } + return reflect.TypeOf((*s)(nil)) + }(), + defaults: nil, + help: "1. field (json-type-object, help-required) test-field\n" + + "{\n" + + " \"test-field--key\": test-field--value, (json-type-object) test-field--desc\n" + + " ...\n" + + "}\n", + }, + { + name: "command with slice of primitives field", + method: "test", + reflectType: func() reflect.Type { + type s struct { + Field []int64 + } + return reflect.TypeOf((*s)(nil)) + }(), + defaults: nil, + help: "1. field (json-type-arrayjson-type-numeric, help-required) test-field\n", + }, + { + name: "command with slice of structs field", + method: "test", + reflectType: func() reflect.Type { + type s2 struct { + F int64 + } + type s struct { + Field []s2 + } + return reflect.TypeOf((*s)(nil)) + }(), + defaults: nil, + help: "1. field (json-type-arrayjson-type-object, help-required) test-field\n" + + "[{\n" + + " \"f\": n, (json-type-numeric) s2-f\n" + + "},...]\n", + }, + } + + xT := func(key string) string { + return key + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + help := btcjson.TstArgHelp(xT, test.reflectType, test.defaults, + test.method) + if help != test.help { + t.Errorf("Test #%d (%s) unexpected help - got:\n%v\n"+ + "want:\n%v", i, test.name, help, test.help) + continue + } + } +} + +// TestMethodHelp ensures the method help function works as expected for various +// command structs. +func TestMethodHelp(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + method string + reflectType reflect.Type + defaults map[int]reflect.Value + resultTypes []interface{} + help string + }{ + { + name: "command with no args or results", + method: "test", + reflectType: func() reflect.Type { + type s struct{} + return reflect.TypeOf((*s)(nil)) + }(), + help: "test\n\ntest--synopsis\n\n" + + "help-arguments:\nhelp-arguments-none\n\n" + + "help-result:\nhelp-result-nothing\n", + }, + { + name: "command with no args and one primitive result", + method: "test", + reflectType: func() reflect.Type { + type s struct{} + return reflect.TypeOf((*s)(nil)) + }(), + resultTypes: []interface{}{(*int64)(nil)}, + help: "test\n\ntest--synopsis\n\n" + + "help-arguments:\nhelp-arguments-none\n\n" + + "help-result:\nn (json-type-numeric) test--result0\n", + }, + { + name: "command with no args and two results", + method: "test", + reflectType: func() reflect.Type { + type s struct{} + return reflect.TypeOf((*s)(nil)) + }(), + resultTypes: []interface{}{(*int64)(nil), nil}, + help: "test\n\ntest--synopsis\n\n" + + "help-arguments:\nhelp-arguments-none\n\n" + + "help-result (test--condition0):\nn (json-type-numeric) test--result0\n\n" + + "help-result (test--condition1):\nhelp-result-nothing\n", + }, + { + name: "command with primitive arg and no results", + method: "test", + reflectType: func() reflect.Type { + type s struct { + Field bool + } + return reflect.TypeOf((*s)(nil)) + }(), + help: "test field\n\ntest--synopsis\n\n" + + "help-arguments:\n1. field (json-type-bool, help-required) test-field\n\n" + + "help-result:\nhelp-result-nothing\n", + }, + { + name: "command with primitive optional and no results", + method: "test", + reflectType: func() reflect.Type { + type s struct { + Field *bool + } + return reflect.TypeOf((*s)(nil)) + }(), + help: "test (field)\n\ntest--synopsis\n\n" + + "help-arguments:\n1. field (json-type-bool, help-optional) test-field\n\n" + + "help-result:\nhelp-result-nothing\n", + }, + } + + xT := func(key string) string { + return key + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + help := btcjson.TestMethodHelp(xT, test.reflectType, + test.defaults, test.method, test.resultTypes) + if help != test.help { + t.Errorf("Test #%d (%s) unexpected help - got:\n%v\n"+ + "want:\n%v", i, test.name, help, test.help) + continue + } + } +} + +// TestGenerateHelpErrors ensures the GenerateHelp function returns the expected +// errors. +func TestGenerateHelpErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + method string + resultTypes []interface{} + err btcjson.Error + }{ + { + name: "unregistered command", + method: "boguscommand", + err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod}, + }, + { + name: "non-pointer result type", + method: "help", + resultTypes: []interface{}{0}, + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "invalid result type", + method: "help", + resultTypes: []interface{}{(*complex64)(nil)}, + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "missing description", + method: "help", + resultTypes: []interface{}{(*string)(nil), nil}, + err: btcjson.Error{ErrorCode: btcjson.ErrMissingDescription}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + _, err := btcjson.GenerateHelp(test.method, nil, + test.resultTypes...) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+ + "want %T", i, test.name, err, test.err) + continue + } + gotErrorCode := err.(btcjson.Error).ErrorCode + if gotErrorCode != test.err.ErrorCode { + t.Errorf("Test #%d (%s) mismatched error code - got "+ + "%v (%v), want %v", i, test.name, gotErrorCode, + err, test.err.ErrorCode) + continue + } + } +} + +// TestGenerateHelp performs a very basic test to ensure GenerateHelp is working +// as expected. The internal are testd much more thoroughly in other tests, so +// there is no need to add more tests here. +func TestGenerateHelp(t *testing.T) { + t.Parallel() + + descs := map[string]string{ + "help--synopsis": "test", + "help-command": "test", + } + help, err := btcjson.GenerateHelp("help", descs) + if err != nil { + t.Fatalf("GenerateHelp: unexpected error: %v", err) + } + wantHelp := "help (\"command\")\n\n" + + "test\n\nArguments:\n1. command (string, optional) test\n\n" + + "Result:\nNothing\n" + if help != wantHelp { + t.Fatalf("GenerateHelp: unexpected help - got\n%v\nwant\n%v", + help, wantHelp) + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/helpers.go b/vendor/github.com/btcsuite/btcd/btcjson/helpers.go new file mode 100644 index 0000000000000000000000000000000000000000..d9b452e7c3a728c0e94dbca842efa452d63aa208 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/helpers.go @@ -0,0 +1,77 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +// Bool is a helper routine that allocates a new bool value to store v and +// returns a pointer to it. This is useful when assigning optional parameters. +func Bool(v bool) *bool { + p := new(bool) + *p = v + return p +} + +// Int is a helper routine that allocates a new int value to store v and +// returns a pointer to it. This is useful when assigning optional parameters. +func Int(v int) *int { + p := new(int) + *p = v + return p +} + +// Uint is a helper routine that allocates a new uint value to store v and +// returns a pointer to it. This is useful when assigning optional parameters. +func Uint(v uint) *uint { + p := new(uint) + *p = v + return p +} + +// Int32 is a helper routine that allocates a new int32 value to store v and +// returns a pointer to it. This is useful when assigning optional parameters. +func Int32(v int32) *int32 { + p := new(int32) + *p = v + return p +} + +// Uint32 is a helper routine that allocates a new uint32 value to store v and +// returns a pointer to it. This is useful when assigning optional parameters. +func Uint32(v uint32) *uint32 { + p := new(uint32) + *p = v + return p +} + +// Int64 is a helper routine that allocates a new int64 value to store v and +// returns a pointer to it. This is useful when assigning optional parameters. +func Int64(v int64) *int64 { + p := new(int64) + *p = v + return p +} + +// Uint64 is a helper routine that allocates a new uint64 value to store v and +// returns a pointer to it. This is useful when assigning optional parameters. +func Uint64(v uint64) *uint64 { + p := new(uint64) + *p = v + return p +} + +// Float64 is a helper routine that allocates a new float64 value to store v and +// returns a pointer to it. This is useful when assigning optional parameters. +func Float64(v float64) *float64 { + p := new(float64) + *p = v + return p +} + +// String is a helper routine that allocates a new string value to store v and +// returns a pointer to it. This is useful when assigning optional parameters. +func String(v string) *string { + p := new(string) + *p = v + return p +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/helpers_test.go b/vendor/github.com/btcsuite/btcd/btcjson/helpers_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7bcaf4bcc9fe2492c368378213930c53fe509248 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/helpers_test.go @@ -0,0 +1,115 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestHelpers tests the various helper functions which create pointers to +// primitive types. +func TestHelpers(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + f func() interface{} + expected interface{} + }{ + { + name: "bool", + f: func() interface{} { + return btcjson.Bool(true) + }, + expected: func() interface{} { + val := true + return &val + }(), + }, + { + name: "int", + f: func() interface{} { + return btcjson.Int(5) + }, + expected: func() interface{} { + val := int(5) + return &val + }(), + }, + { + name: "uint", + f: func() interface{} { + return btcjson.Uint(5) + }, + expected: func() interface{} { + val := uint(5) + return &val + }(), + }, + { + name: "int32", + f: func() interface{} { + return btcjson.Int32(5) + }, + expected: func() interface{} { + val := int32(5) + return &val + }(), + }, + { + name: "uint32", + f: func() interface{} { + return btcjson.Uint32(5) + }, + expected: func() interface{} { + val := uint32(5) + return &val + }(), + }, + { + name: "int64", + f: func() interface{} { + return btcjson.Int64(5) + }, + expected: func() interface{} { + val := int64(5) + return &val + }(), + }, + { + name: "uint64", + f: func() interface{} { + return btcjson.Uint64(5) + }, + expected: func() interface{} { + val := uint64(5) + return &val + }(), + }, + { + name: "string", + f: func() interface{} { + return btcjson.String("abc") + }, + expected: func() interface{} { + val := "abc" + return &val + }(), + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.f() + if !reflect.DeepEqual(result, test.expected) { + t.Errorf("Test #%d (%s) unexpected value - got %v, "+ + "want %v", i, test.name, result, test.expected) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/jsonrpc.go b/vendor/github.com/btcsuite/btcd/btcjson/jsonrpc.go new file mode 100644 index 0000000000000000000000000000000000000000..e99d9f42655e6758d4324bce0a4c8237ad1ed8d3 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/jsonrpc.go @@ -0,0 +1,150 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +import ( + "encoding/json" + "fmt" +) + +// RPCErrorCode represents an error code to be used as a part of an RPCError +// which is in turn used in a JSON-RPC Response object. +// +// A specific type is used to help ensure the wrong errors aren't used. +type RPCErrorCode int + +// RPCError represents an error that is used as a part of a JSON-RPC Response +// object. +type RPCError struct { + Code RPCErrorCode `json:"code,omitempty"` + Message string `json:"message,omitempty"` +} + +// Guarantee RPCError satisifies the builtin error interface. +var _, _ error = RPCError{}, (*RPCError)(nil) + +// Error returns a string describing the RPC error. This satisifies the +// builtin error interface. +func (e RPCError) Error() string { + return fmt.Sprintf("%d: %s", e.Code, e.Message) +} + +// NewRPCError constructs and returns a new JSON-RPC error that is suitable +// for use in a JSON-RPC Response object. +func NewRPCError(code RPCErrorCode, message string) *RPCError { + return &RPCError{ + Code: code, + Message: message, + } +} + +// IsValidIDType checks that the ID field (which can go in any of the JSON-RPC +// requests, responses, or notifications) is valid. JSON-RPC 1.0 allows any +// valid JSON type. JSON-RPC 2.0 (which bitcoind follows for some parts) only +// allows string, number, or null, so this function restricts the allowed types +// to that list. This function is only provided in case the caller is manually +// marshalling for some reason. The functions which accept an ID in this +// package already call this function to ensure the provided id is valid. +func IsValidIDType(id interface{}) bool { + switch id.(type) { + case int, int8, int16, int32, int64, + uint, uint8, uint16, uint32, uint64, + float32, float64, + string, + nil: + return true + default: + return false + } +} + +// Request is a type for raw JSON-RPC 1.0 requests. The Method field identifies +// the specific command type which in turns leads to different parameters. +// Callers typically will not use this directly since this package provides a +// statically typed command infrastructure which handles creation of these +// requests, however this struct it being exported in case the caller wants to +// construct raw requests for some reason. +type Request struct { + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` + Params []json.RawMessage `json:"params"` + ID interface{} `json:"id"` +} + +// NewRequest returns a new JSON-RPC 1.0 request object given the provided id, +// method, and parameters. The parameters are marshalled into a json.RawMessage +// for the Params field of the returned request object. This function is only +// provided in case the caller wants to construct raw requests for some reason. +// +// Typically callers will instead want to create a registered concrete command +// type with the NewCmd or New<Foo>Cmd functions and call the MarshalCmd +// function with that command to generate the marshalled JSON-RPC request. +func NewRequest(id interface{}, method string, params []interface{}) (*Request, error) { + if !IsValidIDType(id) { + str := fmt.Sprintf("the id of type '%T' is invalid", id) + return nil, makeError(ErrInvalidType, str) + } + + rawParams := make([]json.RawMessage, 0, len(params)) + for _, param := range params { + marshalledParam, err := json.Marshal(param) + if err != nil { + return nil, err + } + rawMessage := json.RawMessage(marshalledParam) + rawParams = append(rawParams, rawMessage) + } + + return &Request{ + Jsonrpc: "1.0", + ID: id, + Method: method, + Params: rawParams, + }, nil +} + +// Response is the general form of a JSON-RPC response. The type of the Result +// field varies from one command to the next, so it is implemented as an +// interface. The ID field has to be a pointer for Go to put a null in it when +// empty. +type Response struct { + Result json.RawMessage `json:"result"` + Error *RPCError `json:"error"` + ID *interface{} `json:"id"` +} + +// NewResponse returns a new JSON-RPC response object given the provided id, +// marshalled result, and RPC error. This function is only provided in case the +// caller wants to construct raw responses for some reason. +// +// Typically callers will instead want to create the fully marshalled JSON-RPC +// response to send over the wire with the MarshalResponse function. +func NewResponse(id interface{}, marshalledResult []byte, rpcErr *RPCError) (*Response, error) { + if !IsValidIDType(id) { + str := fmt.Sprintf("the id of type '%T' is invalid", id) + return nil, makeError(ErrInvalidType, str) + } + + pid := &id + return &Response{ + Result: marshalledResult, + Error: rpcErr, + ID: pid, + }, nil +} + +// MarshalResponse marshals the passed id, result, and RPCError to a JSON-RPC +// response byte slice that is suitable for transmission to a JSON-RPC client. +func MarshalResponse(id interface{}, result interface{}, rpcErr *RPCError) ([]byte, error) { + marshalledResult, err := json.Marshal(result) + if err != nil { + return nil, err + } + response, err := NewResponse(id, marshalledResult, rpcErr) + if err != nil { + return nil, err + } + return json.Marshal(&response) +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/jsonrpc_test.go b/vendor/github.com/btcsuite/btcd/btcjson/jsonrpc_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7a5d75618ca12efa4f72d3f7f889a092880bdea7 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/jsonrpc_test.go @@ -0,0 +1,161 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestIsValidIDType ensures the IsValidIDType function behaves as expected. +func TestIsValidIDType(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + id interface{} + isValid bool + }{ + {"int", int(1), true}, + {"int8", int8(1), true}, + {"int16", int16(1), true}, + {"int32", int32(1), true}, + {"int64", int64(1), true}, + {"uint", uint(1), true}, + {"uint8", uint8(1), true}, + {"uint16", uint16(1), true}, + {"uint32", uint32(1), true}, + {"uint64", uint64(1), true}, + {"string", "1", true}, + {"nil", nil, true}, + {"float32", float32(1), true}, + {"float64", float64(1), true}, + {"bool", true, false}, + {"chan int", make(chan int), false}, + {"complex64", complex64(1), false}, + {"complex128", complex128(1), false}, + {"func", func() {}, false}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + if btcjson.IsValidIDType(test.id) != test.isValid { + t.Errorf("Test #%d (%s) valid mismatch - got %v, "+ + "want %v", i, test.name, !test.isValid, + test.isValid) + continue + } + } +} + +// TestMarshalResponse ensures the MarshalResponse function works as expected. +func TestMarshalResponse(t *testing.T) { + t.Parallel() + + testID := 1 + tests := []struct { + name string + result interface{} + jsonErr *btcjson.RPCError + expected []byte + }{ + { + name: "ordinary bool result with no error", + result: true, + jsonErr: nil, + expected: []byte(`{"result":true,"error":null,"id":1}`), + }, + { + name: "result with error", + result: nil, + jsonErr: func() *btcjson.RPCError { + return btcjson.NewRPCError(btcjson.ErrRPCBlockNotFound, "123 not found") + }(), + expected: []byte(`{"result":null,"error":{"code":-5,"message":"123 not found"},"id":1}`), + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + _, _ = i, test + marshalled, err := btcjson.MarshalResponse(testID, test.result, test.jsonErr) + if err != nil { + t.Errorf("Test #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !reflect.DeepEqual(marshalled, test.expected) { + t.Errorf("Test #%d (%s) mismatched result - got %s, "+ + "want %s", i, test.name, marshalled, + test.expected) + } + } +} + +// TestMiscErrors tests a few error conditions not covered elsewhere. +func TestMiscErrors(t *testing.T) { + t.Parallel() + + // Force an error in NewRequest by giving it a parameter type that is + // not supported. + _, err := btcjson.NewRequest(nil, "test", []interface{}{make(chan int)}) + if err == nil { + t.Error("NewRequest: did not receive error") + return + } + + // Force an error in MarshalResponse by giving it an id type that is not + // supported. + wantErr := btcjson.Error{ErrorCode: btcjson.ErrInvalidType} + _, err = btcjson.MarshalResponse(make(chan int), nil, nil) + if jerr, ok := err.(btcjson.Error); !ok || jerr.ErrorCode != wantErr.ErrorCode { + t.Errorf("MarshalResult: did not receive expected error - got "+ + "%v (%[1]T), want %v (%[2]T)", err, wantErr) + return + } + + // Force an error in MarshalResponse by giving it a result type that + // can't be marshalled. + _, err = btcjson.MarshalResponse(1, make(chan int), nil) + if _, ok := err.(*json.UnsupportedTypeError); !ok { + wantErr := &json.UnsupportedTypeError{} + t.Errorf("MarshalResult: did not receive expected error - got "+ + "%v (%[1]T), want %T", err, wantErr) + return + } +} + +// TestRPCError tests the error output for the RPCError type. +func TestRPCError(t *testing.T) { + t.Parallel() + + tests := []struct { + in *btcjson.RPCError + want string + }{ + { + btcjson.ErrRPCInvalidRequest, + "-32600: Invalid request", + }, + { + btcjson.ErrRPCMethodNotFound, + "-32601: Method not found", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.in.Error() + if result != test.want { + t.Errorf("Error #%d\n got: %s want: %s", i, result, + test.want) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/jsonrpcerr.go b/vendor/github.com/btcsuite/btcd/btcjson/jsonrpcerr.go new file mode 100644 index 0000000000000000000000000000000000000000..16a024161e31b5d30cbc8aa68f6f634ffcb3b2a8 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/jsonrpcerr.go @@ -0,0 +1,83 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +// Standard JSON-RPC 2.0 errors. +var ( + ErrRPCInvalidRequest = &RPCError{ + Code: -32600, + Message: "Invalid request", + } + ErrRPCMethodNotFound = &RPCError{ + Code: -32601, + Message: "Method not found", + } + ErrRPCInvalidParams = &RPCError{ + Code: -32602, + Message: "Invalid parameters", + } + ErrRPCInternal = &RPCError{ + Code: -32603, + Message: "Internal error", + } + ErrRPCParse = &RPCError{ + Code: -32700, + Message: "Parse error", + } +) + +// General application defined JSON errors. +const ( + ErrRPCMisc RPCErrorCode = -1 + ErrRPCForbiddenBySafeMode RPCErrorCode = -2 + ErrRPCType RPCErrorCode = -3 + ErrRPCInvalidAddressOrKey RPCErrorCode = -5 + ErrRPCOutOfMemory RPCErrorCode = -7 + ErrRPCInvalidParameter RPCErrorCode = -8 + ErrRPCDatabase RPCErrorCode = -20 + ErrRPCDeserialization RPCErrorCode = -22 + ErrRPCVerify RPCErrorCode = -25 +) + +// Peer-to-peer client errors. +const ( + ErrRPCClientNotConnected RPCErrorCode = -9 + ErrRPCClientInInitialDownload RPCErrorCode = -10 +) + +// Wallet JSON errors +const ( + ErrRPCWallet RPCErrorCode = -4 + ErrRPCWalletInsufficientFunds RPCErrorCode = -6 + ErrRPCWalletInvalidAccountName RPCErrorCode = -11 + ErrRPCWalletKeypoolRanOut RPCErrorCode = -12 + ErrRPCWalletUnlockNeeded RPCErrorCode = -13 + ErrRPCWalletPassphraseIncorrect RPCErrorCode = -14 + ErrRPCWalletWrongEncState RPCErrorCode = -15 + ErrRPCWalletEncryptionFailed RPCErrorCode = -16 + ErrRPCWalletAlreadyUnlocked RPCErrorCode = -17 +) + +// Specific Errors related to commands. These are the ones a user of the RPC +// server are most likely to see. Generally, the codes should match one of the +// more general errors above. +const ( + ErrRPCBlockNotFound RPCErrorCode = -5 + ErrRPCBlockCount RPCErrorCode = -5 + ErrRPCBestBlockHash RPCErrorCode = -5 + ErrRPCDifficulty RPCErrorCode = -5 + ErrRPCOutOfRange RPCErrorCode = -1 + ErrRPCNoTxInfo RPCErrorCode = -5 + ErrRPCNoNewestBlockInfo RPCErrorCode = -5 + ErrRPCInvalidTxVout RPCErrorCode = -5 + ErrRPCRawTxString RPCErrorCode = -32602 + ErrRPCDecodeHexString RPCErrorCode = -22 +) + +// Errors that are specific to btcd. +const ( + ErrRPCNoWallet RPCErrorCode = -1 + ErrRPCUnimplemented RPCErrorCode = -1 +) diff --git a/vendor/github.com/btcsuite/btcd/btcjson/register.go b/vendor/github.com/btcsuite/btcd/btcjson/register.go new file mode 100644 index 0000000000000000000000000000000000000000..5de001c91e2c9ad156ee95e83c9de99d66e62823 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/register.go @@ -0,0 +1,292 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +import ( + "encoding/json" + "fmt" + "reflect" + "sort" + "strconv" + "strings" + "sync" +) + +// UsageFlag define flags that specify additional properties about the +// circumstances under which a command can be used. +type UsageFlag uint32 + +const ( + // UFWalletOnly indicates that the command can only be used with an RPC + // server that supports wallet commands. + UFWalletOnly UsageFlag = 1 << iota + + // UFWebsocketOnly indicates that the command can only be used when + // communicating with an RPC server over websockets. This typically + // applies to notifications and notification registration functions + // since neiher makes since when using a single-shot HTTP-POST request. + UFWebsocketOnly + + // UFNotification indicates that the command is actually a notification. + // This means when it is marshalled, the ID must be nil. + UFNotification + + // highestUsageFlagBit is the maximum usage flag bit and is used in the + // stringer and tests to ensure all of the above constants have been + // tested. + highestUsageFlagBit +) + +// Map of UsageFlag values back to their constant names for pretty printing. +var usageFlagStrings = map[UsageFlag]string{ + UFWalletOnly: "UFWalletOnly", + UFWebsocketOnly: "UFWebsocketOnly", + UFNotification: "UFNotification", +} + +// String returns the UsageFlag in human-readable form. +func (fl UsageFlag) String() string { + // No flags are set. + if fl == 0 { + return "0x0" + } + + // Add individual bit flags. + s := "" + for flag := UFWalletOnly; flag < highestUsageFlagBit; flag <<= 1 { + if fl&flag == flag { + s += usageFlagStrings[flag] + "|" + fl -= flag + } + } + + // Add remaining value as raw hex. + s = strings.TrimRight(s, "|") + if fl != 0 { + s += "|0x" + strconv.FormatUint(uint64(fl), 16) + } + s = strings.TrimLeft(s, "|") + return s +} + +// methodInfo keeps track of information about each registered method such as +// the parameter information. +type methodInfo struct { + maxParams int + numReqParams int + numOptParams int + defaults map[int]reflect.Value + flags UsageFlag + usage string +} + +var ( + // These fields are used to map the registered types to method names. + registerLock sync.RWMutex + methodToConcreteType = make(map[string]reflect.Type) + methodToInfo = make(map[string]methodInfo) + concreteTypeToMethod = make(map[reflect.Type]string) +) + +// baseKindString returns the base kind for a given reflect.Type after +// indirecting through all pointers. +func baseKindString(rt reflect.Type) string { + numIndirects := 0 + for rt.Kind() == reflect.Ptr { + numIndirects++ + rt = rt.Elem() + } + + return fmt.Sprintf("%s%s", strings.Repeat("*", numIndirects), rt.Kind()) +} + +// isAcceptableKind returns whether or not the passed field type is a supported +// type. It is called after the first pointer indirection, so further pointers +// are not supported. +func isAcceptableKind(kind reflect.Kind) bool { + switch kind { + case reflect.Chan: + fallthrough + case reflect.Complex64: + fallthrough + case reflect.Complex128: + fallthrough + case reflect.Func: + fallthrough + case reflect.Ptr: + fallthrough + case reflect.Interface: + return false + } + + return true +} + +// RegisterCmd registers a new command that will automatically marshal to and +// from JSON-RPC with full type checking and positional parameter support. It +// also accepts usage flags which identify the circumstances under which the +// command can be used. +// +// This package automatically registers all of the exported commands by default +// using this function, however it is also exported so callers can easily +// register custom types. +// +// The type format is very strict since it needs to be able to automatically +// marshal to and from JSON-RPC 1.0. The following enumerates the requirements: +// +// - The provided command must be a single pointer to a struct +// - All fields must be exported +// - The order of the positional parameters in the marshalled JSON will be in +// the same order as declared in the struct definition +// - Struct embedding is not supported +// - Struct fields may NOT be channels, functions, complex, or interface +// - A field in the provided struct with a pointer is treated as optional +// - Multiple indirections (i.e **int) are not supported +// - Once the first optional field (pointer) is encountered, the remaining +// fields must also be optional fields (pointers) as required by positional +// params +// - A field that has a 'jsonrpcdefault' struct tag must be an optional field +// (pointer) +// +// NOTE: This function only needs to be able to examine the structure of the +// passed struct, so it does not need to be an actual instance. Therefore, it +// is recommended to simply pass a nil pointer cast to the appropriate type. +// For example, (*FooCmd)(nil). +func RegisterCmd(method string, cmd interface{}, flags UsageFlag) error { + registerLock.Lock() + defer registerLock.Unlock() + + if _, ok := methodToConcreteType[method]; ok { + str := fmt.Sprintf("method %q is already registered", method) + return makeError(ErrDuplicateMethod, str) + } + + // Ensure that no unrecognized flag bits were specified. + if ^(highestUsageFlagBit-1)&flags != 0 { + str := fmt.Sprintf("invalid usage flags specified for method "+ + "%s: %v", method, flags) + return makeError(ErrInvalidUsageFlags, str) + } + + rtp := reflect.TypeOf(cmd) + if rtp.Kind() != reflect.Ptr { + str := fmt.Sprintf("type must be *struct not '%s (%s)'", rtp, + rtp.Kind()) + return makeError(ErrInvalidType, str) + } + rt := rtp.Elem() + if rt.Kind() != reflect.Struct { + str := fmt.Sprintf("type must be *struct not '%s (*%s)'", + rtp, rt.Kind()) + return makeError(ErrInvalidType, str) + } + + // Enumerate the struct fields to validate them and gather parameter + // information. + numFields := rt.NumField() + numOptFields := 0 + defaults := make(map[int]reflect.Value) + for i := 0; i < numFields; i++ { + rtf := rt.Field(i) + if rtf.Anonymous { + str := fmt.Sprintf("embedded fields are not supported "+ + "(field name: %q)", rtf.Name) + return makeError(ErrEmbeddedType, str) + } + if rtf.PkgPath != "" { + str := fmt.Sprintf("unexported fields are not supported "+ + "(field name: %q)", rtf.Name) + return makeError(ErrUnexportedField, str) + } + + // Disallow types that can't be JSON encoded. Also, determine + // if the field is optional based on it being a pointer. + var isOptional bool + switch kind := rtf.Type.Kind(); kind { + case reflect.Ptr: + isOptional = true + kind = rtf.Type.Elem().Kind() + fallthrough + default: + if !isAcceptableKind(kind) { + str := fmt.Sprintf("unsupported field type "+ + "'%s (%s)' (field name %q)", rtf.Type, + baseKindString(rtf.Type), rtf.Name) + return makeError(ErrUnsupportedFieldType, str) + } + } + + // Count the optional fields and ensure all fields after the + // first optional field are also optional. + if isOptional { + numOptFields++ + } else { + if numOptFields > 0 { + str := fmt.Sprintf("all fields after the first "+ + "optional field must also be optional "+ + "(field name %q)", rtf.Name) + return makeError(ErrNonOptionalField, str) + } + } + + // Ensure the default value can be unsmarshalled into the type + // and that defaults are only specified for optional fields. + if tag := rtf.Tag.Get("jsonrpcdefault"); tag != "" { + if !isOptional { + str := fmt.Sprintf("required fields must not "+ + "have a default specified (field name "+ + "%q)", rtf.Name) + return makeError(ErrNonOptionalDefault, str) + } + + rvf := reflect.New(rtf.Type.Elem()) + err := json.Unmarshal([]byte(tag), rvf.Interface()) + if err != nil { + str := fmt.Sprintf("default value of %q is "+ + "the wrong type (field name %q)", tag, + rtf.Name) + return makeError(ErrMismatchedDefault, str) + } + defaults[i] = rvf + } + } + + // Update the registration maps. + methodToConcreteType[method] = rtp + methodToInfo[method] = methodInfo{ + maxParams: numFields, + numReqParams: numFields - numOptFields, + numOptParams: numOptFields, + defaults: defaults, + flags: flags, + } + concreteTypeToMethod[rtp] = method + return nil +} + +// MustRegisterCmd performs the same function as RegisterCmd except it panics +// if there is an error. This should only be called from package init +// functions. +func MustRegisterCmd(method string, cmd interface{}, flags UsageFlag) { + if err := RegisterCmd(method, cmd, flags); err != nil { + panic(fmt.Sprintf("failed to register type %q: %v\n", method, + err)) + } +} + +// RegisteredCmdMethods returns a sorted list of methods for all registered +// commands. +func RegisteredCmdMethods() []string { + registerLock.Lock() + defer registerLock.Unlock() + + methods := make([]string, 0, len(methodToInfo)) + for k := range methodToInfo { + methods = append(methods, k) + } + + sort.Sort(sort.StringSlice(methods)) + return methods +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/register_test.go b/vendor/github.com/btcsuite/btcd/btcjson/register_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4cac9c7a8663a05d944a9013b92b4e48d7641b9d --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/register_test.go @@ -0,0 +1,263 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "reflect" + "sort" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestUsageFlagStringer tests the stringized output for the UsageFlag type. +func TestUsageFlagStringer(t *testing.T) { + t.Parallel() + + tests := []struct { + in btcjson.UsageFlag + want string + }{ + {0, "0x0"}, + {btcjson.UFWalletOnly, "UFWalletOnly"}, + {btcjson.UFWebsocketOnly, "UFWebsocketOnly"}, + {btcjson.UFNotification, "UFNotification"}, + {btcjson.UFWalletOnly | btcjson.UFWebsocketOnly, + "UFWalletOnly|UFWebsocketOnly"}, + {btcjson.UFWalletOnly | btcjson.UFWebsocketOnly | (1 << 31), + "UFWalletOnly|UFWebsocketOnly|0x80000000"}, + } + + // Detect additional usage flags that don't have the stringer added. + numUsageFlags := 0 + highestUsageFlagBit := btcjson.TstHighestUsageFlagBit + for highestUsageFlagBit > 1 { + numUsageFlags++ + highestUsageFlagBit >>= 1 + } + if len(tests)-3 != numUsageFlags { + t.Errorf("It appears a usage flag was added without adding " + + "an associated stringer test") + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.in.String() + if result != test.want { + t.Errorf("String #%d\n got: %s want: %s", i, result, + test.want) + continue + } + } +} + +// TestRegisterCmdErrors ensures the RegisterCmd function returns the expected +// error when provided with invalid types. +func TestRegisterCmdErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + method string + cmdFunc func() interface{} + flags btcjson.UsageFlag + err btcjson.Error + }{ + { + name: "duplicate method", + method: "getblock", + cmdFunc: func() interface{} { + return struct{}{} + }, + err: btcjson.Error{ErrorCode: btcjson.ErrDuplicateMethod}, + }, + { + name: "invalid usage flags", + method: "registertestcmd", + cmdFunc: func() interface{} { + return 0 + }, + flags: btcjson.TstHighestUsageFlagBit, + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidUsageFlags}, + }, + { + name: "invalid type", + method: "registertestcmd", + cmdFunc: func() interface{} { + return 0 + }, + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "invalid type 2", + method: "registertestcmd", + cmdFunc: func() interface{} { + return &[]string{} + }, + err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, + }, + { + name: "embedded field", + method: "registertestcmd", + cmdFunc: func() interface{} { + type test struct{ int } + return (*test)(nil) + }, + err: btcjson.Error{ErrorCode: btcjson.ErrEmbeddedType}, + }, + { + name: "unexported field", + method: "registertestcmd", + cmdFunc: func() interface{} { + type test struct{ a int } + return (*test)(nil) + }, + err: btcjson.Error{ErrorCode: btcjson.ErrUnexportedField}, + }, + { + name: "unsupported field type 1", + method: "registertestcmd", + cmdFunc: func() interface{} { + type test struct{ A **int } + return (*test)(nil) + }, + err: btcjson.Error{ErrorCode: btcjson.ErrUnsupportedFieldType}, + }, + { + name: "unsupported field type 2", + method: "registertestcmd", + cmdFunc: func() interface{} { + type test struct{ A chan int } + return (*test)(nil) + }, + err: btcjson.Error{ErrorCode: btcjson.ErrUnsupportedFieldType}, + }, + { + name: "unsupported field type 3", + method: "registertestcmd", + cmdFunc: func() interface{} { + type test struct{ A complex64 } + return (*test)(nil) + }, + err: btcjson.Error{ErrorCode: btcjson.ErrUnsupportedFieldType}, + }, + { + name: "unsupported field type 4", + method: "registertestcmd", + cmdFunc: func() interface{} { + type test struct{ A complex128 } + return (*test)(nil) + }, + err: btcjson.Error{ErrorCode: btcjson.ErrUnsupportedFieldType}, + }, + { + name: "unsupported field type 5", + method: "registertestcmd", + cmdFunc: func() interface{} { + type test struct{ A func() } + return (*test)(nil) + }, + err: btcjson.Error{ErrorCode: btcjson.ErrUnsupportedFieldType}, + }, + { + name: "unsupported field type 6", + method: "registertestcmd", + cmdFunc: func() interface{} { + type test struct{ A interface{} } + return (*test)(nil) + }, + err: btcjson.Error{ErrorCode: btcjson.ErrUnsupportedFieldType}, + }, + { + name: "required after optional", + method: "registertestcmd", + cmdFunc: func() interface{} { + type test struct { + A *int + B int + } + return (*test)(nil) + }, + err: btcjson.Error{ErrorCode: btcjson.ErrNonOptionalField}, + }, + { + name: "non-optional with default", + method: "registertestcmd", + cmdFunc: func() interface{} { + type test struct { + A int `jsonrpcdefault:"1"` + } + return (*test)(nil) + }, + err: btcjson.Error{ErrorCode: btcjson.ErrNonOptionalDefault}, + }, + { + name: "mismatched default", + method: "registertestcmd", + cmdFunc: func() interface{} { + type test struct { + A *int `jsonrpcdefault:"1.7"` + } + return (*test)(nil) + }, + err: btcjson.Error{ErrorCode: btcjson.ErrMismatchedDefault}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + err := btcjson.RegisterCmd(test.method, test.cmdFunc(), + test.flags) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("Test #%d (%s) wrong error - got %T, "+ + "want %T", i, test.name, err, test.err) + continue + } + gotErrorCode := err.(btcjson.Error).ErrorCode + if gotErrorCode != test.err.ErrorCode { + t.Errorf("Test #%d (%s) mismatched error code - got "+ + "%v, want %v", i, test.name, gotErrorCode, + test.err.ErrorCode) + continue + } + } +} + +// TestMustRegisterCmdPanic ensures the MustRegisterCmd function panics when +// used to register an invalid type. +func TestMustRegisterCmdPanic(t *testing.T) { + t.Parallel() + + // Setup a defer to catch the expected panic to ensure it actually + // paniced. + defer func() { + if err := recover(); err == nil { + t.Error("MustRegisterCmd did not panic as expected") + } + }() + + // Intentionally try to register an invalid type to force a panic. + btcjson.MustRegisterCmd("panicme", 0, 0) +} + +// TestRegisteredCmdMethods tests the RegisteredCmdMethods function ensure it +// works as expected. +func TestRegisteredCmdMethods(t *testing.T) { + t.Parallel() + + // Ensure the registerd methods are returned. + methods := btcjson.RegisteredCmdMethods() + if len(methods) == 0 { + t.Fatal("RegisteredCmdMethods: no methods") + } + + // Ensure the returned methods are sorted. + sortedMethods := make([]string, len(methods)) + copy(sortedMethods, methods) + sort.Sort(sort.StringSlice(sortedMethods)) + if !reflect.DeepEqual(sortedMethods, methods) { + t.Fatal("RegisteredCmdMethods: methods are not sorted") + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/walletsvrcmds.go b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrcmds.go new file mode 100644 index 0000000000000000000000000000000000000000..f340714bd56e85b95f6a18d9816eaaf99623d4bb --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrcmds.go @@ -0,0 +1,685 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// NOTE: This file is intended to house the RPC commands that are supported by +// a wallet server. + +package btcjson + +// AddMultisigAddressCmd defines the addmutisigaddress JSON-RPC command. +type AddMultisigAddressCmd struct { + NRequired int + Keys []string + Account *string +} + +// NewAddMultisigAddressCmd returns a new instance which can be used to issue a +// addmultisigaddress JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewAddMultisigAddressCmd(nRequired int, keys []string, account *string) *AddMultisigAddressCmd { + return &AddMultisigAddressCmd{ + NRequired: nRequired, + Keys: keys, + Account: account, + } +} + +// CreateMultisigCmd defines the createmultisig JSON-RPC command. +type CreateMultisigCmd struct { + NRequired int + Keys []string +} + +// NewCreateMultisigCmd returns a new instance which can be used to issue a +// createmultisig JSON-RPC command. +func NewCreateMultisigCmd(nRequired int, keys []string) *CreateMultisigCmd { + return &CreateMultisigCmd{ + NRequired: nRequired, + Keys: keys, + } +} + +// DumpPrivKeyCmd defines the dumpprivkey JSON-RPC command. +type DumpPrivKeyCmd struct { + Address string +} + +// NewDumpPrivKeyCmd returns a new instance which can be used to issue a +// dumpprivkey JSON-RPC command. +func NewDumpPrivKeyCmd(address string) *DumpPrivKeyCmd { + return &DumpPrivKeyCmd{ + Address: address, + } +} + +// EncryptWalletCmd defines the encryptwallet JSON-RPC command. +type EncryptWalletCmd struct { + Passphrase string +} + +// NewEncryptWalletCmd returns a new instance which can be used to issue a +// encryptwallet JSON-RPC command. +func NewEncryptWalletCmd(passphrase string) *EncryptWalletCmd { + return &EncryptWalletCmd{ + Passphrase: passphrase, + } +} + +// EstimateFeeCmd defines the estimatefee JSON-RPC command. +type EstimateFeeCmd struct { + NumBlocks int64 +} + +// NewEstimateFeeCmd returns a new instance which can be used to issue a +// estimatefee JSON-RPC command. +func NewEstimateFeeCmd(numBlocks int64) *EstimateFeeCmd { + return &EstimateFeeCmd{ + NumBlocks: numBlocks, + } +} + +// EstimatePriorityCmd defines the estimatepriority JSON-RPC command. +type EstimatePriorityCmd struct { + NumBlocks int64 +} + +// NewEstimatePriorityCmd returns a new instance which can be used to issue a +// estimatepriority JSON-RPC command. +func NewEstimatePriorityCmd(numBlocks int64) *EstimatePriorityCmd { + return &EstimatePriorityCmd{ + NumBlocks: numBlocks, + } +} + +// GetAccountCmd defines the getaccount JSON-RPC command. +type GetAccountCmd struct { + Address string +} + +// NewGetAccountCmd returns a new instance which can be used to issue a +// getaccount JSON-RPC command. +func NewGetAccountCmd(address string) *GetAccountCmd { + return &GetAccountCmd{ + Address: address, + } +} + +// GetAccountAddressCmd defines the getaccountaddress JSON-RPC command. +type GetAccountAddressCmd struct { + Account string +} + +// NewGetAccountAddressCmd returns a new instance which can be used to issue a +// getaccountaddress JSON-RPC command. +func NewGetAccountAddressCmd(account string) *GetAccountAddressCmd { + return &GetAccountAddressCmd{ + Account: account, + } +} + +// GetAddressesByAccountCmd defines the getaddressesbyaccount JSON-RPC command. +type GetAddressesByAccountCmd struct { + Account string +} + +// NewGetAddressesByAccountCmd returns a new instance which can be used to issue +// a getaddressesbyaccount JSON-RPC command. +func NewGetAddressesByAccountCmd(account string) *GetAddressesByAccountCmd { + return &GetAddressesByAccountCmd{ + Account: account, + } +} + +// GetBalanceCmd defines the getbalance JSON-RPC command. +type GetBalanceCmd struct { + Account *string + MinConf *int `jsonrpcdefault:"1"` +} + +// NewGetBalanceCmd returns a new instance which can be used to issue a +// getbalance JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetBalanceCmd(account *string, minConf *int) *GetBalanceCmd { + return &GetBalanceCmd{ + Account: account, + MinConf: minConf, + } +} + +// GetNewAddressCmd defines the getnewaddress JSON-RPC command. +type GetNewAddressCmd struct { + Account *string +} + +// NewGetNewAddressCmd returns a new instance which can be used to issue a +// getnewaddress JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetNewAddressCmd(account *string) *GetNewAddressCmd { + return &GetNewAddressCmd{ + Account: account, + } +} + +// GetRawChangeAddressCmd defines the getrawchangeaddress JSON-RPC command. +type GetRawChangeAddressCmd struct { + Account *string +} + +// NewGetRawChangeAddressCmd returns a new instance which can be used to issue a +// getrawchangeaddress JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetRawChangeAddressCmd(account *string) *GetRawChangeAddressCmd { + return &GetRawChangeAddressCmd{ + Account: account, + } +} + +// GetReceivedByAccountCmd defines the getreceivedbyaccount JSON-RPC command. +type GetReceivedByAccountCmd struct { + Account string + MinConf *int `jsonrpcdefault:"1"` +} + +// NewGetReceivedByAccountCmd returns a new instance which can be used to issue +// a getreceivedbyaccount JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetReceivedByAccountCmd(account string, minConf *int) *GetReceivedByAccountCmd { + return &GetReceivedByAccountCmd{ + Account: account, + MinConf: minConf, + } +} + +// GetReceivedByAddressCmd defines the getreceivedbyaddress JSON-RPC command. +type GetReceivedByAddressCmd struct { + Address string + MinConf *int `jsonrpcdefault:"1"` +} + +// NewGetReceivedByAddressCmd returns a new instance which can be used to issue +// a getreceivedbyaddress JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetReceivedByAddressCmd(address string, minConf *int) *GetReceivedByAddressCmd { + return &GetReceivedByAddressCmd{ + Address: address, + MinConf: minConf, + } +} + +// GetTransactionCmd defines the gettransaction JSON-RPC command. +type GetTransactionCmd struct { + Txid string + IncludeWatchOnly *bool `jsonrpcdefault:"false"` +} + +// NewGetTransactionCmd returns a new instance which can be used to issue a +// gettransaction JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetTransactionCmd(txHash string, includeWatchOnly *bool) *GetTransactionCmd { + return &GetTransactionCmd{ + Txid: txHash, + IncludeWatchOnly: includeWatchOnly, + } +} + +// GetWalletInfoCmd defines the getwalletinfo JSON-RPC command. +type GetWalletInfoCmd struct{} + +// NewGetWalletInfoCmd returns a new instance which can be used to issue a +// getwalletinfo JSON-RPC command. +func NewGetWalletInfoCmd() *GetWalletInfoCmd { + return &GetWalletInfoCmd{} +} + +// ImportPrivKeyCmd defines the importprivkey JSON-RPC command. +type ImportPrivKeyCmd struct { + PrivKey string + Label *string + Rescan *bool `jsonrpcdefault:"true"` +} + +// NewImportPrivKeyCmd returns a new instance which can be used to issue a +// importprivkey JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewImportPrivKeyCmd(privKey string, label *string, rescan *bool) *ImportPrivKeyCmd { + return &ImportPrivKeyCmd{ + PrivKey: privKey, + Label: label, + Rescan: rescan, + } +} + +// KeyPoolRefillCmd defines the keypoolrefill JSON-RPC command. +type KeyPoolRefillCmd struct { + NewSize *uint `jsonrpcdefault:"100"` +} + +// NewKeyPoolRefillCmd returns a new instance which can be used to issue a +// keypoolrefill JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewKeyPoolRefillCmd(newSize *uint) *KeyPoolRefillCmd { + return &KeyPoolRefillCmd{ + NewSize: newSize, + } +} + +// ListAccountsCmd defines the listaccounts JSON-RPC command. +type ListAccountsCmd struct { + MinConf *int `jsonrpcdefault:"1"` +} + +// NewListAccountsCmd returns a new instance which can be used to issue a +// listaccounts JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewListAccountsCmd(minConf *int) *ListAccountsCmd { + return &ListAccountsCmd{ + MinConf: minConf, + } +} + +// ListAddressGroupingsCmd defines the listaddressgroupings JSON-RPC command. +type ListAddressGroupingsCmd struct{} + +// NewListAddressGroupingsCmd returns a new instance which can be used to issue +// a listaddressgroupoings JSON-RPC command. +func NewListAddressGroupingsCmd() *ListAddressGroupingsCmd { + return &ListAddressGroupingsCmd{} +} + +// ListLockUnspentCmd defines the listlockunspent JSON-RPC command. +type ListLockUnspentCmd struct{} + +// NewListLockUnspentCmd returns a new instance which can be used to issue a +// listlockunspent JSON-RPC command. +func NewListLockUnspentCmd() *ListLockUnspentCmd { + return &ListLockUnspentCmd{} +} + +// ListReceivedByAccountCmd defines the listreceivedbyaccount JSON-RPC command. +type ListReceivedByAccountCmd struct { + MinConf *int `jsonrpcdefault:"1"` + IncludeEmpty *bool `jsonrpcdefault:"false"` + IncludeWatchOnly *bool `jsonrpcdefault:"false"` +} + +// NewListReceivedByAccountCmd returns a new instance which can be used to issue +// a listreceivedbyaccount JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewListReceivedByAccountCmd(minConf *int, includeEmpty, includeWatchOnly *bool) *ListReceivedByAccountCmd { + return &ListReceivedByAccountCmd{ + MinConf: minConf, + IncludeEmpty: includeEmpty, + IncludeWatchOnly: includeWatchOnly, + } +} + +// ListReceivedByAddressCmd defines the listreceivedbyaddress JSON-RPC command. +type ListReceivedByAddressCmd struct { + MinConf *int `jsonrpcdefault:"1"` + IncludeEmpty *bool `jsonrpcdefault:"false"` + IncludeWatchOnly *bool `jsonrpcdefault:"false"` +} + +// NewListReceivedByAddressCmd returns a new instance which can be used to issue +// a listreceivedbyaddress JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewListReceivedByAddressCmd(minConf *int, includeEmpty, includeWatchOnly *bool) *ListReceivedByAddressCmd { + return &ListReceivedByAddressCmd{ + MinConf: minConf, + IncludeEmpty: includeEmpty, + IncludeWatchOnly: includeWatchOnly, + } +} + +// ListSinceBlockCmd defines the listsinceblock JSON-RPC command. +type ListSinceBlockCmd struct { + BlockHash *string + TargetConfirmations *int `jsonrpcdefault:"1"` + IncludeWatchOnly *bool `jsonrpcdefault:"false"` +} + +// NewListSinceBlockCmd returns a new instance which can be used to issue a +// listsinceblock JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewListSinceBlockCmd(blockHash *string, targetConfirms *int, includeWatchOnly *bool) *ListSinceBlockCmd { + return &ListSinceBlockCmd{ + BlockHash: blockHash, + TargetConfirmations: targetConfirms, + IncludeWatchOnly: includeWatchOnly, + } +} + +// ListTransactionsCmd defines the listtransactions JSON-RPC command. +type ListTransactionsCmd struct { + Account *string + Count *int `jsonrpcdefault:"10"` + From *int `jsonrpcdefault:"0"` + IncludeWatchOnly *bool `jsonrpcdefault:"false"` +} + +// NewListTransactionsCmd returns a new instance which can be used to issue a +// listtransactions JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewListTransactionsCmd(account *string, count, from *int, includeWatchOnly *bool) *ListTransactionsCmd { + return &ListTransactionsCmd{ + Account: account, + Count: count, + From: from, + IncludeWatchOnly: includeWatchOnly, + } +} + +// ListUnspentCmd defines the listunspent JSON-RPC command. +type ListUnspentCmd struct { + MinConf *int `jsonrpcdefault:"1"` + MaxConf *int `jsonrpcdefault:"9999999"` + Addresses *[]string +} + +// NewListUnspentCmd returns a new instance which can be used to issue a +// listunspent JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewListUnspentCmd(minConf, maxConf *int, addresses *[]string) *ListUnspentCmd { + return &ListUnspentCmd{ + MinConf: minConf, + MaxConf: maxConf, + Addresses: addresses, + } +} + +// LockUnspentCmd defines the lockunspent JSON-RPC command. +type LockUnspentCmd struct { + Unlock bool + Transactions []TransactionInput +} + +// NewLockUnspentCmd returns a new instance which can be used to issue a +// lockunspent JSON-RPC command. +func NewLockUnspentCmd(unlock bool, transactions []TransactionInput) *LockUnspentCmd { + return &LockUnspentCmd{ + Unlock: unlock, + Transactions: transactions, + } +} + +// MoveCmd defines the move JSON-RPC command. +type MoveCmd struct { + FromAccount string + ToAccount string + Amount float64 // In BTC + MinConf *int `jsonrpcdefault:"1"` + Comment *string +} + +// NewMoveCmd returns a new instance which can be used to issue a move JSON-RPC +// command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewMoveCmd(fromAccount, toAccount string, amount float64, minConf *int, comment *string) *MoveCmd { + return &MoveCmd{ + FromAccount: fromAccount, + ToAccount: toAccount, + Amount: amount, + MinConf: minConf, + Comment: comment, + } +} + +// SendFromCmd defines the sendfrom JSON-RPC command. +type SendFromCmd struct { + FromAccount string + ToAddress string + Amount float64 // In BTC + MinConf *int `jsonrpcdefault:"1"` + Comment *string + CommentTo *string +} + +// NewSendFromCmd returns a new instance which can be used to issue a sendfrom +// JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewSendFromCmd(fromAccount, toAddress string, amount float64, minConf *int, comment, commentTo *string) *SendFromCmd { + return &SendFromCmd{ + FromAccount: fromAccount, + ToAddress: toAddress, + Amount: amount, + MinConf: minConf, + Comment: comment, + CommentTo: commentTo, + } +} + +// SendManyCmd defines the sendmany JSON-RPC command. +type SendManyCmd struct { + FromAccount string + Amounts map[string]float64 `jsonrpcusage:"{\"address\":amount,...}"` // In BTC + MinConf *int `jsonrpcdefault:"1"` + Comment *string +} + +// NewSendManyCmd returns a new instance which can be used to issue a sendmany +// JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewSendManyCmd(fromAccount string, amounts map[string]float64, minConf *int, comment *string) *SendManyCmd { + return &SendManyCmd{ + FromAccount: fromAccount, + Amounts: amounts, + MinConf: minConf, + Comment: comment, + } +} + +// SendToAddressCmd defines the sendtoaddress JSON-RPC command. +type SendToAddressCmd struct { + Address string + Amount float64 + Comment *string + CommentTo *string +} + +// NewSendToAddressCmd returns a new instance which can be used to issue a +// sendtoaddress JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewSendToAddressCmd(address string, amount float64, comment, commentTo *string) *SendToAddressCmd { + return &SendToAddressCmd{ + Address: address, + Amount: amount, + Comment: comment, + CommentTo: commentTo, + } +} + +// SetAccountCmd defines the setaccount JSON-RPC command. +type SetAccountCmd struct { + Address string + Account string +} + +// NewSetAccountCmd returns a new instance which can be used to issue a +// setaccount JSON-RPC command. +func NewSetAccountCmd(address, account string) *SetAccountCmd { + return &SetAccountCmd{ + Address: address, + Account: account, + } +} + +// SetTxFeeCmd defines the settxfee JSON-RPC command. +type SetTxFeeCmd struct { + Amount float64 // In BTC +} + +// NewSetTxFeeCmd returns a new instance which can be used to issue a settxfee +// JSON-RPC command. +func NewSetTxFeeCmd(amount float64) *SetTxFeeCmd { + return &SetTxFeeCmd{ + Amount: amount, + } +} + +// SignMessageCmd defines the signmessage JSON-RPC command. +type SignMessageCmd struct { + Address string + Message string +} + +// NewSignMessageCmd returns a new instance which can be used to issue a +// signmessage JSON-RPC command. +func NewSignMessageCmd(address, message string) *SignMessageCmd { + return &SignMessageCmd{ + Address: address, + Message: message, + } +} + +// RawTxInput models the data needed for raw transaction input that is used in +// the SignRawTransactionCmd struct. +type RawTxInput struct { + Txid string `json:"txid"` + Vout uint32 `json:"vout"` + ScriptPubKey string `json:"scriptPubKey"` + RedeemScript string `json:"redeemScript"` +} + +// SignRawTransactionCmd defines the signrawtransaction JSON-RPC command. +type SignRawTransactionCmd struct { + RawTx string + Inputs *[]RawTxInput + PrivKeys *[]string + Flags *string `jsonrpcdefault:"\"ALL\""` +} + +// NewSignRawTransactionCmd returns a new instance which can be used to issue a +// signrawtransaction JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewSignRawTransactionCmd(hexEncodedTx string, inputs *[]RawTxInput, privKeys *[]string, flags *string) *SignRawTransactionCmd { + return &SignRawTransactionCmd{ + RawTx: hexEncodedTx, + Inputs: inputs, + PrivKeys: privKeys, + Flags: flags, + } +} + +// WalletLockCmd defines the walletlock JSON-RPC command. +type WalletLockCmd struct{} + +// NewWalletLockCmd returns a new instance which can be used to issue a +// walletlock JSON-RPC command. +func NewWalletLockCmd() *WalletLockCmd { + return &WalletLockCmd{} +} + +// WalletPassphraseCmd defines the walletpassphrase JSON-RPC command. +type WalletPassphraseCmd struct { + Passphrase string + Timeout int64 +} + +// NewWalletPassphraseCmd returns a new instance which can be used to issue a +// walletpassphrase JSON-RPC command. +func NewWalletPassphraseCmd(passphrase string, timeout int64) *WalletPassphraseCmd { + return &WalletPassphraseCmd{ + Passphrase: passphrase, + Timeout: timeout, + } +} + +// WalletPassphraseChangeCmd defines the walletpassphrase JSON-RPC command. +type WalletPassphraseChangeCmd struct { + OldPassphrase string + NewPassphrase string +} + +// NewWalletPassphraseChangeCmd returns a new instance which can be used to +// issue a walletpassphrasechange JSON-RPC command. +func NewWalletPassphraseChangeCmd(oldPassphrase, newPassphrase string) *WalletPassphraseChangeCmd { + return &WalletPassphraseChangeCmd{ + OldPassphrase: oldPassphrase, + NewPassphrase: newPassphrase, + } +} + +func init() { + // The commands in this file are only usable with a wallet server. + flags := UFWalletOnly + + MustRegisterCmd("addmultisigaddress", (*AddMultisigAddressCmd)(nil), flags) + MustRegisterCmd("createmultisig", (*CreateMultisigCmd)(nil), flags) + MustRegisterCmd("dumpprivkey", (*DumpPrivKeyCmd)(nil), flags) + MustRegisterCmd("encryptwallet", (*EncryptWalletCmd)(nil), flags) + MustRegisterCmd("estimatefee", (*EstimateFeeCmd)(nil), flags) + MustRegisterCmd("estimatepriority", (*EstimatePriorityCmd)(nil), flags) + MustRegisterCmd("getaccount", (*GetAccountCmd)(nil), flags) + MustRegisterCmd("getaccountaddress", (*GetAccountAddressCmd)(nil), flags) + MustRegisterCmd("getaddressesbyaccount", (*GetAddressesByAccountCmd)(nil), flags) + MustRegisterCmd("getbalance", (*GetBalanceCmd)(nil), flags) + MustRegisterCmd("getnewaddress", (*GetNewAddressCmd)(nil), flags) + MustRegisterCmd("getrawchangeaddress", (*GetRawChangeAddressCmd)(nil), flags) + MustRegisterCmd("getreceivedbyaccount", (*GetReceivedByAccountCmd)(nil), flags) + MustRegisterCmd("getreceivedbyaddress", (*GetReceivedByAddressCmd)(nil), flags) + MustRegisterCmd("gettransaction", (*GetTransactionCmd)(nil), flags) + MustRegisterCmd("getwalletinfo", (*GetWalletInfoCmd)(nil), flags) + MustRegisterCmd("importprivkey", (*ImportPrivKeyCmd)(nil), flags) + MustRegisterCmd("keypoolrefill", (*KeyPoolRefillCmd)(nil), flags) + MustRegisterCmd("listaccounts", (*ListAccountsCmd)(nil), flags) + MustRegisterCmd("listaddressgroupings", (*ListAddressGroupingsCmd)(nil), flags) + MustRegisterCmd("listlockunspent", (*ListLockUnspentCmd)(nil), flags) + MustRegisterCmd("listreceivedbyaccount", (*ListReceivedByAccountCmd)(nil), flags) + MustRegisterCmd("listreceivedbyaddress", (*ListReceivedByAddressCmd)(nil), flags) + MustRegisterCmd("listsinceblock", (*ListSinceBlockCmd)(nil), flags) + MustRegisterCmd("listtransactions", (*ListTransactionsCmd)(nil), flags) + MustRegisterCmd("listunspent", (*ListUnspentCmd)(nil), flags) + MustRegisterCmd("lockunspent", (*LockUnspentCmd)(nil), flags) + MustRegisterCmd("move", (*MoveCmd)(nil), flags) + MustRegisterCmd("sendfrom", (*SendFromCmd)(nil), flags) + MustRegisterCmd("sendmany", (*SendManyCmd)(nil), flags) + MustRegisterCmd("sendtoaddress", (*SendToAddressCmd)(nil), flags) + MustRegisterCmd("setaccount", (*SetAccountCmd)(nil), flags) + MustRegisterCmd("settxfee", (*SetTxFeeCmd)(nil), flags) + MustRegisterCmd("signmessage", (*SignMessageCmd)(nil), flags) + MustRegisterCmd("signrawtransaction", (*SignRawTransactionCmd)(nil), flags) + MustRegisterCmd("walletlock", (*WalletLockCmd)(nil), flags) + MustRegisterCmd("walletpassphrase", (*WalletPassphraseCmd)(nil), flags) + MustRegisterCmd("walletpassphrasechange", (*WalletPassphraseChangeCmd)(nil), flags) +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/walletsvrcmds_test.go b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrcmds_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d4755cc1735497523c55f4787200b9f1ae623c4c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrcmds_test.go @@ -0,0 +1,1261 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestWalletSvrCmds tests all of the wallet server commands marshal and +// unmarshal into valid results include handling of optional fields being +// omitted in the marshalled command, while optional fields with defaults have +// the default assigned on unmarshalled commands. +func TestWalletSvrCmds(t *testing.T) { + t.Parallel() + + testID := int(1) + tests := []struct { + name string + newCmd func() (interface{}, error) + staticCmd func() interface{} + marshalled string + unmarshalled interface{} + }{ + { + name: "addmultisigaddress", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("addmultisigaddress", 2, []string{"031234", "035678"}) + }, + staticCmd: func() interface{} { + keys := []string{"031234", "035678"} + return btcjson.NewAddMultisigAddressCmd(2, keys, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"addmultisigaddress","params":[2,["031234","035678"]],"id":1}`, + unmarshalled: &btcjson.AddMultisigAddressCmd{ + NRequired: 2, + Keys: []string{"031234", "035678"}, + Account: nil, + }, + }, + { + name: "addmultisigaddress optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("addmultisigaddress", 2, []string{"031234", "035678"}, "test") + }, + staticCmd: func() interface{} { + keys := []string{"031234", "035678"} + return btcjson.NewAddMultisigAddressCmd(2, keys, btcjson.String("test")) + }, + marshalled: `{"jsonrpc":"1.0","method":"addmultisigaddress","params":[2,["031234","035678"],"test"],"id":1}`, + unmarshalled: &btcjson.AddMultisigAddressCmd{ + NRequired: 2, + Keys: []string{"031234", "035678"}, + Account: btcjson.String("test"), + }, + }, + { + name: "createmultisig", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("createmultisig", 2, []string{"031234", "035678"}) + }, + staticCmd: func() interface{} { + keys := []string{"031234", "035678"} + return btcjson.NewCreateMultisigCmd(2, keys) + }, + marshalled: `{"jsonrpc":"1.0","method":"createmultisig","params":[2,["031234","035678"]],"id":1}`, + unmarshalled: &btcjson.CreateMultisigCmd{ + NRequired: 2, + Keys: []string{"031234", "035678"}, + }, + }, + { + name: "dumpprivkey", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("dumpprivkey", "1Address") + }, + staticCmd: func() interface{} { + return btcjson.NewDumpPrivKeyCmd("1Address") + }, + marshalled: `{"jsonrpc":"1.0","method":"dumpprivkey","params":["1Address"],"id":1}`, + unmarshalled: &btcjson.DumpPrivKeyCmd{ + Address: "1Address", + }, + }, + { + name: "encryptwallet", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("encryptwallet", "pass") + }, + staticCmd: func() interface{} { + return btcjson.NewEncryptWalletCmd("pass") + }, + marshalled: `{"jsonrpc":"1.0","method":"encryptwallet","params":["pass"],"id":1}`, + unmarshalled: &btcjson.EncryptWalletCmd{ + Passphrase: "pass", + }, + }, + { + name: "estimatefee", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("estimatefee", 6) + }, + staticCmd: func() interface{} { + return btcjson.NewEstimateFeeCmd(6) + }, + marshalled: `{"jsonrpc":"1.0","method":"estimatefee","params":[6],"id":1}`, + unmarshalled: &btcjson.EstimateFeeCmd{ + NumBlocks: 6, + }, + }, + { + name: "estimatepriority", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("estimatepriority", 6) + }, + staticCmd: func() interface{} { + return btcjson.NewEstimatePriorityCmd(6) + }, + marshalled: `{"jsonrpc":"1.0","method":"estimatepriority","params":[6],"id":1}`, + unmarshalled: &btcjson.EstimatePriorityCmd{ + NumBlocks: 6, + }, + }, + { + name: "getaccount", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getaccount", "1Address") + }, + staticCmd: func() interface{} { + return btcjson.NewGetAccountCmd("1Address") + }, + marshalled: `{"jsonrpc":"1.0","method":"getaccount","params":["1Address"],"id":1}`, + unmarshalled: &btcjson.GetAccountCmd{ + Address: "1Address", + }, + }, + { + name: "getaccountaddress", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getaccountaddress", "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewGetAccountAddressCmd("acct") + }, + marshalled: `{"jsonrpc":"1.0","method":"getaccountaddress","params":["acct"],"id":1}`, + unmarshalled: &btcjson.GetAccountAddressCmd{ + Account: "acct", + }, + }, + { + name: "getaddressesbyaccount", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getaddressesbyaccount", "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewGetAddressesByAccountCmd("acct") + }, + marshalled: `{"jsonrpc":"1.0","method":"getaddressesbyaccount","params":["acct"],"id":1}`, + unmarshalled: &btcjson.GetAddressesByAccountCmd{ + Account: "acct", + }, + }, + { + name: "getbalance", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getbalance") + }, + staticCmd: func() interface{} { + return btcjson.NewGetBalanceCmd(nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getbalance","params":[],"id":1}`, + unmarshalled: &btcjson.GetBalanceCmd{ + Account: nil, + MinConf: btcjson.Int(1), + }, + }, + { + name: "getbalance optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getbalance", "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewGetBalanceCmd(btcjson.String("acct"), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getbalance","params":["acct"],"id":1}`, + unmarshalled: &btcjson.GetBalanceCmd{ + Account: btcjson.String("acct"), + MinConf: btcjson.Int(1), + }, + }, + { + name: "getbalance optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getbalance", "acct", 6) + }, + staticCmd: func() interface{} { + return btcjson.NewGetBalanceCmd(btcjson.String("acct"), btcjson.Int(6)) + }, + marshalled: `{"jsonrpc":"1.0","method":"getbalance","params":["acct",6],"id":1}`, + unmarshalled: &btcjson.GetBalanceCmd{ + Account: btcjson.String("acct"), + MinConf: btcjson.Int(6), + }, + }, + { + name: "getnewaddress", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getnewaddress") + }, + staticCmd: func() interface{} { + return btcjson.NewGetNewAddressCmd(nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getnewaddress","params":[],"id":1}`, + unmarshalled: &btcjson.GetNewAddressCmd{ + Account: nil, + }, + }, + { + name: "getnewaddress optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getnewaddress", "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewGetNewAddressCmd(btcjson.String("acct")) + }, + marshalled: `{"jsonrpc":"1.0","method":"getnewaddress","params":["acct"],"id":1}`, + unmarshalled: &btcjson.GetNewAddressCmd{ + Account: btcjson.String("acct"), + }, + }, + { + name: "getrawchangeaddress", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getrawchangeaddress") + }, + staticCmd: func() interface{} { + return btcjson.NewGetRawChangeAddressCmd(nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getrawchangeaddress","params":[],"id":1}`, + unmarshalled: &btcjson.GetRawChangeAddressCmd{ + Account: nil, + }, + }, + { + name: "getrawchangeaddress optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getrawchangeaddress", "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewGetRawChangeAddressCmd(btcjson.String("acct")) + }, + marshalled: `{"jsonrpc":"1.0","method":"getrawchangeaddress","params":["acct"],"id":1}`, + unmarshalled: &btcjson.GetRawChangeAddressCmd{ + Account: btcjson.String("acct"), + }, + }, + { + name: "getreceivedbyaccount", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getreceivedbyaccount", "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewGetReceivedByAccountCmd("acct", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getreceivedbyaccount","params":["acct"],"id":1}`, + unmarshalled: &btcjson.GetReceivedByAccountCmd{ + Account: "acct", + MinConf: btcjson.Int(1), + }, + }, + { + name: "getreceivedbyaccount optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getreceivedbyaccount", "acct", 6) + }, + staticCmd: func() interface{} { + return btcjson.NewGetReceivedByAccountCmd("acct", btcjson.Int(6)) + }, + marshalled: `{"jsonrpc":"1.0","method":"getreceivedbyaccount","params":["acct",6],"id":1}`, + unmarshalled: &btcjson.GetReceivedByAccountCmd{ + Account: "acct", + MinConf: btcjson.Int(6), + }, + }, + { + name: "getreceivedbyaddress", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getreceivedbyaddress", "1Address") + }, + staticCmd: func() interface{} { + return btcjson.NewGetReceivedByAddressCmd("1Address", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getreceivedbyaddress","params":["1Address"],"id":1}`, + unmarshalled: &btcjson.GetReceivedByAddressCmd{ + Address: "1Address", + MinConf: btcjson.Int(1), + }, + }, + { + name: "getreceivedbyaddress optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getreceivedbyaddress", "1Address", 6) + }, + staticCmd: func() interface{} { + return btcjson.NewGetReceivedByAddressCmd("1Address", btcjson.Int(6)) + }, + marshalled: `{"jsonrpc":"1.0","method":"getreceivedbyaddress","params":["1Address",6],"id":1}`, + unmarshalled: &btcjson.GetReceivedByAddressCmd{ + Address: "1Address", + MinConf: btcjson.Int(6), + }, + }, + { + name: "gettransaction", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("gettransaction", "123") + }, + staticCmd: func() interface{} { + return btcjson.NewGetTransactionCmd("123", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"gettransaction","params":["123"],"id":1}`, + unmarshalled: &btcjson.GetTransactionCmd{ + Txid: "123", + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "gettransaction optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("gettransaction", "123", true) + }, + staticCmd: func() interface{} { + return btcjson.NewGetTransactionCmd("123", btcjson.Bool(true)) + }, + marshalled: `{"jsonrpc":"1.0","method":"gettransaction","params":["123",true],"id":1}`, + unmarshalled: &btcjson.GetTransactionCmd{ + Txid: "123", + IncludeWatchOnly: btcjson.Bool(true), + }, + }, + { + name: "getwalletinfo", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getwalletinfo") + }, + staticCmd: func() interface{} { + return btcjson.NewGetWalletInfoCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"getwalletinfo","params":[],"id":1}`, + unmarshalled: &btcjson.GetWalletInfoCmd{}, + }, + { + name: "importprivkey", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("importprivkey", "abc") + }, + staticCmd: func() interface{} { + return btcjson.NewImportPrivKeyCmd("abc", nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"importprivkey","params":["abc"],"id":1}`, + unmarshalled: &btcjson.ImportPrivKeyCmd{ + PrivKey: "abc", + Label: nil, + Rescan: btcjson.Bool(true), + }, + }, + { + name: "importprivkey optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("importprivkey", "abc", "label") + }, + staticCmd: func() interface{} { + return btcjson.NewImportPrivKeyCmd("abc", btcjson.String("label"), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"importprivkey","params":["abc","label"],"id":1}`, + unmarshalled: &btcjson.ImportPrivKeyCmd{ + PrivKey: "abc", + Label: btcjson.String("label"), + Rescan: btcjson.Bool(true), + }, + }, + { + name: "importprivkey optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("importprivkey", "abc", "label", false) + }, + staticCmd: func() interface{} { + return btcjson.NewImportPrivKeyCmd("abc", btcjson.String("label"), btcjson.Bool(false)) + }, + marshalled: `{"jsonrpc":"1.0","method":"importprivkey","params":["abc","label",false],"id":1}`, + unmarshalled: &btcjson.ImportPrivKeyCmd{ + PrivKey: "abc", + Label: btcjson.String("label"), + Rescan: btcjson.Bool(false), + }, + }, + { + name: "keypoolrefill", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("keypoolrefill") + }, + staticCmd: func() interface{} { + return btcjson.NewKeyPoolRefillCmd(nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"keypoolrefill","params":[],"id":1}`, + unmarshalled: &btcjson.KeyPoolRefillCmd{ + NewSize: btcjson.Uint(100), + }, + }, + { + name: "keypoolrefill optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("keypoolrefill", 200) + }, + staticCmd: func() interface{} { + return btcjson.NewKeyPoolRefillCmd(btcjson.Uint(200)) + }, + marshalled: `{"jsonrpc":"1.0","method":"keypoolrefill","params":[200],"id":1}`, + unmarshalled: &btcjson.KeyPoolRefillCmd{ + NewSize: btcjson.Uint(200), + }, + }, + { + name: "listaccounts", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listaccounts") + }, + staticCmd: func() interface{} { + return btcjson.NewListAccountsCmd(nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listaccounts","params":[],"id":1}`, + unmarshalled: &btcjson.ListAccountsCmd{ + MinConf: btcjson.Int(1), + }, + }, + { + name: "listaccounts optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listaccounts", 6) + }, + staticCmd: func() interface{} { + return btcjson.NewListAccountsCmd(btcjson.Int(6)) + }, + marshalled: `{"jsonrpc":"1.0","method":"listaccounts","params":[6],"id":1}`, + unmarshalled: &btcjson.ListAccountsCmd{ + MinConf: btcjson.Int(6), + }, + }, + { + name: "listaddressgroupings", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listaddressgroupings") + }, + staticCmd: func() interface{} { + return btcjson.NewListAddressGroupingsCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"listaddressgroupings","params":[],"id":1}`, + unmarshalled: &btcjson.ListAddressGroupingsCmd{}, + }, + { + name: "listlockunspent", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listlockunspent") + }, + staticCmd: func() interface{} { + return btcjson.NewListLockUnspentCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"listlockunspent","params":[],"id":1}`, + unmarshalled: &btcjson.ListLockUnspentCmd{}, + }, + { + name: "listreceivedbyaccount", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listreceivedbyaccount") + }, + staticCmd: func() interface{} { + return btcjson.NewListReceivedByAccountCmd(nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listreceivedbyaccount","params":[],"id":1}`, + unmarshalled: &btcjson.ListReceivedByAccountCmd{ + MinConf: btcjson.Int(1), + IncludeEmpty: btcjson.Bool(false), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listreceivedbyaccount optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listreceivedbyaccount", 6) + }, + staticCmd: func() interface{} { + return btcjson.NewListReceivedByAccountCmd(btcjson.Int(6), nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listreceivedbyaccount","params":[6],"id":1}`, + unmarshalled: &btcjson.ListReceivedByAccountCmd{ + MinConf: btcjson.Int(6), + IncludeEmpty: btcjson.Bool(false), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listreceivedbyaccount optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listreceivedbyaccount", 6, true) + }, + staticCmd: func() interface{} { + return btcjson.NewListReceivedByAccountCmd(btcjson.Int(6), btcjson.Bool(true), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listreceivedbyaccount","params":[6,true],"id":1}`, + unmarshalled: &btcjson.ListReceivedByAccountCmd{ + MinConf: btcjson.Int(6), + IncludeEmpty: btcjson.Bool(true), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listreceivedbyaccount optional3", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listreceivedbyaccount", 6, true, false) + }, + staticCmd: func() interface{} { + return btcjson.NewListReceivedByAccountCmd(btcjson.Int(6), btcjson.Bool(true), btcjson.Bool(false)) + }, + marshalled: `{"jsonrpc":"1.0","method":"listreceivedbyaccount","params":[6,true,false],"id":1}`, + unmarshalled: &btcjson.ListReceivedByAccountCmd{ + MinConf: btcjson.Int(6), + IncludeEmpty: btcjson.Bool(true), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listreceivedbyaddress", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listreceivedbyaddress") + }, + staticCmd: func() interface{} { + return btcjson.NewListReceivedByAddressCmd(nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listreceivedbyaddress","params":[],"id":1}`, + unmarshalled: &btcjson.ListReceivedByAddressCmd{ + MinConf: btcjson.Int(1), + IncludeEmpty: btcjson.Bool(false), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listreceivedbyaddress optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listreceivedbyaddress", 6) + }, + staticCmd: func() interface{} { + return btcjson.NewListReceivedByAddressCmd(btcjson.Int(6), nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listreceivedbyaddress","params":[6],"id":1}`, + unmarshalled: &btcjson.ListReceivedByAddressCmd{ + MinConf: btcjson.Int(6), + IncludeEmpty: btcjson.Bool(false), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listreceivedbyaddress optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listreceivedbyaddress", 6, true) + }, + staticCmd: func() interface{} { + return btcjson.NewListReceivedByAddressCmd(btcjson.Int(6), btcjson.Bool(true), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listreceivedbyaddress","params":[6,true],"id":1}`, + unmarshalled: &btcjson.ListReceivedByAddressCmd{ + MinConf: btcjson.Int(6), + IncludeEmpty: btcjson.Bool(true), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listreceivedbyaddress optional3", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listreceivedbyaddress", 6, true, false) + }, + staticCmd: func() interface{} { + return btcjson.NewListReceivedByAddressCmd(btcjson.Int(6), btcjson.Bool(true), btcjson.Bool(false)) + }, + marshalled: `{"jsonrpc":"1.0","method":"listreceivedbyaddress","params":[6,true,false],"id":1}`, + unmarshalled: &btcjson.ListReceivedByAddressCmd{ + MinConf: btcjson.Int(6), + IncludeEmpty: btcjson.Bool(true), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listsinceblock", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listsinceblock") + }, + staticCmd: func() interface{} { + return btcjson.NewListSinceBlockCmd(nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listsinceblock","params":[],"id":1}`, + unmarshalled: &btcjson.ListSinceBlockCmd{ + BlockHash: nil, + TargetConfirmations: btcjson.Int(1), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listsinceblock optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listsinceblock", "123") + }, + staticCmd: func() interface{} { + return btcjson.NewListSinceBlockCmd(btcjson.String("123"), nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listsinceblock","params":["123"],"id":1}`, + unmarshalled: &btcjson.ListSinceBlockCmd{ + BlockHash: btcjson.String("123"), + TargetConfirmations: btcjson.Int(1), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listsinceblock optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listsinceblock", "123", 6) + }, + staticCmd: func() interface{} { + return btcjson.NewListSinceBlockCmd(btcjson.String("123"), btcjson.Int(6), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listsinceblock","params":["123",6],"id":1}`, + unmarshalled: &btcjson.ListSinceBlockCmd{ + BlockHash: btcjson.String("123"), + TargetConfirmations: btcjson.Int(6), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listsinceblock optional3", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listsinceblock", "123", 6, true) + }, + staticCmd: func() interface{} { + return btcjson.NewListSinceBlockCmd(btcjson.String("123"), btcjson.Int(6), btcjson.Bool(true)) + }, + marshalled: `{"jsonrpc":"1.0","method":"listsinceblock","params":["123",6,true],"id":1}`, + unmarshalled: &btcjson.ListSinceBlockCmd{ + BlockHash: btcjson.String("123"), + TargetConfirmations: btcjson.Int(6), + IncludeWatchOnly: btcjson.Bool(true), + }, + }, + { + name: "listtransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listtransactions") + }, + staticCmd: func() interface{} { + return btcjson.NewListTransactionsCmd(nil, nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listtransactions","params":[],"id":1}`, + unmarshalled: &btcjson.ListTransactionsCmd{ + Account: nil, + Count: btcjson.Int(10), + From: btcjson.Int(0), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listtransactions optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listtransactions", "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewListTransactionsCmd(btcjson.String("acct"), nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listtransactions","params":["acct"],"id":1}`, + unmarshalled: &btcjson.ListTransactionsCmd{ + Account: btcjson.String("acct"), + Count: btcjson.Int(10), + From: btcjson.Int(0), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listtransactions optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listtransactions", "acct", 20) + }, + staticCmd: func() interface{} { + return btcjson.NewListTransactionsCmd(btcjson.String("acct"), btcjson.Int(20), nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listtransactions","params":["acct",20],"id":1}`, + unmarshalled: &btcjson.ListTransactionsCmd{ + Account: btcjson.String("acct"), + Count: btcjson.Int(20), + From: btcjson.Int(0), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listtransactions optional3", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listtransactions", "acct", 20, 1) + }, + staticCmd: func() interface{} { + return btcjson.NewListTransactionsCmd(btcjson.String("acct"), btcjson.Int(20), + btcjson.Int(1), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listtransactions","params":["acct",20,1],"id":1}`, + unmarshalled: &btcjson.ListTransactionsCmd{ + Account: btcjson.String("acct"), + Count: btcjson.Int(20), + From: btcjson.Int(1), + IncludeWatchOnly: btcjson.Bool(false), + }, + }, + { + name: "listtransactions optional4", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listtransactions", "acct", 20, 1, true) + }, + staticCmd: func() interface{} { + return btcjson.NewListTransactionsCmd(btcjson.String("acct"), btcjson.Int(20), + btcjson.Int(1), btcjson.Bool(true)) + }, + marshalled: `{"jsonrpc":"1.0","method":"listtransactions","params":["acct",20,1,true],"id":1}`, + unmarshalled: &btcjson.ListTransactionsCmd{ + Account: btcjson.String("acct"), + Count: btcjson.Int(20), + From: btcjson.Int(1), + IncludeWatchOnly: btcjson.Bool(true), + }, + }, + { + name: "listunspent", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listunspent") + }, + staticCmd: func() interface{} { + return btcjson.NewListUnspentCmd(nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listunspent","params":[],"id":1}`, + unmarshalled: &btcjson.ListUnspentCmd{ + MinConf: btcjson.Int(1), + MaxConf: btcjson.Int(9999999), + Addresses: nil, + }, + }, + { + name: "listunspent optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listunspent", 6) + }, + staticCmd: func() interface{} { + return btcjson.NewListUnspentCmd(btcjson.Int(6), nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listunspent","params":[6],"id":1}`, + unmarshalled: &btcjson.ListUnspentCmd{ + MinConf: btcjson.Int(6), + MaxConf: btcjson.Int(9999999), + Addresses: nil, + }, + }, + { + name: "listunspent optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listunspent", 6, 100) + }, + staticCmd: func() interface{} { + return btcjson.NewListUnspentCmd(btcjson.Int(6), btcjson.Int(100), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listunspent","params":[6,100],"id":1}`, + unmarshalled: &btcjson.ListUnspentCmd{ + MinConf: btcjson.Int(6), + MaxConf: btcjson.Int(100), + Addresses: nil, + }, + }, + { + name: "listunspent optional3", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listunspent", 6, 100, []string{"1Address", "1Address2"}) + }, + staticCmd: func() interface{} { + return btcjson.NewListUnspentCmd(btcjson.Int(6), btcjson.Int(100), + &[]string{"1Address", "1Address2"}) + }, + marshalled: `{"jsonrpc":"1.0","method":"listunspent","params":[6,100,["1Address","1Address2"]],"id":1}`, + unmarshalled: &btcjson.ListUnspentCmd{ + MinConf: btcjson.Int(6), + MaxConf: btcjson.Int(100), + Addresses: &[]string{"1Address", "1Address2"}, + }, + }, + { + name: "lockunspent", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("lockunspent", true, `[{"txid":"123","vout":1}]`) + }, + staticCmd: func() interface{} { + txInputs := []btcjson.TransactionInput{ + {Txid: "123", Vout: 1}, + } + return btcjson.NewLockUnspentCmd(true, txInputs) + }, + marshalled: `{"jsonrpc":"1.0","method":"lockunspent","params":[true,[{"txid":"123","vout":1}]],"id":1}`, + unmarshalled: &btcjson.LockUnspentCmd{ + Unlock: true, + Transactions: []btcjson.TransactionInput{ + {Txid: "123", Vout: 1}, + }, + }, + }, + { + name: "move", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("move", "from", "to", 0.5) + }, + staticCmd: func() interface{} { + return btcjson.NewMoveCmd("from", "to", 0.5, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"move","params":["from","to",0.5],"id":1}`, + unmarshalled: &btcjson.MoveCmd{ + FromAccount: "from", + ToAccount: "to", + Amount: 0.5, + MinConf: btcjson.Int(1), + Comment: nil, + }, + }, + { + name: "move optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("move", "from", "to", 0.5, 6) + }, + staticCmd: func() interface{} { + return btcjson.NewMoveCmd("from", "to", 0.5, btcjson.Int(6), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"move","params":["from","to",0.5,6],"id":1}`, + unmarshalled: &btcjson.MoveCmd{ + FromAccount: "from", + ToAccount: "to", + Amount: 0.5, + MinConf: btcjson.Int(6), + Comment: nil, + }, + }, + { + name: "move optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("move", "from", "to", 0.5, 6, "comment") + }, + staticCmd: func() interface{} { + return btcjson.NewMoveCmd("from", "to", 0.5, btcjson.Int(6), btcjson.String("comment")) + }, + marshalled: `{"jsonrpc":"1.0","method":"move","params":["from","to",0.5,6,"comment"],"id":1}`, + unmarshalled: &btcjson.MoveCmd{ + FromAccount: "from", + ToAccount: "to", + Amount: 0.5, + MinConf: btcjson.Int(6), + Comment: btcjson.String("comment"), + }, + }, + { + name: "sendfrom", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("sendfrom", "from", "1Address", 0.5) + }, + staticCmd: func() interface{} { + return btcjson.NewSendFromCmd("from", "1Address", 0.5, nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"sendfrom","params":["from","1Address",0.5],"id":1}`, + unmarshalled: &btcjson.SendFromCmd{ + FromAccount: "from", + ToAddress: "1Address", + Amount: 0.5, + MinConf: btcjson.Int(1), + Comment: nil, + CommentTo: nil, + }, + }, + { + name: "sendfrom optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("sendfrom", "from", "1Address", 0.5, 6) + }, + staticCmd: func() interface{} { + return btcjson.NewSendFromCmd("from", "1Address", 0.5, btcjson.Int(6), nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"sendfrom","params":["from","1Address",0.5,6],"id":1}`, + unmarshalled: &btcjson.SendFromCmd{ + FromAccount: "from", + ToAddress: "1Address", + Amount: 0.5, + MinConf: btcjson.Int(6), + Comment: nil, + CommentTo: nil, + }, + }, + { + name: "sendfrom optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("sendfrom", "from", "1Address", 0.5, 6, "comment") + }, + staticCmd: func() interface{} { + return btcjson.NewSendFromCmd("from", "1Address", 0.5, btcjson.Int(6), + btcjson.String("comment"), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"sendfrom","params":["from","1Address",0.5,6,"comment"],"id":1}`, + unmarshalled: &btcjson.SendFromCmd{ + FromAccount: "from", + ToAddress: "1Address", + Amount: 0.5, + MinConf: btcjson.Int(6), + Comment: btcjson.String("comment"), + CommentTo: nil, + }, + }, + { + name: "sendfrom optional3", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("sendfrom", "from", "1Address", 0.5, 6, "comment", "commentto") + }, + staticCmd: func() interface{} { + return btcjson.NewSendFromCmd("from", "1Address", 0.5, btcjson.Int(6), + btcjson.String("comment"), btcjson.String("commentto")) + }, + marshalled: `{"jsonrpc":"1.0","method":"sendfrom","params":["from","1Address",0.5,6,"comment","commentto"],"id":1}`, + unmarshalled: &btcjson.SendFromCmd{ + FromAccount: "from", + ToAddress: "1Address", + Amount: 0.5, + MinConf: btcjson.Int(6), + Comment: btcjson.String("comment"), + CommentTo: btcjson.String("commentto"), + }, + }, + { + name: "sendmany", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("sendmany", "from", `{"1Address":0.5}`) + }, + staticCmd: func() interface{} { + amounts := map[string]float64{"1Address": 0.5} + return btcjson.NewSendManyCmd("from", amounts, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"sendmany","params":["from",{"1Address":0.5}],"id":1}`, + unmarshalled: &btcjson.SendManyCmd{ + FromAccount: "from", + Amounts: map[string]float64{"1Address": 0.5}, + MinConf: btcjson.Int(1), + Comment: nil, + }, + }, + { + name: "sendmany optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("sendmany", "from", `{"1Address":0.5}`, 6) + }, + staticCmd: func() interface{} { + amounts := map[string]float64{"1Address": 0.5} + return btcjson.NewSendManyCmd("from", amounts, btcjson.Int(6), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"sendmany","params":["from",{"1Address":0.5},6],"id":1}`, + unmarshalled: &btcjson.SendManyCmd{ + FromAccount: "from", + Amounts: map[string]float64{"1Address": 0.5}, + MinConf: btcjson.Int(6), + Comment: nil, + }, + }, + { + name: "sendmany optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("sendmany", "from", `{"1Address":0.5}`, 6, "comment") + }, + staticCmd: func() interface{} { + amounts := map[string]float64{"1Address": 0.5} + return btcjson.NewSendManyCmd("from", amounts, btcjson.Int(6), btcjson.String("comment")) + }, + marshalled: `{"jsonrpc":"1.0","method":"sendmany","params":["from",{"1Address":0.5},6,"comment"],"id":1}`, + unmarshalled: &btcjson.SendManyCmd{ + FromAccount: "from", + Amounts: map[string]float64{"1Address": 0.5}, + MinConf: btcjson.Int(6), + Comment: btcjson.String("comment"), + }, + }, + { + name: "sendtoaddress", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("sendtoaddress", "1Address", 0.5) + }, + staticCmd: func() interface{} { + return btcjson.NewSendToAddressCmd("1Address", 0.5, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"sendtoaddress","params":["1Address",0.5],"id":1}`, + unmarshalled: &btcjson.SendToAddressCmd{ + Address: "1Address", + Amount: 0.5, + Comment: nil, + CommentTo: nil, + }, + }, + { + name: "sendtoaddress optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("sendtoaddress", "1Address", 0.5, "comment", "commentto") + }, + staticCmd: func() interface{} { + return btcjson.NewSendToAddressCmd("1Address", 0.5, btcjson.String("comment"), + btcjson.String("commentto")) + }, + marshalled: `{"jsonrpc":"1.0","method":"sendtoaddress","params":["1Address",0.5,"comment","commentto"],"id":1}`, + unmarshalled: &btcjson.SendToAddressCmd{ + Address: "1Address", + Amount: 0.5, + Comment: btcjson.String("comment"), + CommentTo: btcjson.String("commentto"), + }, + }, + { + name: "setaccount", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("setaccount", "1Address", "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewSetAccountCmd("1Address", "acct") + }, + marshalled: `{"jsonrpc":"1.0","method":"setaccount","params":["1Address","acct"],"id":1}`, + unmarshalled: &btcjson.SetAccountCmd{ + Address: "1Address", + Account: "acct", + }, + }, + { + name: "settxfee", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("settxfee", 0.0001) + }, + staticCmd: func() interface{} { + return btcjson.NewSetTxFeeCmd(0.0001) + }, + marshalled: `{"jsonrpc":"1.0","method":"settxfee","params":[0.0001],"id":1}`, + unmarshalled: &btcjson.SetTxFeeCmd{ + Amount: 0.0001, + }, + }, + { + name: "signmessage", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("signmessage", "1Address", "message") + }, + staticCmd: func() interface{} { + return btcjson.NewSignMessageCmd("1Address", "message") + }, + marshalled: `{"jsonrpc":"1.0","method":"signmessage","params":["1Address","message"],"id":1}`, + unmarshalled: &btcjson.SignMessageCmd{ + Address: "1Address", + Message: "message", + }, + }, + { + name: "signrawtransaction", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("signrawtransaction", "001122") + }, + staticCmd: func() interface{} { + return btcjson.NewSignRawTransactionCmd("001122", nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"signrawtransaction","params":["001122"],"id":1}`, + unmarshalled: &btcjson.SignRawTransactionCmd{ + RawTx: "001122", + Inputs: nil, + PrivKeys: nil, + Flags: btcjson.String("ALL"), + }, + }, + { + name: "signrawtransaction optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("signrawtransaction", "001122", `[{"txid":"123","vout":1,"scriptPubKey":"00","redeemScript":"01"}]`) + }, + staticCmd: func() interface{} { + txInputs := []btcjson.RawTxInput{ + { + Txid: "123", + Vout: 1, + ScriptPubKey: "00", + RedeemScript: "01", + }, + } + + return btcjson.NewSignRawTransactionCmd("001122", &txInputs, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"signrawtransaction","params":["001122",[{"txid":"123","vout":1,"scriptPubKey":"00","redeemScript":"01"}]],"id":1}`, + unmarshalled: &btcjson.SignRawTransactionCmd{ + RawTx: "001122", + Inputs: &[]btcjson.RawTxInput{ + { + Txid: "123", + Vout: 1, + ScriptPubKey: "00", + RedeemScript: "01", + }, + }, + PrivKeys: nil, + Flags: btcjson.String("ALL"), + }, + }, + { + name: "signrawtransaction optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("signrawtransaction", "001122", `[]`, `["abc"]`) + }, + staticCmd: func() interface{} { + txInputs := []btcjson.RawTxInput{} + privKeys := []string{"abc"} + return btcjson.NewSignRawTransactionCmd("001122", &txInputs, &privKeys, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"signrawtransaction","params":["001122",[],["abc"]],"id":1}`, + unmarshalled: &btcjson.SignRawTransactionCmd{ + RawTx: "001122", + Inputs: &[]btcjson.RawTxInput{}, + PrivKeys: &[]string{"abc"}, + Flags: btcjson.String("ALL"), + }, + }, + { + name: "signrawtransaction optional3", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("signrawtransaction", "001122", `[]`, `[]`, "ALL") + }, + staticCmd: func() interface{} { + txInputs := []btcjson.RawTxInput{} + privKeys := []string{} + return btcjson.NewSignRawTransactionCmd("001122", &txInputs, &privKeys, + btcjson.String("ALL")) + }, + marshalled: `{"jsonrpc":"1.0","method":"signrawtransaction","params":["001122",[],[],"ALL"],"id":1}`, + unmarshalled: &btcjson.SignRawTransactionCmd{ + RawTx: "001122", + Inputs: &[]btcjson.RawTxInput{}, + PrivKeys: &[]string{}, + Flags: btcjson.String("ALL"), + }, + }, + { + name: "walletlock", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("walletlock") + }, + staticCmd: func() interface{} { + return btcjson.NewWalletLockCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"walletlock","params":[],"id":1}`, + unmarshalled: &btcjson.WalletLockCmd{}, + }, + { + name: "walletpassphrase", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("walletpassphrase", "pass", 60) + }, + staticCmd: func() interface{} { + return btcjson.NewWalletPassphraseCmd("pass", 60) + }, + marshalled: `{"jsonrpc":"1.0","method":"walletpassphrase","params":["pass",60],"id":1}`, + unmarshalled: &btcjson.WalletPassphraseCmd{ + Passphrase: "pass", + Timeout: 60, + }, + }, + { + name: "walletpassphrasechange", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("walletpassphrasechange", "old", "new") + }, + staticCmd: func() interface{} { + return btcjson.NewWalletPassphraseChangeCmd("old", "new") + }, + marshalled: `{"jsonrpc":"1.0","method":"walletpassphrasechange","params":["old","new"],"id":1}`, + unmarshalled: &btcjson.WalletPassphraseChangeCmd{ + OldPassphrase: "old", + NewPassphrase: "new", + }, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Marshal the command as created by the new static command + // creation function. + marshalled, err := btcjson.MarshalCmd(testID, test.staticCmd()) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + // Ensure the command is created without error via the generic + // new command creation function. + cmd, err := test.newCmd() + if err != nil { + t.Errorf("Test #%d (%s) unexpected NewCmd error: %v ", + i, test.name, err) + } + + // Marshal the command as created by the generic new command + // creation function. + marshalled, err = btcjson.MarshalCmd(testID, cmd) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + var request btcjson.Request + if err := json.Unmarshal(marshalled, &request); err != nil { + t.Errorf("Test #%d (%s) unexpected error while "+ + "unmarshalling JSON-RPC request: %v", i, + test.name, err) + continue + } + + cmd, err = btcjson.UnmarshalCmd(&request) + if err != nil { + t.Errorf("UnmarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !reflect.DeepEqual(cmd, test.unmarshalled) { + t.Errorf("Test #%d (%s) unexpected unmarshalled command "+ + "- got %s, want %s", i, test.name, + fmt.Sprintf("(%T) %+[1]v", cmd), + fmt.Sprintf("(%T) %+[1]v\n", test.unmarshalled)) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/walletsvrresults.go b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrresults.go new file mode 100644 index 0000000000000000000000000000000000000000..2f7d80c608dd2d4a7de12434ac924cfed164aac5 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrresults.go @@ -0,0 +1,158 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +// GetTransactionDetailsResult models the details data from the gettransaction command. +// +// This models the "short" version of the ListTransactionsResult type, which +// excludes fields common to the transaction. These common fields are instead +// part of the GetTransactionResult. +type GetTransactionDetailsResult struct { + Account string `json:"account"` + Address string `json:"address,omitempty"` + Amount float64 `json:"amount"` + Category string `json:"category"` + InvolvesWatchOnly bool `json:"involveswatchonly,omitempty"` + Fee *float64 `json:"fee,omitempty"` + Vout uint32 `json:"vout"` +} + +// GetTransactionResult models the data from the gettransaction command. +type GetTransactionResult struct { + Amount float64 `json:"amount"` + Fee float64 `json:"fee,omitempty"` + Confirmations int64 `json:"confirmations"` + BlockHash string `json:"blockhash"` + BlockIndex int64 `json:"blockindex"` + BlockTime int64 `json:"blocktime"` + TxID string `json:"txid"` + WalletConflicts []string `json:"walletconflicts"` + Time int64 `json:"time"` + TimeReceived int64 `json:"timereceived"` + Details []GetTransactionDetailsResult `json:"details"` + Hex string `json:"hex"` +} + +// InfoWalletResult models the data returned by the wallet server getinfo +// command. +type InfoWalletResult struct { + Version int32 `json:"version"` + ProtocolVersion int32 `json:"protocolversion"` + WalletVersion int32 `json:"walletversion"` + Balance float64 `json:"balance"` + Blocks int32 `json:"blocks"` + TimeOffset int64 `json:"timeoffset"` + Connections int32 `json:"connections"` + Proxy string `json:"proxy"` + Difficulty float64 `json:"difficulty"` + TestNet bool `json:"testnet"` + KeypoolOldest int64 `json:"keypoololdest"` + KeypoolSize int32 `json:"keypoolsize"` + UnlockedUntil int64 `json:"unlocked_until"` + PaytxFee float64 `json:"paytxfee"` + RelayFee float64 `json:"relayfee"` + Errors string `json:"errors"` +} + +// ListTransactionsResult models the data from the listtransactions command. +type ListTransactionsResult struct { + Account string `json:"account"` + Address string `json:"address,omitempty"` + Amount float64 `json:"amount"` + BlockHash string `json:"blockhash,omitempty"` + BlockIndex *int64 `json:"blockindex,omitempty"` + BlockTime int64 `json:"blocktime,omitempty"` + Category string `json:"category"` + Confirmations int64 `json:"confirmations"` + Fee *float64 `json:"fee,omitempty"` + Generated bool `json:"generated,omitempty"` + InvolvesWatchOnly bool `json:"involveswatchonly,omitempty"` + Time int64 `json:"time"` + TimeReceived int64 `json:"timereceived"` + TxID string `json:"txid"` + Vout uint32 `json:"vout"` + WalletConflicts []string `json:"walletconflicts"` + Comment string `json:"comment,omitempty"` + OtherAccount string `json:"otheraccount,omitempty"` +} + +// ListReceivedByAccountResult models the data from the listreceivedbyaccount +// command. +type ListReceivedByAccountResult struct { + Account string `json:"account"` + Amount float64 `json:"amount"` + Confirmations uint64 `json:"confirmations"` +} + +// ListReceivedByAddressResult models the data from the listreceivedbyaddress +// command. +type ListReceivedByAddressResult struct { + Account string `json:"account"` + Address string `json:"address"` + Amount float64 `json:"amount"` + Confirmations uint64 `json:"confirmations"` + TxIDs []string `json:"txids,omitempty"` + InvolvesWatchonly bool `json:"involvesWatchonly,omitempty"` +} + +// ListSinceBlockResult models the data from the listsinceblock command. +type ListSinceBlockResult struct { + Transactions []ListTransactionsResult `json:"transactions"` + LastBlock string `json:"lastblock"` +} + +// ListUnspentResult models a successful response from the listunspent request. +type ListUnspentResult struct { + TxID string `json:"txid"` + Vout uint32 `json:"vout"` + Address string `json:"address"` + Account string `json:"account"` + ScriptPubKey string `json:"scriptPubKey"` + RedeemScript string `json:"redeemScript,omitempty"` + Amount float64 `json:"amount"` + Confirmations int64 `json:"confirmations"` + Spendable bool `json:"spendable"` +} + +// SignRawTransactionError models the data that contains script verification +// errors from the signrawtransaction request. +type SignRawTransactionError struct { + TxID string `json:"txid"` + Vout uint32 `json:"vout"` + ScriptSig string `json:"scriptSig"` + Sequence uint32 `json:"sequence"` + Error string `json:"error"` +} + +// SignRawTransactionResult models the data from the signrawtransaction +// command. +type SignRawTransactionResult struct { + Hex string `json:"hex"` + Complete bool `json:"complete"` + Errors []SignRawTransactionError `json:"errors,omitempty"` +} + +// ValidateAddressWalletResult models the data returned by the wallet server +// validateaddress command. +type ValidateAddressWalletResult struct { + IsValid bool `json:"isvalid"` + Address string `json:"address,omitempty"` + IsMine bool `json:"ismine,omitempty"` + IsWatchOnly bool `json:"iswatchonly,omitempty"` + IsScript bool `json:"isscript,omitempty"` + PubKey string `json:"pubkey,omitempty"` + IsCompressed bool `json:"iscompressed,omitempty"` + Account string `json:"account,omitempty"` + Addresses []string `json:"addresses,omitempty"` + Hex string `json:"hex,omitempty"` + Script string `json:"script,omitempty"` + SigsRequired int32 `json:"sigsrequired,omitempty"` +} + +// GetBestBlockResult models the data from the getbestblock command. +type GetBestBlockResult struct { + Hash string `json:"hash"` + Height int32 `json:"height"` +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/walletsvrwscmds.go b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrwscmds.go new file mode 100644 index 0000000000000000000000000000000000000000..e1e60fbea2c54b1e83c69f44e88928ea0de347f6 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrwscmds.go @@ -0,0 +1,128 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson + +// NOTE: This file is intended to house the RPC commands that are supported by +// a wallet server, but are only available via websockets. + +// CreateEncryptedWalletCmd defines the createencryptedwallet JSON-RPC command. +type CreateEncryptedWalletCmd struct { + Passphrase string +} + +// NewCreateEncryptedWalletCmd returns a new instance which can be used to issue +// a createencryptedwallet JSON-RPC command. +func NewCreateEncryptedWalletCmd(passphrase string) *CreateEncryptedWalletCmd { + return &CreateEncryptedWalletCmd{ + Passphrase: passphrase, + } +} + +// ExportWatchingWalletCmd defines the exportwatchingwallet JSON-RPC command. +type ExportWatchingWalletCmd struct { + Account *string + Download *bool `jsonrpcdefault:"false"` +} + +// NewExportWatchingWalletCmd returns a new instance which can be used to issue +// a exportwatchingwallet JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewExportWatchingWalletCmd(account *string, download *bool) *ExportWatchingWalletCmd { + return &ExportWatchingWalletCmd{ + Account: account, + Download: download, + } +} + +// GetUnconfirmedBalanceCmd defines the getunconfirmedbalance JSON-RPC command. +type GetUnconfirmedBalanceCmd struct { + Account *string +} + +// NewGetUnconfirmedBalanceCmd returns a new instance which can be used to issue +// a getunconfirmedbalance JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewGetUnconfirmedBalanceCmd(account *string) *GetUnconfirmedBalanceCmd { + return &GetUnconfirmedBalanceCmd{ + Account: account, + } +} + +// ListAddressTransactionsCmd defines the listaddresstransactions JSON-RPC +// command. +type ListAddressTransactionsCmd struct { + Addresses []string + Account *string +} + +// NewListAddressTransactionsCmd returns a new instance which can be used to +// issue a listaddresstransactions JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewListAddressTransactionsCmd(addresses []string, account *string) *ListAddressTransactionsCmd { + return &ListAddressTransactionsCmd{ + Addresses: addresses, + Account: account, + } +} + +// ListAllTransactionsCmd defines the listalltransactions JSON-RPC command. +type ListAllTransactionsCmd struct { + Account *string +} + +// NewListAllTransactionsCmd returns a new instance which can be used to issue a +// listalltransactions JSON-RPC command. +// +// The parameters which are pointers indicate they are optional. Passing nil +// for optional parameters will use the default value. +func NewListAllTransactionsCmd(account *string) *ListAllTransactionsCmd { + return &ListAllTransactionsCmd{ + Account: account, + } +} + +// RecoverAddressesCmd defines the recoveraddresses JSON-RPC command. +type RecoverAddressesCmd struct { + Account string + N int +} + +// NewRecoverAddressesCmd returns a new instance which can be used to issue a +// recoveraddresses JSON-RPC command. +func NewRecoverAddressesCmd(account string, n int) *RecoverAddressesCmd { + return &RecoverAddressesCmd{ + Account: account, + N: n, + } +} + +// WalletIsLockedCmd defines the walletislocked JSON-RPC command. +type WalletIsLockedCmd struct{} + +// NewWalletIsLockedCmd returns a new instance which can be used to issue a +// walletislocked JSON-RPC command. +func NewWalletIsLockedCmd() *WalletIsLockedCmd { + return &WalletIsLockedCmd{} +} + +func init() { + // The commands in this file are only usable with a wallet server via + // websockets. + flags := UFWalletOnly | UFWebsocketOnly + + MustRegisterCmd("createencryptedwallet", (*CreateEncryptedWalletCmd)(nil), flags) + MustRegisterCmd("exportwatchingwallet", (*ExportWatchingWalletCmd)(nil), flags) + MustRegisterCmd("getunconfirmedbalance", (*GetUnconfirmedBalanceCmd)(nil), flags) + MustRegisterCmd("listaddresstransactions", (*ListAddressTransactionsCmd)(nil), flags) + MustRegisterCmd("listalltransactions", (*ListAllTransactionsCmd)(nil), flags) + MustRegisterCmd("recoveraddresses", (*RecoverAddressesCmd)(nil), flags) + MustRegisterCmd("walletislocked", (*WalletIsLockedCmd)(nil), flags) +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/walletsvrwscmds_test.go b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrwscmds_test.go new file mode 100644 index 0000000000000000000000000000000000000000..17144b6ea7d900551ad914eabd48e0091ce1804e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrwscmds_test.go @@ -0,0 +1,259 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestWalletSvrWsCmds tests all of the wallet server websocket-specific +// commands marshal and unmarshal into valid results include handling of +// optional fields being omitted in the marshalled command, while optional +// fields with defaults have the default assigned on unmarshalled commands. +func TestWalletSvrWsCmds(t *testing.T) { + t.Parallel() + + testID := int(1) + tests := []struct { + name string + newCmd func() (interface{}, error) + staticCmd func() interface{} + marshalled string + unmarshalled interface{} + }{ + { + name: "createencryptedwallet", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("createencryptedwallet", "pass") + }, + staticCmd: func() interface{} { + return btcjson.NewCreateEncryptedWalletCmd("pass") + }, + marshalled: `{"jsonrpc":"1.0","method":"createencryptedwallet","params":["pass"],"id":1}`, + unmarshalled: &btcjson.CreateEncryptedWalletCmd{Passphrase: "pass"}, + }, + { + name: "exportwatchingwallet", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("exportwatchingwallet") + }, + staticCmd: func() interface{} { + return btcjson.NewExportWatchingWalletCmd(nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"exportwatchingwallet","params":[],"id":1}`, + unmarshalled: &btcjson.ExportWatchingWalletCmd{ + Account: nil, + Download: btcjson.Bool(false), + }, + }, + { + name: "exportwatchingwallet optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("exportwatchingwallet", "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewExportWatchingWalletCmd(btcjson.String("acct"), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"exportwatchingwallet","params":["acct"],"id":1}`, + unmarshalled: &btcjson.ExportWatchingWalletCmd{ + Account: btcjson.String("acct"), + Download: btcjson.Bool(false), + }, + }, + { + name: "exportwatchingwallet optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("exportwatchingwallet", "acct", true) + }, + staticCmd: func() interface{} { + return btcjson.NewExportWatchingWalletCmd(btcjson.String("acct"), + btcjson.Bool(true)) + }, + marshalled: `{"jsonrpc":"1.0","method":"exportwatchingwallet","params":["acct",true],"id":1}`, + unmarshalled: &btcjson.ExportWatchingWalletCmd{ + Account: btcjson.String("acct"), + Download: btcjson.Bool(true), + }, + }, + { + name: "getunconfirmedbalance", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getunconfirmedbalance") + }, + staticCmd: func() interface{} { + return btcjson.NewGetUnconfirmedBalanceCmd(nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"getunconfirmedbalance","params":[],"id":1}`, + unmarshalled: &btcjson.GetUnconfirmedBalanceCmd{ + Account: nil, + }, + }, + { + name: "getunconfirmedbalance optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("getunconfirmedbalance", "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewGetUnconfirmedBalanceCmd(btcjson.String("acct")) + }, + marshalled: `{"jsonrpc":"1.0","method":"getunconfirmedbalance","params":["acct"],"id":1}`, + unmarshalled: &btcjson.GetUnconfirmedBalanceCmd{ + Account: btcjson.String("acct"), + }, + }, + { + name: "listaddresstransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listaddresstransactions", `["1Address"]`) + }, + staticCmd: func() interface{} { + return btcjson.NewListAddressTransactionsCmd([]string{"1Address"}, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listaddresstransactions","params":[["1Address"]],"id":1}`, + unmarshalled: &btcjson.ListAddressTransactionsCmd{ + Addresses: []string{"1Address"}, + Account: nil, + }, + }, + { + name: "listaddresstransactions optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listaddresstransactions", `["1Address"]`, "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewListAddressTransactionsCmd([]string{"1Address"}, + btcjson.String("acct")) + }, + marshalled: `{"jsonrpc":"1.0","method":"listaddresstransactions","params":[["1Address"],"acct"],"id":1}`, + unmarshalled: &btcjson.ListAddressTransactionsCmd{ + Addresses: []string{"1Address"}, + Account: btcjson.String("acct"), + }, + }, + { + name: "listalltransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listalltransactions") + }, + staticCmd: func() interface{} { + return btcjson.NewListAllTransactionsCmd(nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"listalltransactions","params":[],"id":1}`, + unmarshalled: &btcjson.ListAllTransactionsCmd{ + Account: nil, + }, + }, + { + name: "listalltransactions optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("listalltransactions", "acct") + }, + staticCmd: func() interface{} { + return btcjson.NewListAllTransactionsCmd(btcjson.String("acct")) + }, + marshalled: `{"jsonrpc":"1.0","method":"listalltransactions","params":["acct"],"id":1}`, + unmarshalled: &btcjson.ListAllTransactionsCmd{ + Account: btcjson.String("acct"), + }, + }, + { + name: "recoveraddresses", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("recoveraddresses", "acct", 10) + }, + staticCmd: func() interface{} { + return btcjson.NewRecoverAddressesCmd("acct", 10) + }, + marshalled: `{"jsonrpc":"1.0","method":"recoveraddresses","params":["acct",10],"id":1}`, + unmarshalled: &btcjson.RecoverAddressesCmd{ + Account: "acct", + N: 10, + }, + }, + { + name: "walletislocked", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("walletislocked") + }, + staticCmd: func() interface{} { + return btcjson.NewWalletIsLockedCmd() + }, + marshalled: `{"jsonrpc":"1.0","method":"walletislocked","params":[],"id":1}`, + unmarshalled: &btcjson.WalletIsLockedCmd{}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Marshal the command as created by the new static command + // creation function. + marshalled, err := btcjson.MarshalCmd(testID, test.staticCmd()) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + // Ensure the command is created without error via the generic + // new command creation function. + cmd, err := test.newCmd() + if err != nil { + t.Errorf("Test #%d (%s) unexpected NewCmd error: %v ", + i, test.name, err) + } + + // Marshal the command as created by the generic new command + // creation function. + marshalled, err = btcjson.MarshalCmd(testID, cmd) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + var request btcjson.Request + if err := json.Unmarshal(marshalled, &request); err != nil { + t.Errorf("Test #%d (%s) unexpected error while "+ + "unmarshalling JSON-RPC request: %v", i, + test.name, err) + continue + } + + cmd, err = btcjson.UnmarshalCmd(&request) + if err != nil { + t.Errorf("UnmarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !reflect.DeepEqual(cmd, test.unmarshalled) { + t.Errorf("Test #%d (%s) unexpected unmarshalled command "+ + "- got %s, want %s", i, test.name, + fmt.Sprintf("(%T) %+[1]v", cmd), + fmt.Sprintf("(%T) %+[1]v\n", test.unmarshalled)) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/walletsvrwsntfns.go b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrwsntfns.go new file mode 100644 index 0000000000000000000000000000000000000000..26dc15089bbd037e18304877d8b87507a6d59e7c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrwsntfns.go @@ -0,0 +1,95 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// NOTE: This file is intended to house the RPC websocket notifications that are +// supported by a wallet server. + +package btcjson + +const ( + // AccountBalanceNtfnMethod is the method used for account balance + // notifications. + AccountBalanceNtfnMethod = "accountbalance" + + // BtcdConnectedNtfnMethod is the method used for notifications when + // a wallet server is connected to a chain server. + BtcdConnectedNtfnMethod = "btcdconnected" + + // WalletLockStateNtfnMethod is the method used to notify the lock state + // of a wallet has changed. + WalletLockStateNtfnMethod = "walletlockstate" + + // NewTxNtfnMethod is the method used to notify that a wallet server has + // added a new transaction to the transaciton store. + NewTxNtfnMethod = "newtx" +) + +// AccountBalanceNtfn defines the accountbalance JSON-RPC notification. +type AccountBalanceNtfn struct { + Account string + Balance float64 // In BTC + Confirmed bool // Whether Balance is confirmed or unconfirmed. +} + +// NewAccountBalanceNtfn returns a new instance which can be used to issue an +// accountbalance JSON-RPC notification. +func NewAccountBalanceNtfn(account string, balance float64, confirmed bool) *AccountBalanceNtfn { + return &AccountBalanceNtfn{ + Account: account, + Balance: balance, + Confirmed: confirmed, + } +} + +// BtcdConnectedNtfn defines the btcdconnected JSON-RPC notification. +type BtcdConnectedNtfn struct { + Connected bool +} + +// NewBtcdConnectedNtfn returns a new instance which can be used to issue a +// btcdconnected JSON-RPC notification. +func NewBtcdConnectedNtfn(connected bool) *BtcdConnectedNtfn { + return &BtcdConnectedNtfn{ + Connected: connected, + } +} + +// WalletLockStateNtfn defines the walletlockstate JSON-RPC notification. +type WalletLockStateNtfn struct { + Locked bool +} + +// NewWalletLockStateNtfn returns a new instance which can be used to issue a +// walletlockstate JSON-RPC notification. +func NewWalletLockStateNtfn(locked bool) *WalletLockStateNtfn { + return &WalletLockStateNtfn{ + Locked: locked, + } +} + +// NewTxNtfn defines the newtx JSON-RPC notification. +type NewTxNtfn struct { + Account string + Details ListTransactionsResult +} + +// NewNewTxNtfn returns a new instance which can be used to issue a newtx +// JSON-RPC notification. +func NewNewTxNtfn(account string, details ListTransactionsResult) *NewTxNtfn { + return &NewTxNtfn{ + Account: account, + Details: details, + } +} + +func init() { + // The commands in this file are only usable with a wallet server via + // websockets and are notifications. + flags := UFWalletOnly | UFWebsocketOnly | UFNotification + + MustRegisterCmd(AccountBalanceNtfnMethod, (*AccountBalanceNtfn)(nil), flags) + MustRegisterCmd(BtcdConnectedNtfnMethod, (*BtcdConnectedNtfn)(nil), flags) + MustRegisterCmd(WalletLockStateNtfnMethod, (*WalletLockStateNtfn)(nil), flags) + MustRegisterCmd(NewTxNtfnMethod, (*NewTxNtfn)(nil), flags) +} diff --git a/vendor/github.com/btcsuite/btcd/btcjson/walletsvrwsntfns_test.go b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrwsntfns_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a1b267c5bb4e3020c75e4258398cbbcd06b1ebdc --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/btcjson/walletsvrwsntfns_test.go @@ -0,0 +1,181 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcjson_test + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/btcsuite/btcd/btcjson" +) + +// TestWalletSvrWsNtfns tests all of the chain server websocket-specific +// notifications marshal and unmarshal into valid results include handling of +// optional fields being omitted in the marshalled command, while optional +// fields with defaults have the default assigned on unmarshalled commands. +func TestWalletSvrWsNtfns(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + newNtfn func() (interface{}, error) + staticNtfn func() interface{} + marshalled string + unmarshalled interface{} + }{ + { + name: "accountbalance", + newNtfn: func() (interface{}, error) { + return btcjson.NewCmd("accountbalance", "acct", 1.25, true) + }, + staticNtfn: func() interface{} { + return btcjson.NewAccountBalanceNtfn("acct", 1.25, true) + }, + marshalled: `{"jsonrpc":"1.0","method":"accountbalance","params":["acct",1.25,true],"id":null}`, + unmarshalled: &btcjson.AccountBalanceNtfn{ + Account: "acct", + Balance: 1.25, + Confirmed: true, + }, + }, + { + name: "btcdconnected", + newNtfn: func() (interface{}, error) { + return btcjson.NewCmd("btcdconnected", true) + }, + staticNtfn: func() interface{} { + return btcjson.NewBtcdConnectedNtfn(true) + }, + marshalled: `{"jsonrpc":"1.0","method":"btcdconnected","params":[true],"id":null}`, + unmarshalled: &btcjson.BtcdConnectedNtfn{ + Connected: true, + }, + }, + { + name: "walletlockstate", + newNtfn: func() (interface{}, error) { + return btcjson.NewCmd("walletlockstate", true) + }, + staticNtfn: func() interface{} { + return btcjson.NewWalletLockStateNtfn(true) + }, + marshalled: `{"jsonrpc":"1.0","method":"walletlockstate","params":[true],"id":null}`, + unmarshalled: &btcjson.WalletLockStateNtfn{ + Locked: true, + }, + }, + { + name: "newtx", + newNtfn: func() (interface{}, error) { + return btcjson.NewCmd("newtx", "acct", `{"account":"acct","address":"1Address","category":"send","amount":1.5,"fee":0.0001,"confirmations":1,"txid":"456","walletconflicts":[],"time":12345678,"timereceived":12345876,"vout":789,"otheraccount":"otheracct"}`) + }, + staticNtfn: func() interface{} { + result := btcjson.ListTransactionsResult{ + Account: "acct", + Address: "1Address", + Category: "send", + Amount: 1.5, + Fee: btcjson.Float64(0.0001), + Confirmations: 1, + TxID: "456", + WalletConflicts: []string{}, + Time: 12345678, + TimeReceived: 12345876, + Vout: 789, + OtherAccount: "otheracct", + } + return btcjson.NewNewTxNtfn("acct", result) + }, + marshalled: `{"jsonrpc":"1.0","method":"newtx","params":["acct",{"account":"acct","address":"1Address","amount":1.5,"category":"send","confirmations":1,"fee":0.0001,"time":12345678,"timereceived":12345876,"txid":"456","vout":789,"walletconflicts":[],"otheraccount":"otheracct"}],"id":null}`, + unmarshalled: &btcjson.NewTxNtfn{ + Account: "acct", + Details: btcjson.ListTransactionsResult{ + Account: "acct", + Address: "1Address", + Category: "send", + Amount: 1.5, + Fee: btcjson.Float64(0.0001), + Confirmations: 1, + TxID: "456", + WalletConflicts: []string{}, + Time: 12345678, + TimeReceived: 12345876, + Vout: 789, + OtherAccount: "otheracct", + }, + }, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Marshal the notification as created by the new static + // creation function. The ID is nil for notifications. + marshalled, err := btcjson.MarshalCmd(nil, test.staticNtfn()) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + // Ensure the notification is created without error via the + // generic new notification creation function. + cmd, err := test.newNtfn() + if err != nil { + t.Errorf("Test #%d (%s) unexpected NewCmd error: %v ", + i, test.name, err) + } + + // Marshal the notification as created by the generic new + // notification creation function. The ID is nil for + // notifications. + marshalled, err = btcjson.MarshalCmd(nil, cmd) + if err != nil { + t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !bytes.Equal(marshalled, []byte(test.marshalled)) { + t.Errorf("Test #%d (%s) unexpected marshalled data - "+ + "got %s, want %s", i, test.name, marshalled, + test.marshalled) + continue + } + + var request btcjson.Request + if err := json.Unmarshal(marshalled, &request); err != nil { + t.Errorf("Test #%d (%s) unexpected error while "+ + "unmarshalling JSON-RPC request: %v", i, + test.name, err) + continue + } + + cmd, err = btcjson.UnmarshalCmd(&request) + if err != nil { + t.Errorf("UnmarshalCmd #%d (%s) unexpected error: %v", i, + test.name, err) + continue + } + + if !reflect.DeepEqual(cmd, test.unmarshalled) { + t.Errorf("Test #%d (%s) unexpected unmarshalled command "+ + "- got %s, want %s", i, test.name, + fmt.Sprintf("(%T) %+[1]v", cmd), + fmt.Sprintf("(%T) %+[1]v\n", test.unmarshalled)) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/chaincfg/README.md b/vendor/github.com/btcsuite/btcd/chaincfg/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ae28c16812f54125622673e51e0ede77043bbdce --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/chaincfg/README.md @@ -0,0 +1,87 @@ +chaincfg +======== + +[] +(https://travis-ci.org/btcsuite/btcd) [![ISC License] +(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[] +(http://godoc.org/github.com/btcsuite/btcd/chaincfg) + +Package chaincfg defines chain configuration parameters for the three standard +Bitcoin networks and provides the ability for callers to define their own custom +Bitcoin networks. + +Although this package was primarily written for btcd, it has intentionally been +designed so it can be used as a standalone package for any projects needing to +use parameters for the standard Bitcoin networks or for projects needing to +define their own network. + +## Sample Use + +```Go +package main + +import ( + "flag" + "fmt" + "log" + + "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcd/chaincfg" +) + +var testnet = flag.Bool("testnet", false, "operate on the testnet Bitcoin network") + +// By default (without -testnet), use mainnet. +var chainParams = &chaincfg.MainNetParams + +func main() { + flag.Parse() + + // Modify active network parameters if operating on testnet. + if *testnet { + chainParams = &chaincfg.TestNet3Params + } + + // later... + + // Create and print new payment address, specific to the active network. + pubKeyHash := make([]byte, 20) + addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, chainParams) + if err != nil { + log.Fatal(err) + } + fmt.Println(addr) +} +``` + +## Installation and Updating + +```bash +$ go get -u github.com/btcsuite/btcd/chaincfg +``` + +## GPG Verification Key + +All official release tags are signed by Conformal so users can ensure the code +has not been tampered with and is coming from the btcsuite developers. To +verify the signature perform the following: + +- Download the public key from the Conformal website at + https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt + +- Import the public key into your GPG keyring: + ```bash + gpg --import GIT-GPG-KEY-conformal.txt + ``` + +- Verify the release tag with the following command where `TAG_NAME` is a + placeholder for the specific tag: + ```bash + git tag -v TAG_NAME + ``` + +## License + +Package chaincfg is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/vendor/github.com/btcsuite/btcd/chaincfg/doc.go b/vendor/github.com/btcsuite/btcd/chaincfg/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..3659adbf4668ecc27c86478e7fd4fb9899acd44f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/chaincfg/doc.go @@ -0,0 +1,61 @@ +// Package chaincfg defines chain configuration parameters. +// +// In addition to the main Bitcoin network, which is intended for the transfer +// of monetary value, there also exists two currently active standard networks: +// regression test and testnet (version 3). These networks are incompatible +// with each other (each sharing a different genesis block) and software should +// handle errors where input intended for one network is used on an application +// instance running on a different network. +// +// For library packages, chaincfg provides the ability to lookup chain +// parameters and encoding magics when passed a *Params. Older APIs not updated +// to the new convention of passing a *Params may lookup the parameters for a +// wire.BitcoinNet using ParamsForNet, but be aware that this usage is +// deprecated and will be removed from chaincfg in the future. +// +// For main packages, a (typically global) var may be assigned the address of +// one of the standard Param vars for use as the application's "active" network. +// When a network parameter is needed, it may then be looked up through this +// variable (either directly, or hidden in a library call). +// +// package main +// +// import ( +// "flag" +// "fmt" +// "log" +// +// "github.com/btcsuite/btcutil" +// "github.com/btcsuite/btcd/chaincfg" +// ) +// +// var testnet = flag.Bool("testnet", false, "operate on the testnet Bitcoin network") +// +// // By default (without -testnet), use mainnet. +// var chainParams = &chaincfg.MainNetParams +// +// func main() { +// flag.Parse() +// +// // Modify active network parameters if operating on testnet. +// if *testnet { +// chainParams = &chaincfg.TestNet3Params +// } +// +// // later... +// +// // Create and print new payment address, specific to the active network. +// pubKeyHash := make([]byte, 20) +// addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, chainParams) +// if err != nil { +// log.Fatal(err) +// } +// fmt.Println(addr) +// } +// +// If an application does not use one of the three standard Bitcoin networks, +// a new Params struct may be created which defines the parameters for the +// non-standard network. As a general rule of thumb, all network parameters +// should be unique to the network, but parameter collisions can still occur +// (unfortunately, this is the case with regtest and testnet3 sharing magics). +package chaincfg diff --git a/vendor/github.com/btcsuite/btcd/chaincfg/genesis.go b/vendor/github.com/btcsuite/btcd/chaincfg/genesis.go new file mode 100644 index 0000000000000000000000000000000000000000..396c8392616b1c6276500160c1d31eaa51cd93f2 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/chaincfg/genesis.go @@ -0,0 +1,171 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package chaincfg + +import ( + "time" + + "github.com/btcsuite/btcd/wire" +) + +// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for +// the main network, regression test network, and test network (version 3). +var genesisCoinbaseTx = wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{ + 0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, 0x45, /* |.......E| */ + 0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6d, 0x65, /* |The Time| */ + 0x73, 0x20, 0x30, 0x33, 0x2f, 0x4a, 0x61, 0x6e, /* |s 03/Jan| */ + 0x2f, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, 0x68, /* |/2009 Ch| */ + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x6f, 0x72, /* |ancellor| */ + 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x72, 0x69, 0x6e, /* | on brin| */ + 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x63, /* |k of sec|*/ + 0x6f, 0x6e, 0x64, 0x20, 0x62, 0x61, 0x69, 0x6c, /* |ond bail| */ + 0x6f, 0x75, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, /* |out for |*/ + 0x62, 0x61, 0x6e, 0x6b, 0x73, /* |banks| */ + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 0x12a05f200, + PkScript: []byte{ + 0x41, 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, /* |A.g....U| */ + 0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, /* |H'.g..q0| */ + 0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, /* |..\..(.9| */ + 0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, /* |..yb...a| */ + 0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, /* |..I..?L.| */ + 0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, /* |8..U....| */ + 0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, /* |..\8M...| */ + 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, /* |.W.Lp+k.| */ + 0x1d, 0x5f, 0xac, /* |._.| */ + }, + }, + }, + LockTime: 0, +} + +// genesisHash is the hash of the first block in the block chain for the main +// network (genesis block). +var genesisHash = wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, +}) + +// genesisMerkleRoot is the hash of the first transaction in the genesis block +// for the main network. +var genesisMerkleRoot = wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, + 0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61, + 0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32, + 0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, +}) + +// genesisBlock defines the genesis block of the block chain which serves as the +// public transaction ledger for the main network. +var genesisBlock = wire.MsgBlock{ + Header: wire.BlockHeader{ + Version: 1, + PrevBlock: wire.ShaHash{}, // 0000000000000000000000000000000000000000000000000000000000000000 + MerkleRoot: genesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b + Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 18:15:05 +0000 UTC + Bits: 0x1d00ffff, // 486604799 [00000000ffff0000000000000000000000000000000000000000000000000000] + Nonce: 0x7c2bac1d, // 2083236893 + }, + Transactions: []*wire.MsgTx{&genesisCoinbaseTx}, +} + +// regTestGenesisHash is the hash of the first block in the block chain for the +// regression test network (genesis block). +var regTestGenesisHash = wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, + 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, + 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, + 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, +}) + +// regTestGenesisMerkleRoot is the hash of the first transaction in the genesis +// block for the regression test network. It is the same as the merkle root for +// the main network. +var regTestGenesisMerkleRoot = genesisMerkleRoot + +// regTestGenesisBlock defines the genesis block of the block chain which serves +// as the public transaction ledger for the regression test network. +var regTestGenesisBlock = wire.MsgBlock{ + Header: wire.BlockHeader{ + Version: 1, + PrevBlock: wire.ShaHash{}, // 0000000000000000000000000000000000000000000000000000000000000000 + MerkleRoot: regTestGenesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b + Timestamp: time.Unix(1296688602, 0), // 2011-02-02 23:16:42 +0000 UTC + Bits: 0x207fffff, // 545259519 [7fffff0000000000000000000000000000000000000000000000000000000000] + Nonce: 2, + }, + Transactions: []*wire.MsgTx{&genesisCoinbaseTx}, +} + +// testNet3GenesisHash is the hash of the first block in the block chain for the +// test network (version 3). +var testNet3GenesisHash = wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0x43, 0x49, 0x7f, 0xd7, 0xf8, 0x26, 0x95, 0x71, + 0x08, 0xf4, 0xa3, 0x0f, 0xd9, 0xce, 0xc3, 0xae, + 0xba, 0x79, 0x97, 0x20, 0x84, 0xe9, 0x0e, 0xad, + 0x01, 0xea, 0x33, 0x09, 0x00, 0x00, 0x00, 0x00, +}) + +// testNet3GenesisMerkleRoot is the hash of the first transaction in the genesis +// block for the test network (version 3). It is the same as the merkle root +// for the main network. +var testNet3GenesisMerkleRoot = genesisMerkleRoot + +// testNet3GenesisBlock defines the genesis block of the block chain which +// serves as the public transaction ledger for the test network (version 3). +var testNet3GenesisBlock = wire.MsgBlock{ + Header: wire.BlockHeader{ + Version: 1, + PrevBlock: wire.ShaHash{}, // 0000000000000000000000000000000000000000000000000000000000000000 + MerkleRoot: testNet3GenesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b + Timestamp: time.Unix(1296688602, 0), // 2011-02-02 23:16:42 +0000 UTC + Bits: 0x1d00ffff, // 486604799 [00000000ffff0000000000000000000000000000000000000000000000000000] + Nonce: 0x18aea41a, // 414098458 + }, + Transactions: []*wire.MsgTx{&genesisCoinbaseTx}, +} + +// simNetGenesisHash is the hash of the first block in the block chain for the +// simulation test network. +var simNetGenesisHash = wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0xf6, 0x7a, 0xd7, 0x69, 0x5d, 0x9b, 0x66, 0x2a, + 0x72, 0xff, 0x3d, 0x8e, 0xdb, 0xbb, 0x2d, 0xe0, + 0xbf, 0xa6, 0x7b, 0x13, 0x97, 0x4b, 0xb9, 0x91, + 0x0d, 0x11, 0x6d, 0x5c, 0xbd, 0x86, 0x3e, 0x68, +}) + +// simNetGenesisMerkleRoot is the hash of the first transaction in the genesis +// block for the simulation test network. It is the same as the merkle root for +// the main network. +var simNetGenesisMerkleRoot = genesisMerkleRoot + +// simNetGenesisBlock defines the genesis block of the block chain which serves +// as the public transaction ledger for the simulation test network. +var simNetGenesisBlock = wire.MsgBlock{ + Header: wire.BlockHeader{ + Version: 1, + PrevBlock: wire.ShaHash{}, // 0000000000000000000000000000000000000000000000000000000000000000 + MerkleRoot: simNetGenesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b + Timestamp: time.Unix(1401292357, 0), // 2014-05-28 15:52:37 +0000 UTC + Bits: 0x207fffff, // 545259519 [7fffff0000000000000000000000000000000000000000000000000000000000] + Nonce: 2, + }, + Transactions: []*wire.MsgTx{&genesisCoinbaseTx}, +} diff --git a/vendor/github.com/btcsuite/btcd/chaincfg/genesis_test.go b/vendor/github.com/btcsuite/btcd/chaincfg/genesis_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3e4f2b728f004afd973cf6adf1b38d1e85702c56 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/chaincfg/genesis_test.go @@ -0,0 +1,283 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package chaincfg + +import ( + "bytes" + "testing" + + "github.com/davecgh/go-spew/spew" +) + +// TestGenesisBlock tests the genesis block of the main network for validity by +// checking the encoded bytes and hashes. +func TestGenesisBlock(t *testing.T) { + // Encode the genesis block to raw bytes. + var buf bytes.Buffer + err := MainNetParams.GenesisBlock.Serialize(&buf) + if err != nil { + t.Fatalf("TestGenesisBlock: %v", err) + } + + // Ensure the encoded block matches the expected bytes. + if !bytes.Equal(buf.Bytes(), genesisBlockBytes) { + t.Fatalf("TestGenesisBlock: Genesis block does not appear valid - "+ + "got %v, want %v", spew.Sdump(buf.Bytes()), + spew.Sdump(genesisBlockBytes)) + } + + // Check hash of the block against expected hash. + hash := MainNetParams.GenesisBlock.BlockSha() + if !MainNetParams.GenesisHash.IsEqual(&hash) { + t.Fatalf("TestGenesisBlock: Genesis block hash does not "+ + "appear valid - got %v, want %v", spew.Sdump(hash), + spew.Sdump(MainNetParams.GenesisHash)) + } +} + +// TestRegTestGenesisBlock tests the genesis block of the regression test +// network for validity by checking the encoded bytes and hashes. +func TestRegTestGenesisBlock(t *testing.T) { + // Encode the genesis block to raw bytes. + var buf bytes.Buffer + err := RegressionNetParams.GenesisBlock.Serialize(&buf) + if err != nil { + t.Fatalf("TestRegTestGenesisBlock: %v", err) + } + + // Ensure the encoded block matches the expected bytes. + if !bytes.Equal(buf.Bytes(), regTestGenesisBlockBytes) { + t.Fatalf("TestRegTestGenesisBlock: Genesis block does not "+ + "appear valid - got %v, want %v", + spew.Sdump(buf.Bytes()), + spew.Sdump(regTestGenesisBlockBytes)) + } + + // Check hash of the block against expected hash. + hash := RegressionNetParams.GenesisBlock.BlockSha() + if !RegressionNetParams.GenesisHash.IsEqual(&hash) { + t.Fatalf("TestRegTestGenesisBlock: Genesis block hash does "+ + "not appear valid - got %v, want %v", spew.Sdump(hash), + spew.Sdump(RegressionNetParams.GenesisHash)) + } +} + +// TestTestNet3GenesisBlock tests the genesis block of the test network (version +// 3) for validity by checking the encoded bytes and hashes. +func TestTestNet3GenesisBlock(t *testing.T) { + // Encode the genesis block to raw bytes. + var buf bytes.Buffer + err := TestNet3Params.GenesisBlock.Serialize(&buf) + if err != nil { + t.Fatalf("TestTestNet3GenesisBlock: %v", err) + } + + // Ensure the encoded block matches the expected bytes. + if !bytes.Equal(buf.Bytes(), testNet3GenesisBlockBytes) { + t.Fatalf("TestTestNet3GenesisBlock: Genesis block does not "+ + "appear valid - got %v, want %v", + spew.Sdump(buf.Bytes()), + spew.Sdump(testNet3GenesisBlockBytes)) + } + + // Check hash of the block against expected hash. + hash := TestNet3Params.GenesisBlock.BlockSha() + if !TestNet3Params.GenesisHash.IsEqual(&hash) { + t.Fatalf("TestTestNet3GenesisBlock: Genesis block hash does "+ + "not appear valid - got %v, want %v", spew.Sdump(hash), + spew.Sdump(TestNet3Params.GenesisHash)) + } +} + +// TestSimNetGenesisBlock tests the genesis block of the simulation test network +// for validity by checking the encoded bytes and hashes. +func TestSimNetGenesisBlock(t *testing.T) { + // Encode the genesis block to raw bytes. + var buf bytes.Buffer + err := SimNetParams.GenesisBlock.Serialize(&buf) + if err != nil { + t.Fatalf("TestSimNetGenesisBlock: %v", err) + } + + // Ensure the encoded block matches the expected bytes. + if !bytes.Equal(buf.Bytes(), simNetGenesisBlockBytes) { + t.Fatalf("TestSimNetGenesisBlock: Genesis block does not "+ + "appear valid - got %v, want %v", + spew.Sdump(buf.Bytes()), + spew.Sdump(simNetGenesisBlockBytes)) + } + + // Check hash of the block against expected hash. + hash := SimNetParams.GenesisBlock.BlockSha() + if !SimNetParams.GenesisHash.IsEqual(&hash) { + t.Fatalf("TestSimNetGenesisBlock: Genesis block hash does "+ + "not appear valid - got %v, want %v", spew.Sdump(hash), + spew.Sdump(SimNetParams.GenesisHash)) + } +} + +// genesisBlockBytes are the wire encoded bytes for the genesis block of the +// main network as of protocol version 60002. +var genesisBlockBytes = []byte{ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x3b, 0xa3, 0xed, 0xfd, /* |....;...| */ + 0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e, /* |z{..z.,>| */ + 0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, /* |gv.a....| */ + 0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa, /* |..Q2:...| */ + 0x4b, 0x1e, 0x5e, 0x4a, 0x29, 0xab, 0x5f, 0x49, /* |K.^J)._I| */ + 0xff, 0xff, 0x00, 0x1d, 0x1d, 0xac, 0x2b, 0x7c, /* |......+|| */ + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, /* |........| */ + 0xff, 0xff, 0x4d, 0x04, 0xff, 0xff, 0x00, 0x1d, /* |..M.....| */ + 0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, /* |..EThe T| */ + 0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f, /* |imes 03/| */ + 0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39, /* |Jan/2009| */ + 0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c, /* | Chancel| */ + 0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62, /* |lor on b| */ + 0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, /* |rink of | */ + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62, /* |second b| */ + 0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66, /* |ailout f| */ + 0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73, /* |or banks| */ + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, /* |........| */ + 0x2a, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, /* |*....CA.| */ + 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, /* |g....UH'| */ + 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, /* |.g..q0..| */ + 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, /* |\..(.9..| */ + 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6, /* |yb...a..| */ + 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, /* |I..?L.8.| */ + 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde, /* |.U......| */ + 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, /* |\8M....W| */ + 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f, /* |.Lp+k.._|*/ + 0xac, 0x00, 0x00, 0x00, 0x00, /* |.....| */ +} + +// regTestGenesisBlockBytes are the wire encoded bytes for the genesis block of +// the regression test network as of protocol version 60002. +var regTestGenesisBlockBytes = []byte{ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x3b, 0xa3, 0xed, 0xfd, /* |....;...| */ + 0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e, /* |z{..z.,>| */ + 0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, /* |gv.a....| */ + 0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa, /* |..Q2:...| */ + 0x4b, 0x1e, 0x5e, 0x4a, 0xda, 0xe5, 0x49, 0x4d, /* |K.^J)._I| */ + 0xff, 0xff, 0x7f, 0x20, 0x02, 0x00, 0x00, 0x00, /* |......+|| */ + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, /* |........| */ + 0xff, 0xff, 0x4d, 0x04, 0xff, 0xff, 0x00, 0x1d, /* |..M.....| */ + 0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, /* |..EThe T| */ + 0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f, /* |imes 03/| */ + 0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39, /* |Jan/2009| */ + 0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c, /* | Chancel| */ + 0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62, /* |lor on b| */ + 0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, /* |rink of | */ + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62, /* |second b| */ + 0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66, /* |ailout f| */ + 0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73, /* |or banks| */ + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, /* |........| */ + 0x2a, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, /* |*....CA.| */ + 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, /* |g....UH'| */ + 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, /* |.g..q0..| */ + 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, /* |\..(.9..| */ + 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6, /* |yb...a..| */ + 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, /* |I..?L.8.| */ + 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde, /* |.U......| */ + 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, /* |\8M....W| */ + 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f, /* |.Lp+k.._|*/ + 0xac, 0x00, 0x00, 0x00, 0x00, /* |.....| */ +} + +// testNet3GenesisBlockBytes are the wire encoded bytes for the genesis block of +// the test network (version 3) as of protocol version 60002. +var testNet3GenesisBlockBytes = []byte{ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x3b, 0xa3, 0xed, 0xfd, /* |....;...| */ + 0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e, /* |z{..z.,>| */ + 0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, /* |gv.a....| */ + 0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa, /* |..Q2:...| */ + 0x4b, 0x1e, 0x5e, 0x4a, 0xda, 0xe5, 0x49, 0x4d, /* |K.^J)._I| */ + 0xff, 0xff, 0x00, 0x1d, 0x1a, 0xa4, 0xae, 0x18, /* |......+|| */ + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, /* |........| */ + 0xff, 0xff, 0x4d, 0x04, 0xff, 0xff, 0x00, 0x1d, /* |..M.....| */ + 0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, /* |..EThe T| */ + 0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f, /* |imes 03/| */ + 0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39, /* |Jan/2009| */ + 0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c, /* | Chancel| */ + 0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62, /* |lor on b| */ + 0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, /* |rink of | */ + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62, /* |second b| */ + 0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66, /* |ailout f| */ + 0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73, /* |or banks| */ + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, /* |........| */ + 0x2a, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, /* |*....CA.| */ + 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, /* |g....UH'| */ + 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, /* |.g..q0..| */ + 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, /* |\..(.9..| */ + 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6, /* |yb...a..| */ + 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, /* |I..?L.8.| */ + 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde, /* |.U......| */ + 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, /* |\8M....W| */ + 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f, /* |.Lp+k.._|*/ + 0xac, 0x00, 0x00, 0x00, 0x00, /* |.....| */ +} + +// simNetGenesisBlockBytes are the wire encoded bytes for the genesis block of +// the simulation test network as of protocol version 70002. +var simNetGenesisBlockBytes = []byte{ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x3b, 0xa3, 0xed, 0xfd, /* |....;...| */ + 0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e, /* |z{..z.,>| */ + 0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, /* |gv.a....| */ + 0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa, /* |..Q2:...| */ + 0x4b, 0x1e, 0x5e, 0x4a, 0x45, 0x06, 0x86, 0x53, /* |K.^J)._I| */ + 0xff, 0xff, 0x7f, 0x20, 0x02, 0x00, 0x00, 0x00, /* |......+|| */ + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* |........| */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, /* |........| */ + 0xff, 0xff, 0x4d, 0x04, 0xff, 0xff, 0x00, 0x1d, /* |..M.....| */ + 0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, /* |..EThe T| */ + 0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f, /* |imes 03/| */ + 0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39, /* |Jan/2009| */ + 0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c, /* | Chancel| */ + 0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62, /* |lor on b| */ + 0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, /* |rink of | */ + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62, /* |second b| */ + 0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66, /* |ailout f| */ + 0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73, /* |or banks| */ + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, /* |........| */ + 0x2a, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, /* |*....CA.| */ + 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, /* |g....UH'| */ + 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, /* |.g..q0..| */ + 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, /* |\..(.9..| */ + 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6, /* |yb...a..| */ + 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, /* |I..?L.8.| */ + 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde, /* |.U......| */ + 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, /* |\8M....W| */ + 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f, /* |.Lp+k.._|*/ + 0xac, 0x00, 0x00, 0x00, 0x00, /* |.....| */ +} diff --git a/vendor/github.com/btcsuite/btcd/chaincfg/params.go b/vendor/github.com/btcsuite/btcd/chaincfg/params.go new file mode 100644 index 0000000000000000000000000000000000000000..8bcc5a8dd897c512f7cb614fa1c6b63b3ea4c0dd --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/chaincfg/params.go @@ -0,0 +1,439 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package chaincfg + +import ( + "errors" + "math/big" + + "github.com/btcsuite/btcd/wire" +) + +// These variables are the chain proof-of-work limit parameters for each default +// network. +var ( + // bigOne is 1 represented as a big.Int. It is defined here to avoid + // the overhead of creating it multiple times. + bigOne = big.NewInt(1) + + // mainPowLimit is the highest proof of work value a Bitcoin block can + // have for the main network. It is the value 2^224 - 1. + mainPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne) + + // regressionPowLimit is the highest proof of work value a Bitcoin block + // can have for the regression test network. It is the value 2^255 - 1. + regressionPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 255), bigOne) + + // testNet3PowLimit is the highest proof of work value a Bitcoin block + // can have for the test network (version 3). It is the value + // 2^224 - 1. + testNet3PowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne) + + // simNetPowLimit is the highest proof of work value a Bitcoin block + // can have for the simulation test network. It is the value 2^255 - 1. + simNetPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 255), bigOne) +) + +// Checkpoint identifies a known good point in the block chain. Using +// checkpoints allows a few optimizations for old blocks during initial download +// and also prevents forks from old blocks. +// +// Each checkpoint is selected based upon several factors. See the +// documentation for blockchain.IsCheckpointCandidate for details on the +// selection criteria. +type Checkpoint struct { + Height int32 + Hash *wire.ShaHash +} + +// Params defines a Bitcoin network by its parameters. These parameters may be +// used by Bitcoin applications to differentiate networks as well as addresses +// and keys for one network from those intended for use on another network. +type Params struct { + Name string + Net wire.BitcoinNet + DefaultPort string + DNSSeeds []string + + // Chain parameters + GenesisBlock *wire.MsgBlock + GenesisHash *wire.ShaHash + PowLimit *big.Int + PowLimitBits uint32 + SubsidyHalvingInterval int32 + ResetMinDifficulty bool + GenerateSupported bool + + // Checkpoints ordered from oldest to newest. + Checkpoints []Checkpoint + + // Enforce current block version once network has + // upgraded. This is part of BIP0034. + BlockEnforceNumRequired uint64 + + // Reject previous block versions once network has + // upgraded. This is part of BIP0034. + BlockRejectNumRequired uint64 + + // The number of nodes to check. This is part of BIP0034. + BlockUpgradeNumToCheck uint64 + + // Mempool parameters + RelayNonStdTxs bool + + // Address encoding magics + PubKeyHashAddrID byte // First byte of a P2PKH address + ScriptHashAddrID byte // First byte of a P2SH address + PrivateKeyID byte // First byte of a WIF private key + + // BIP32 hierarchical deterministic extended key magics + HDPrivateKeyID [4]byte + HDPublicKeyID [4]byte + + // BIP44 coin type used in the hierarchical deterministic path for + // address generation. + HDCoinType uint32 +} + +// MainNetParams defines the network parameters for the main Bitcoin network. +var MainNetParams = Params{ + Name: "mainnet", + Net: wire.MainNet, + DefaultPort: "8333", + DNSSeeds: []string{ + "seed.bitcoin.sipa.be", + "dnsseed.bluematt.me", + "dnsseed.bitcoin.dashjr.org", + "seed.bitcoinstats.com", + "seed.bitnodes.io", + "bitseed.xf2.org", + "seed.bitcoin.jonasschnelli.ch", + }, + + // Chain parameters + GenesisBlock: &genesisBlock, + GenesisHash: &genesisHash, + PowLimit: mainPowLimit, + PowLimitBits: 0x1d00ffff, + SubsidyHalvingInterval: 210000, + ResetMinDifficulty: false, + GenerateSupported: false, + + // Checkpoints ordered from oldest to newest. + Checkpoints: []Checkpoint{ + {11111, newShaHashFromStr("0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")}, + {33333, newShaHashFromStr("000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")}, + {74000, newShaHashFromStr("0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")}, + {105000, newShaHashFromStr("00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")}, + {134444, newShaHashFromStr("00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")}, + {168000, newShaHashFromStr("000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")}, + {193000, newShaHashFromStr("000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")}, + {210000, newShaHashFromStr("000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")}, + {216116, newShaHashFromStr("00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")}, + {225430, newShaHashFromStr("00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")}, + {250000, newShaHashFromStr("000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")}, + {267300, newShaHashFromStr("000000000000000a83fbd660e918f218bf37edd92b748ad940483c7c116179ac")}, + {279000, newShaHashFromStr("0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")}, + {300255, newShaHashFromStr("0000000000000000162804527c6e9b9f0563a280525f9d08c12041def0a0f3b2")}, + {319400, newShaHashFromStr("000000000000000021c6052e9becade189495d1c539aa37c58917305fd15f13b")}, + {343185, newShaHashFromStr("0000000000000000072b8bf361d01a6ba7d445dd024203fafc78768ed4368554")}, + {352940, newShaHashFromStr("000000000000000010755df42dba556bb72be6a32f3ce0b6941ce4430152c9ff")}, + {382320, newShaHashFromStr("00000000000000000a8dc6ed5b133d0eb2fd6af56203e4159789b092defd8ab2")}, + }, + + // Enforce current block version once majority of the network has + // upgraded. + // 75% (750 / 1000) + // Reject previous block versions once a majority of the network has + // upgraded. + // 95% (950 / 1000) + BlockEnforceNumRequired: 750, + BlockRejectNumRequired: 950, + BlockUpgradeNumToCheck: 1000, + + // Mempool parameters + RelayNonStdTxs: false, + + // Address encoding magics + PubKeyHashAddrID: 0x00, // starts with 1 + ScriptHashAddrID: 0x05, // starts with 3 + PrivateKeyID: 0x80, // starts with 5 (uncompressed) or K (compressed) + + // BIP32 hierarchical deterministic extended key magics + HDPrivateKeyID: [4]byte{0x04, 0x88, 0xad, 0xe4}, // starts with xprv + HDPublicKeyID: [4]byte{0x04, 0x88, 0xb2, 0x1e}, // starts with xpub + + // BIP44 coin type used in the hierarchical deterministic path for + // address generation. + HDCoinType: 0, +} + +// RegressionNetParams defines the network parameters for the regression test +// Bitcoin network. Not to be confused with the test Bitcoin network (version +// 3), this network is sometimes simply called "testnet". +var RegressionNetParams = Params{ + Name: "regtest", + Net: wire.TestNet, + DefaultPort: "18444", + DNSSeeds: []string{}, + + // Chain parameters + GenesisBlock: ®TestGenesisBlock, + GenesisHash: ®TestGenesisHash, + PowLimit: regressionPowLimit, + PowLimitBits: 0x207fffff, + SubsidyHalvingInterval: 150, + ResetMinDifficulty: true, + GenerateSupported: true, + + // Checkpoints ordered from oldest to newest. + Checkpoints: nil, + + // Enforce current block version once majority of the network has + // upgraded. + // 75% (750 / 1000) + // Reject previous block versions once a majority of the network has + // upgraded. + // 95% (950 / 1000) + BlockEnforceNumRequired: 750, + BlockRejectNumRequired: 950, + BlockUpgradeNumToCheck: 1000, + + // Mempool parameters + RelayNonStdTxs: true, + + // Address encoding magics + PubKeyHashAddrID: 0x6f, // starts with m or n + ScriptHashAddrID: 0xc4, // starts with 2 + PrivateKeyID: 0xef, // starts with 9 (uncompressed) or c (compressed) + + // BIP32 hierarchical deterministic extended key magics + HDPrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94}, // starts with tprv + HDPublicKeyID: [4]byte{0x04, 0x35, 0x87, 0xcf}, // starts with tpub + + // BIP44 coin type used in the hierarchical deterministic path for + // address generation. + HDCoinType: 1, +} + +// TestNet3Params defines the network parameters for the test Bitcoin network +// (version 3). Not to be confused with the regression test network, this +// network is sometimes simply called "testnet". +var TestNet3Params = Params{ + Name: "testnet3", + Net: wire.TestNet3, + DefaultPort: "18333", + DNSSeeds: []string{ + "testnet-seed.bitcoin.schildbach.de", + "testnet-seed.bitcoin.petertodd.org", + "testnet-seed.bluematt.me", + }, + + // Chain parameters + GenesisBlock: &testNet3GenesisBlock, + GenesisHash: &testNet3GenesisHash, + PowLimit: testNet3PowLimit, + PowLimitBits: 0x1d00ffff, + SubsidyHalvingInterval: 210000, + ResetMinDifficulty: true, + GenerateSupported: false, + + // Checkpoints ordered from oldest to newest. + Checkpoints: []Checkpoint{ + {546, newShaHashFromStr("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")}, + }, + + // Enforce current block version once majority of the network has + // upgraded. + // 51% (51 / 100) + // Reject previous block versions once a majority of the network has + // upgraded. + // 75% (75 / 100) + BlockEnforceNumRequired: 51, + BlockRejectNumRequired: 75, + BlockUpgradeNumToCheck: 100, + + // Mempool parameters + RelayNonStdTxs: true, + + // Address encoding magics + PubKeyHashAddrID: 0x6f, // starts with m or n + ScriptHashAddrID: 0xc4, // starts with 2 + PrivateKeyID: 0xef, // starts with 9 (uncompressed) or c (compressed) + + // BIP32 hierarchical deterministic extended key magics + HDPrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94}, // starts with tprv + HDPublicKeyID: [4]byte{0x04, 0x35, 0x87, 0xcf}, // starts with tpub + + // BIP44 coin type used in the hierarchical deterministic path for + // address generation. + HDCoinType: 1, +} + +// SimNetParams defines the network parameters for the simulation test Bitcoin +// network. This network is similar to the normal test network except it is +// intended for private use within a group of individuals doing simulation +// testing. The functionality is intended to differ in that the only nodes +// which are specifically specified are used to create the network rather than +// following normal discovery rules. This is important as otherwise it would +// just turn into another public testnet. +var SimNetParams = Params{ + Name: "simnet", + Net: wire.SimNet, + DefaultPort: "18555", + DNSSeeds: []string{}, // NOTE: There must NOT be any seeds. + + // Chain parameters + GenesisBlock: &simNetGenesisBlock, + GenesisHash: &simNetGenesisHash, + PowLimit: simNetPowLimit, + PowLimitBits: 0x207fffff, + SubsidyHalvingInterval: 210000, + ResetMinDifficulty: true, + GenerateSupported: true, + + // Checkpoints ordered from oldest to newest. + Checkpoints: nil, + + // Enforce current block version once majority of the network has + // upgraded. + // 51% (51 / 100) + // Reject previous block versions once a majority of the network has + // upgraded. + // 75% (75 / 100) + BlockEnforceNumRequired: 51, + BlockRejectNumRequired: 75, + BlockUpgradeNumToCheck: 100, + + // Mempool parameters + RelayNonStdTxs: true, + + // Address encoding magics + PubKeyHashAddrID: 0x3f, // starts with S + ScriptHashAddrID: 0x7b, // starts with s + PrivateKeyID: 0x64, // starts with 4 (uncompressed) or F (compressed) + + // BIP32 hierarchical deterministic extended key magics + HDPrivateKeyID: [4]byte{0x04, 0x20, 0xb9, 0x00}, // starts with sprv + HDPublicKeyID: [4]byte{0x04, 0x20, 0xbd, 0x3a}, // starts with spub + + // BIP44 coin type used in the hierarchical deterministic path for + // address generation. + HDCoinType: 115, // ASCII for s +} + +var ( + // ErrDuplicateNet describes an error where the parameters for a Bitcoin + // network could not be set due to the network already being a standard + // network or previously-registered into this package. + ErrDuplicateNet = errors.New("duplicate Bitcoin network") + + // ErrUnknownHDKeyID describes an error where the provided id which + // is intended to identify the network for a hierarchical deterministic + // private extended key is not registered. + ErrUnknownHDKeyID = errors.New("unknown hd private extended key bytes") +) + +var ( + registeredNets = make(map[wire.BitcoinNet]struct{}) + pubKeyHashAddrIDs = make(map[byte]struct{}) + scriptHashAddrIDs = make(map[byte]struct{}) + hdPrivToPubKeyIDs = make(map[[4]byte][]byte) +) + +// Register registers the network parameters for a Bitcoin network. This may +// error with ErrDuplicateNet if the network is already registered (either +// due to a previous Register call, or the network being one of the default +// networks). +// +// Network parameters should be registered into this package by a main package +// as early as possible. Then, library packages may lookup networks or network +// parameters based on inputs and work regardless of the network being standard +// or not. +func Register(params *Params) error { + if _, ok := registeredNets[params.Net]; ok { + return ErrDuplicateNet + } + registeredNets[params.Net] = struct{}{} + pubKeyHashAddrIDs[params.PubKeyHashAddrID] = struct{}{} + scriptHashAddrIDs[params.ScriptHashAddrID] = struct{}{} + hdPrivToPubKeyIDs[params.HDPrivateKeyID] = params.HDPublicKeyID[:] + return nil +} + +// mustRegister performs the same function as Register except it panics if there +// is an error. This should only be called from package init functions. +func mustRegister(params *Params) { + if err := Register(params); err != nil { + panic("failed to register network: " + err.Error()) + } +} + +// IsPubKeyHashAddrID returns whether the id is an identifier known to prefix a +// pay-to-pubkey-hash address on any default or registered network. This is +// used when decoding an address string into a specific address type. It is up +// to the caller to check both this and IsScriptHashAddrID and decide whether an +// address is a pubkey hash address, script hash address, neither, or +// undeterminable (if both return true). +func IsPubKeyHashAddrID(id byte) bool { + _, ok := pubKeyHashAddrIDs[id] + return ok +} + +// IsScriptHashAddrID returns whether the id is an identifier known to prefix a +// pay-to-script-hash address on any default or registered network. This is +// used when decoding an address string into a specific address type. It is up +// to the caller to check both this and IsPubKeyHashAddrID and decide whether an +// address is a pubkey hash address, script hash address, neither, or +// undeterminable (if both return true). +func IsScriptHashAddrID(id byte) bool { + _, ok := scriptHashAddrIDs[id] + return ok +} + +// HDPrivateKeyToPublicKeyID accepts a private hierarchical deterministic +// extended key id and returns the associated public key id. When the provided +// id is not registered, the ErrUnknownHDKeyID error will be returned. +func HDPrivateKeyToPublicKeyID(id []byte) ([]byte, error) { + if len(id) != 4 { + return nil, ErrUnknownHDKeyID + } + + var key [4]byte + copy(key[:], id) + pubBytes, ok := hdPrivToPubKeyIDs[key] + if !ok { + return nil, ErrUnknownHDKeyID + } + + return pubBytes, nil +} + +// newShaHashFromStr converts the passed big-endian hex string into a +// wire.ShaHash. It only differs from the one available in wire in that +// it panics on an error since it will only (and must only) be called with +// hard-coded, and therefore known good, hashes. +func newShaHashFromStr(hexStr string) *wire.ShaHash { + sha, err := wire.NewShaHashFromStr(hexStr) + if err != nil { + // Ordinarily I don't like panics in library code since it + // can take applications down without them having a chance to + // recover which is extremely annoying, however an exception is + // being made in this case because the only way this can panic + // is if there is an error in the hard-coded hashes. Thus it + // will only ever potentially panic on init and therefore is + // 100% predictable. + panic(err) + } + return sha +} + +func init() { + // Register all default networks when the package is initialized. + mustRegister(&MainNetParams) + mustRegister(&TestNet3Params) + mustRegister(&RegressionNetParams) + mustRegister(&SimNetParams) +} diff --git a/vendor/github.com/btcsuite/btcd/chaincfg/params_test.go b/vendor/github.com/btcsuite/btcd/chaincfg/params_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bb526929e4741977bbbaf1969585c7d1b2426fd5 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/chaincfg/params_test.go @@ -0,0 +1,35 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package chaincfg + +import "testing" + +// TestInvalidHashStr ensures the newShaHashFromStr function panics when used to +// with an invalid hash string. +func TestInvalidHashStr(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected panic for invalid hash, got nil") + } + }() + newShaHashFromStr("banana") +} + +// TestMustRegisterPanic ensures the mustRegister function panics when used to +// register an invalid network. +func TestMustRegisterPanic(t *testing.T) { + t.Parallel() + + // Setup a defer to catch the expected panic to ensure it actually + // paniced. + defer func() { + if err := recover(); err == nil { + t.Error("mustRegister did not panic as expected") + } + }() + + // Intentionally try to register duplicate params to force a panic. + mustRegister(&MainNetParams) +} diff --git a/vendor/github.com/btcsuite/btcd/chaincfg/register_test.go b/vendor/github.com/btcsuite/btcd/chaincfg/register_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b5ac392bc1b7db7492c2cccc72fbc2d6cb55722b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/chaincfg/register_test.go @@ -0,0 +1,380 @@ +package chaincfg_test + +import ( + "bytes" + "reflect" + "testing" + + . "github.com/btcsuite/btcd/chaincfg" +) + +// Define some of the required parameters for a user-registered +// network. This is necessary to test the registration of and +// lookup of encoding magics from the network. +var mockNetParams = Params{ + Name: "mocknet", + Net: 1<<32 - 1, + PubKeyHashAddrID: 0x9f, + ScriptHashAddrID: 0xf9, + HDPrivateKeyID: [4]byte{0x01, 0x02, 0x03, 0x04}, + HDPublicKeyID: [4]byte{0x05, 0x06, 0x07, 0x08}, +} + +func TestRegister(t *testing.T) { + type registerTest struct { + name string + params *Params + err error + } + type magicTest struct { + magic byte + valid bool + } + type hdTest struct { + priv []byte + want []byte + err error + } + + tests := []struct { + name string + register []registerTest + p2pkhMagics []magicTest + p2shMagics []magicTest + hdMagics []hdTest + }{ + { + name: "default networks", + register: []registerTest{ + { + name: "duplicate mainnet", + params: &MainNetParams, + err: ErrDuplicateNet, + }, + { + name: "duplicate regtest", + params: &RegressionNetParams, + err: ErrDuplicateNet, + }, + { + name: "duplicate testnet3", + params: &TestNet3Params, + err: ErrDuplicateNet, + }, + { + name: "duplicate simnet", + params: &SimNetParams, + err: ErrDuplicateNet, + }, + }, + p2pkhMagics: []magicTest{ + { + magic: MainNetParams.PubKeyHashAddrID, + valid: true, + }, + { + magic: TestNet3Params.PubKeyHashAddrID, + valid: true, + }, + { + magic: RegressionNetParams.PubKeyHashAddrID, + valid: true, + }, + { + magic: SimNetParams.PubKeyHashAddrID, + valid: true, + }, + { + magic: mockNetParams.PubKeyHashAddrID, + valid: false, + }, + { + magic: 0xFF, + valid: false, + }, + }, + p2shMagics: []magicTest{ + { + magic: MainNetParams.ScriptHashAddrID, + valid: true, + }, + { + magic: TestNet3Params.ScriptHashAddrID, + valid: true, + }, + { + magic: RegressionNetParams.ScriptHashAddrID, + valid: true, + }, + { + magic: SimNetParams.ScriptHashAddrID, + valid: true, + }, + { + magic: mockNetParams.ScriptHashAddrID, + valid: false, + }, + { + magic: 0xFF, + valid: false, + }, + }, + hdMagics: []hdTest{ + { + priv: MainNetParams.HDPrivateKeyID[:], + want: MainNetParams.HDPublicKeyID[:], + err: nil, + }, + { + priv: TestNet3Params.HDPrivateKeyID[:], + want: TestNet3Params.HDPublicKeyID[:], + err: nil, + }, + { + priv: RegressionNetParams.HDPrivateKeyID[:], + want: RegressionNetParams.HDPublicKeyID[:], + err: nil, + }, + { + priv: SimNetParams.HDPrivateKeyID[:], + want: SimNetParams.HDPublicKeyID[:], + err: nil, + }, + { + priv: mockNetParams.HDPrivateKeyID[:], + err: ErrUnknownHDKeyID, + }, + { + priv: []byte{0xff, 0xff, 0xff, 0xff}, + err: ErrUnknownHDKeyID, + }, + { + priv: []byte{0xff}, + err: ErrUnknownHDKeyID, + }, + }, + }, + { + name: "register mocknet", + register: []registerTest{ + { + name: "mocknet", + params: &mockNetParams, + err: nil, + }, + }, + p2pkhMagics: []magicTest{ + { + magic: MainNetParams.PubKeyHashAddrID, + valid: true, + }, + { + magic: TestNet3Params.PubKeyHashAddrID, + valid: true, + }, + { + magic: RegressionNetParams.PubKeyHashAddrID, + valid: true, + }, + { + magic: SimNetParams.PubKeyHashAddrID, + valid: true, + }, + { + magic: mockNetParams.PubKeyHashAddrID, + valid: true, + }, + { + magic: 0xFF, + valid: false, + }, + }, + p2shMagics: []magicTest{ + { + magic: MainNetParams.ScriptHashAddrID, + valid: true, + }, + { + magic: TestNet3Params.ScriptHashAddrID, + valid: true, + }, + { + magic: RegressionNetParams.ScriptHashAddrID, + valid: true, + }, + { + magic: SimNetParams.ScriptHashAddrID, + valid: true, + }, + { + magic: mockNetParams.ScriptHashAddrID, + valid: true, + }, + { + magic: 0xFF, + valid: false, + }, + }, + hdMagics: []hdTest{ + { + priv: mockNetParams.HDPrivateKeyID[:], + want: mockNetParams.HDPublicKeyID[:], + err: nil, + }, + }, + }, + { + name: "more duplicates", + register: []registerTest{ + { + name: "duplicate mainnet", + params: &MainNetParams, + err: ErrDuplicateNet, + }, + { + name: "duplicate regtest", + params: &RegressionNetParams, + err: ErrDuplicateNet, + }, + { + name: "duplicate testnet3", + params: &TestNet3Params, + err: ErrDuplicateNet, + }, + { + name: "duplicate simnet", + params: &SimNetParams, + err: ErrDuplicateNet, + }, + { + name: "duplicate mocknet", + params: &mockNetParams, + err: ErrDuplicateNet, + }, + }, + p2pkhMagics: []magicTest{ + { + magic: MainNetParams.PubKeyHashAddrID, + valid: true, + }, + { + magic: TestNet3Params.PubKeyHashAddrID, + valid: true, + }, + { + magic: RegressionNetParams.PubKeyHashAddrID, + valid: true, + }, + { + magic: SimNetParams.PubKeyHashAddrID, + valid: true, + }, + { + magic: mockNetParams.PubKeyHashAddrID, + valid: true, + }, + { + magic: 0xFF, + valid: false, + }, + }, + p2shMagics: []magicTest{ + { + magic: MainNetParams.ScriptHashAddrID, + valid: true, + }, + { + magic: TestNet3Params.ScriptHashAddrID, + valid: true, + }, + { + magic: RegressionNetParams.ScriptHashAddrID, + valid: true, + }, + { + magic: SimNetParams.ScriptHashAddrID, + valid: true, + }, + { + magic: mockNetParams.ScriptHashAddrID, + valid: true, + }, + { + magic: 0xFF, + valid: false, + }, + }, + hdMagics: []hdTest{ + { + priv: MainNetParams.HDPrivateKeyID[:], + want: MainNetParams.HDPublicKeyID[:], + err: nil, + }, + { + priv: TestNet3Params.HDPrivateKeyID[:], + want: TestNet3Params.HDPublicKeyID[:], + err: nil, + }, + { + priv: RegressionNetParams.HDPrivateKeyID[:], + want: RegressionNetParams.HDPublicKeyID[:], + err: nil, + }, + { + priv: SimNetParams.HDPrivateKeyID[:], + want: SimNetParams.HDPublicKeyID[:], + err: nil, + }, + { + priv: mockNetParams.HDPrivateKeyID[:], + want: mockNetParams.HDPublicKeyID[:], + err: nil, + }, + { + priv: []byte{0xff, 0xff, 0xff, 0xff}, + err: ErrUnknownHDKeyID, + }, + { + priv: []byte{0xff}, + err: ErrUnknownHDKeyID, + }, + }, + }, + } + + for _, test := range tests { + for _, regTest := range test.register { + err := Register(regTest.params) + if err != regTest.err { + t.Errorf("%s:%s: Registered network with unexpected error: got %v expected %v", + test.name, regTest.name, err, regTest.err) + } + } + for i, magTest := range test.p2pkhMagics { + valid := IsPubKeyHashAddrID(magTest.magic) + if valid != magTest.valid { + t.Errorf("%s: P2PKH magic %d valid mismatch: got %v expected %v", + test.name, i, valid, magTest.valid) + } + } + for i, magTest := range test.p2shMagics { + valid := IsScriptHashAddrID(magTest.magic) + if valid != magTest.valid { + t.Errorf("%s: P2SH magic %d valid mismatch: got %v expected %v", + test.name, i, valid, magTest.valid) + } + } + for i, magTest := range test.hdMagics { + pubKey, err := HDPrivateKeyToPublicKeyID(magTest.priv[:]) + if !reflect.DeepEqual(err, magTest.err) { + t.Errorf("%s: HD magic %d mismatched error: got %v expected %v ", + test.name, i, err, magTest.err) + continue + } + if magTest.err == nil && !bytes.Equal(pubKey, magTest.want[:]) { + t.Errorf("%s: HD magic %d private and public mismatch: got %v expected %v ", + test.name, i, pubKey, magTest.want[:]) + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/cmd/addblock/addblock.go b/vendor/github.com/btcsuite/btcd/cmd/addblock/addblock.go new file mode 100644 index 0000000000000000000000000000000000000000..babb1bd8f3de10a55eba30032e116446961c9dcb --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/cmd/addblock/addblock.go @@ -0,0 +1,130 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "os" + "path/filepath" + "runtime" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/limits" + "github.com/btcsuite/btclog" +) + +const ( + // blockDbNamePrefix is the prefix for the btcd block database. + blockDbNamePrefix = "blocks" +) + +var ( + cfg *config + log btclog.Logger +) + +// loadBlockDB opens the block database and returns a handle to it. +func loadBlockDB() (database.DB, error) { + // The database name is based on the database type. + dbName := blockDbNamePrefix + "_" + cfg.DbType + dbPath := filepath.Join(cfg.DataDir, dbName) + + log.Infof("Loading block database from '%s'", dbPath) + db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net) + if err != nil { + // Return the error if it's not because the database doesn't + // exist. + if dbErr, ok := err.(database.Error); !ok || dbErr.ErrorCode != + database.ErrDbDoesNotExist { + + return nil, err + } + + // Create the db if it does not exist. + err = os.MkdirAll(cfg.DataDir, 0700) + if err != nil { + return nil, err + } + db, err = database.Create(cfg.DbType, dbPath, activeNetParams.Net) + if err != nil { + return nil, err + } + } + + log.Info("Block database loaded") + return db, nil +} + +// realMain is the real main function for the utility. It is necessary to work +// around the fact that deferred functions do not run when os.Exit() is called. +func realMain() error { + // Load configuration and parse command line. + tcfg, _, err := loadConfig() + if err != nil { + return err + } + cfg = tcfg + + // Setup logging. + backendLogger := btclog.NewDefaultBackendLogger() + defer backendLogger.Flush() + log = btclog.NewSubsystemLogger(backendLogger, "") + database.UseLogger(btclog.NewSubsystemLogger(backendLogger, "BCDB: ")) + blockchain.UseLogger(btclog.NewSubsystemLogger(backendLogger, "CHAN: ")) + + // Load the block database. + db, err := loadBlockDB() + if err != nil { + log.Errorf("Failed to load database: %v", err) + return err + } + defer db.Close() + + fi, err := os.Open(cfg.InFile) + if err != nil { + log.Errorf("Failed to open file %v: %v", cfg.InFile, err) + return err + } + defer fi.Close() + + // Create a block importer for the database and input file and start it. + // The done channel returned from start will contain an error if + // anything went wrong. + importer, err := newBlockImporter(db, fi) + if err != nil { + log.Errorf("Failed create block importer: %v", err) + return err + } + + // Perform the import asynchronously. This allows blocks to be + // processed and read in parallel. The results channel returned from + // Import contains the statistics about the import including an error + // if something went wrong. + log.Info("Starting import") + resultsChan := importer.Import() + results := <-resultsChan + if results.err != nil { + log.Errorf("%v", results.err) + return results.err + } + + log.Infof("Processed a total of %d blocks (%d imported, %d already "+ + "known)", results.blocksProcessed, results.blocksImported, + results.blocksProcessed-results.blocksImported) + return nil +} + +func main() { + // Use all processor cores and up some limits. + runtime.GOMAXPROCS(runtime.NumCPU()) + if err := limits.SetLimits(); err != nil { + os.Exit(1) + } + + // Work around defer not working after os.Exit() + if err := realMain(); err != nil { + os.Exit(1) + } +} diff --git a/vendor/github.com/btcsuite/btcd/cmd/addblock/config.go b/vendor/github.com/btcsuite/btcd/cmd/addblock/config.go new file mode 100644 index 0000000000000000000000000000000000000000..785a3d2af026ddd86c26e7b7fce871cc481b5a91 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/cmd/addblock/config.go @@ -0,0 +1,159 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + _ "github.com/btcsuite/btcd/database/ffldb" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + flags "github.com/btcsuite/go-flags" +) + +const ( + defaultDbType = "ffldb" + defaultDataFile = "bootstrap.dat" + defaultProgress = 10 +) + +var ( + btcdHomeDir = btcutil.AppDataDir("btcd", false) + defaultDataDir = filepath.Join(btcdHomeDir, "data") + knownDbTypes = database.SupportedDrivers() + activeNetParams = &chaincfg.MainNetParams +) + +// config defines the configuration options for findcheckpoint. +// +// See loadConfig for details on the configuration load process. +type config struct { + DataDir string `short:"b" long:"datadir" description:"Location of the btcd data directory"` + DbType string `long:"dbtype" description:"Database backend to use for the Block Chain"` + TestNet3 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"` + InFile string `short:"i" long:"infile" description:"File containing the block(s)"` + Progress int `short:"p" long:"progress" description:"Show a progress message each time this number of seconds have passed -- Use 0 to disable progress announcements"` +} + +// filesExists reports whether the named file or directory exists. +func fileExists(name string) bool { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +} + +// validDbType returns whether or not dbType is a supported database type. +func validDbType(dbType string) bool { + for _, knownType := range knownDbTypes { + if dbType == knownType { + return true + } + } + + return false +} + +// netName returns the name used when referring to a bitcoin network. At the +// time of writing, btcd currently places blocks for testnet version 3 in the +// data and log directory "testnet", which does not match the Name field of the +// chaincfg parameters. This function can be used to override this directory name +// as "testnet" when the passed active network matches wire.TestNet3. +// +// A proper upgrade to move the data and log directories for this network to +// "testnet3" is planned for the future, at which point this function can be +// removed and the network parameter's name used instead. +func netName(chainParams *chaincfg.Params) string { + switch chainParams.Net { + case wire.TestNet3: + return "testnet" + default: + return chainParams.Name + } +} + +// loadConfig initializes and parses the config using command line options. +func loadConfig() (*config, []string, error) { + // Default config. + cfg := config{ + DataDir: defaultDataDir, + DbType: defaultDbType, + InFile: defaultDataFile, + Progress: defaultProgress, + } + + // Parse command line options. + parser := flags.NewParser(&cfg, flags.Default) + remainingArgs, err := parser.Parse() + if err != nil { + if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { + parser.WriteHelp(os.Stderr) + } + return nil, nil, err + } + + // 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.TestNet3 { + numNets++ + activeNetParams = &chaincfg.TestNet3Params + } + if cfg.RegressionTest { + numNets++ + activeNetParams = &chaincfg.RegressionNetParams + } + if cfg.SimNet { + numNets++ + activeNetParams = &chaincfg.SimNetParams + } + if numNets > 1 { + str := "%s: The testnet, regtest, and simnet params can't be " + + "used together -- choose one of the three" + err := fmt.Errorf(str, funcName) + fmt.Fprintln(os.Stderr, err) + parser.WriteHelp(os.Stderr) + return nil, nil, err + } + + // Validate database type. + if !validDbType(cfg.DbType) { + str := "%s: The specified database type [%v] is invalid -- " + + "supported types %v" + err := fmt.Errorf(str, "loadConfig", cfg.DbType, knownDbTypes) + fmt.Fprintln(os.Stderr, err) + parser.WriteHelp(os.Stderr) + return nil, nil, err + } + + // Append the network type to the data directory so it is "namespaced" + // per network. In addition to the block database, there are other + // pieces of data that are saved to disk such as address manager state. + // All data is specific to a network, so namespacing the data directory + // means each individual piece of serialized data does not have to + // worry about changing names per network and such. + cfg.DataDir = filepath.Join(cfg.DataDir, netName(activeNetParams)) + + // Ensure the specified block file exists. + if !fileExists(cfg.InFile) { + str := "%s: The specified block file [%v] does not exist" + err := fmt.Errorf(str, "loadConfig", cfg.InFile) + fmt.Fprintln(os.Stderr, err) + parser.WriteHelp(os.Stderr) + return nil, nil, err + } + + return &cfg, remainingArgs, nil +} diff --git a/vendor/github.com/btcsuite/btcd/cmd/addblock/import.go b/vendor/github.com/btcsuite/btcd/cmd/addblock/import.go new file mode 100644 index 0000000000000000000000000000000000000000..a10faf9a6a3b3d0399106fb365ee7fabbfa62046 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/cmd/addblock/import.go @@ -0,0 +1,317 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/binary" + "fmt" + "io" + "sync" + "time" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +var zeroHash = wire.ShaHash{} + +// importResults houses the stats and result as an import operation. +type importResults struct { + blocksProcessed int64 + blocksImported int64 + err error +} + +// blockImporter houses information about an ongoing import from a block data +// file to the block database. +type blockImporter struct { + db database.DB + chain *blockchain.BlockChain + medianTime blockchain.MedianTimeSource + r io.ReadSeeker + processQueue chan []byte + doneChan chan bool + errChan chan error + quit chan struct{} + wg sync.WaitGroup + blocksProcessed int64 + blocksImported int64 + receivedLogBlocks int64 + receivedLogTx int64 + lastHeight int64 + lastBlockTime time.Time + lastLogTime time.Time +} + +// readBlock reads the next block from the input file. +func (bi *blockImporter) readBlock() ([]byte, error) { + // The block file format is: + // <network> <block length> <serialized block> + var net uint32 + err := binary.Read(bi.r, binary.LittleEndian, &net) + if err != nil { + if err != io.EOF { + return nil, err + } + + // No block and no error means there are no more blocks to read. + return nil, nil + } + if net != uint32(activeNetParams.Net) { + return nil, fmt.Errorf("network mismatch -- got %x, want %x", + net, uint32(activeNetParams.Net)) + } + + // Read the block length and ensure it is sane. + var blockLen uint32 + if err := binary.Read(bi.r, binary.LittleEndian, &blockLen); err != nil { + return nil, err + } + if blockLen > wire.MaxBlockPayload { + return nil, fmt.Errorf("block payload of %d bytes is larger "+ + "than the max allowed %d bytes", blockLen, + wire.MaxBlockPayload) + } + + serializedBlock := make([]byte, blockLen) + if _, err := io.ReadFull(bi.r, serializedBlock); err != nil { + return nil, err + } + + return serializedBlock, nil +} + +// processBlock potentially imports the block into the database. It first +// deserializes the raw block while checking for errors. Already known blocks +// are skipped and orphan blocks are considered errors. Finally, it runs the +// block through the chain rules to ensure it follows all rules and matches +// up to the known checkpoint. Returns whether the block was imported along +// with any potential errors. +func (bi *blockImporter) processBlock(serializedBlock []byte) (bool, error) { + // Deserialize the block which includes checks for malformed blocks. + block, err := btcutil.NewBlockFromBytes(serializedBlock) + if err != nil { + return false, err + } + + // update progress statistics + bi.lastBlockTime = block.MsgBlock().Header.Timestamp + bi.receivedLogTx += int64(len(block.MsgBlock().Transactions)) + + // Skip blocks that already exist. + blockSha := block.Sha() + exists, err := bi.chain.HaveBlock(blockSha) + if err != nil { + return false, err + } + if exists { + return false, nil + } + + // Don't bother trying to process orphans. + prevHash := &block.MsgBlock().Header.PrevBlock + if !prevHash.IsEqual(&zeroHash) { + exists, err := bi.chain.HaveBlock(prevHash) + if err != nil { + return false, err + } + if !exists { + return false, fmt.Errorf("import file contains block "+ + "%v which does not link to the available "+ + "block chain", prevHash) + } + } + + // Ensure the blocks follows all of the chain rules and match up to the + // known checkpoints. + isOrphan, err := bi.chain.ProcessBlock(block, bi.medianTime, + blockchain.BFFastAdd) + if err != nil { + return false, err + } + if isOrphan { + return false, fmt.Errorf("import file contains an orphan "+ + "block: %v", blockSha) + } + + return true, nil +} + +// readHandler is the main handler for reading blocks from the import file. +// This allows block processing to take place in parallel with block reads. +// It must be run as a goroutine. +func (bi *blockImporter) readHandler() { +out: + for { + // Read the next block from the file and if anything goes wrong + // notify the status handler with the error and bail. + serializedBlock, err := bi.readBlock() + if err != nil { + bi.errChan <- fmt.Errorf("Error reading from input "+ + "file: %v", err.Error()) + break out + } + + // A nil block with no error means we're done. + if serializedBlock == nil { + break out + } + + // Send the block or quit if we've been signalled to exit by + // the status handler due to an error elsewhere. + select { + case bi.processQueue <- serializedBlock: + case <-bi.quit: + break out + } + } + + // Close the processing channel to signal no more blocks are coming. + close(bi.processQueue) + bi.wg.Done() +} + +// logProgress logs block progress as an information message. In order to +// prevent spam, it limits logging to one message every cfg.Progress seconds +// with duration and totals included. +func (bi *blockImporter) logProgress() { + bi.receivedLogBlocks++ + + now := time.Now() + duration := now.Sub(bi.lastLogTime) + if duration < time.Second*time.Duration(cfg.Progress) { + return + } + + // Truncate the duration to 10s of milliseconds. + durationMillis := int64(duration / time.Millisecond) + tDuration := 10 * time.Millisecond * time.Duration(durationMillis/10) + + // Log information about new block height. + blockStr := "blocks" + if bi.receivedLogBlocks == 1 { + blockStr = "block" + } + txStr := "transactions" + if bi.receivedLogTx == 1 { + txStr = "transaction" + } + log.Infof("Processed %d %s in the last %s (%d %s, height %d, %s)", + bi.receivedLogBlocks, blockStr, tDuration, bi.receivedLogTx, + txStr, bi.lastHeight, bi.lastBlockTime) + + bi.receivedLogBlocks = 0 + bi.receivedLogTx = 0 + bi.lastLogTime = now +} + +// processHandler is the main handler for processing blocks. This allows block +// processing to take place in parallel with block reads from the import file. +// It must be run as a goroutine. +func (bi *blockImporter) processHandler() { +out: + for { + select { + case serializedBlock, ok := <-bi.processQueue: + // We're done when the channel is closed. + if !ok { + break out + } + + bi.blocksProcessed++ + bi.lastHeight++ + imported, err := bi.processBlock(serializedBlock) + if err != nil { + bi.errChan <- err + break out + } + + if imported { + bi.blocksImported++ + } + + bi.logProgress() + + case <-bi.quit: + break out + } + } + bi.wg.Done() +} + +// statusHandler waits for updates from the import operation and notifies +// the passed doneChan with the results of the import. It also causes all +// goroutines to exit if an error is reported from any of them. +func (bi *blockImporter) statusHandler(resultsChan chan *importResults) { + select { + // An error from either of the goroutines means we're done so signal + // caller with the error and signal all goroutines to quit. + case err := <-bi.errChan: + resultsChan <- &importResults{ + blocksProcessed: bi.blocksProcessed, + blocksImported: bi.blocksImported, + err: err, + } + close(bi.quit) + + // The import finished normally. + case <-bi.doneChan: + resultsChan <- &importResults{ + blocksProcessed: bi.blocksProcessed, + blocksImported: bi.blocksImported, + err: nil, + } + } +} + +// Import is the core function which handles importing the blocks from the file +// associated with the block importer to the database. It returns a channel +// on which the results will be returned when the operation has completed. +func (bi *blockImporter) Import() chan *importResults { + // Start up the read and process handling goroutines. This setup allows + // blocks to be read from disk in parallel while being processed. + bi.wg.Add(2) + go bi.readHandler() + go bi.processHandler() + + // Wait for the import to finish in a separate goroutine and signal + // the status handler when done. + go func() { + bi.wg.Wait() + bi.doneChan <- true + }() + + // Start the status handler and return the result channel that it will + // send the results on when the import is done. + resultChan := make(chan *importResults) + go bi.statusHandler(resultChan) + return resultChan +} + +// newBlockImporter returns a new importer for the provided file reader seeker +// and database. +func newBlockImporter(db database.DB, r io.ReadSeeker) (*blockImporter, error) { + chain, err := blockchain.New(&blockchain.Config{ + DB: db, + ChainParams: activeNetParams, + }) + if err != nil { + return nil, err + } + + return &blockImporter{ + db: db, + r: r, + processQueue: make(chan []byte, 2), + doneChan: make(chan bool), + errChan: make(chan error), + quit: make(chan struct{}), + chain: chain, + medianTime: blockchain.NewMedianTime(), + lastLogTime: time.Now(), + }, nil +} diff --git a/vendor/github.com/btcsuite/btcd/cmd/btcctl/btcctl.go b/vendor/github.com/btcsuite/btcd/cmd/btcctl/btcctl.go new file mode 100644 index 0000000000000000000000000000000000000000..5c412f867fa80d660bfa2324547811227bfe1c44 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/cmd/btcctl/btcctl.go @@ -0,0 +1,167 @@ +package main + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/btcsuite/btcd/btcjson" +) + +const ( + showHelpMessage = "Specify -h to show available options" + listCmdMessage = "Specify -l to list available commands" +) + +// commandUsage display the usage for a specific command. +func commandUsage(method string) { + usage, err := btcjson.MethodUsageText(method) + if err != nil { + // This should never happen since the method was already checked + // before calling this function, but be safe. + fmt.Fprintln(os.Stderr, "Failed to obtain command usage:", err) + return + } + + fmt.Fprintln(os.Stderr, "Usage:") + fmt.Fprintf(os.Stderr, " %s\n", usage) +} + +// usage displays the general usage when the help flag is not displayed and +// and an invalid command was specified. The commandUsage function is used +// instead when a valid command was specified. +func usage(errorMessage string) { + appName := filepath.Base(os.Args[0]) + appName = strings.TrimSuffix(appName, filepath.Ext(appName)) + fmt.Fprintln(os.Stderr, errorMessage) + fmt.Fprintln(os.Stderr, "Usage:") + fmt.Fprintf(os.Stderr, " %s [OPTIONS] <command> <args...>\n\n", + appName) + fmt.Fprintln(os.Stderr, showHelpMessage) + fmt.Fprintln(os.Stderr, listCmdMessage) +} + +func main() { + cfg, args, err := loadConfig() + if err != nil { + os.Exit(1) + } + if len(args) < 1 { + usage("No command specified") + os.Exit(1) + } + + // Ensure the specified method identifies a valid registered command and + // is one of the usable types. + method := args[0] + usageFlags, err := btcjson.MethodUsageFlags(method) + if err != nil { + fmt.Fprintf(os.Stderr, "Unrecognized command '%s'\n", method) + fmt.Fprintln(os.Stderr, listCmdMessage) + os.Exit(1) + } + if usageFlags&unusableFlags != 0 { + fmt.Fprintf(os.Stderr, "The '%s' command can only be used via "+ + "websockets\n", method) + fmt.Fprintln(os.Stderr, listCmdMessage) + os.Exit(1) + } + + // Convert remaining command line args to a slice of interface values + // to be passed along as parameters to new command creation function. + // + // Since some commands, such as submitblock, can involve data which is + // too large for the Operating System to allow as a normal command line + // parameter, support using '-' as an argument to allow the argument + // to be read from a stdin pipe. + bio := bufio.NewReader(os.Stdin) + params := make([]interface{}, 0, len(args[1:])) + for _, arg := range args[1:] { + if arg == "-" { + param, err := bio.ReadString('\n') + if err != nil && err != io.EOF { + fmt.Fprintf(os.Stderr, "Failed to read data "+ + "from stdin: %v\n", err) + os.Exit(1) + } + if err == io.EOF && len(param) == 0 { + fmt.Fprintln(os.Stderr, "Not enough lines "+ + "provided on stdin") + os.Exit(1) + } + param = strings.TrimRight(param, "\r\n") + params = append(params, param) + continue + } + + params = append(params, arg) + } + + // Attempt to create the appropriate command using the arguments + // provided by the user. + cmd, err := btcjson.NewCmd(method, params...) + if err != nil { + // Show the error along with its error code when it's a + // btcjson.Error as it reallistcally will always be since the + // NewCmd function is only supposed to return errors of that + // type. + if jerr, ok := err.(btcjson.Error); ok { + fmt.Fprintf(os.Stderr, "%s command: %v (code: %s)\n", + method, err, jerr.ErrorCode) + commandUsage(method) + os.Exit(1) + } + + // The error is not a btcjson.Error and this really should not + // happen. Nevertheless, fallback to just showing the error + // if it should happen due to a bug in the package. + fmt.Fprintf(os.Stderr, "%s command: %v\n", method, err) + commandUsage(method) + os.Exit(1) + } + + // Marshal the command into a JSON-RPC byte slice in preparation for + // sending it to the RPC server. + marshalledJSON, err := btcjson.MarshalCmd(1, cmd) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + // Send the JSON-RPC request to the server using the user-specified + // connection configuration. + result, err := sendPostRequest(marshalledJSON, cfg) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + // Choose how to display the result based on its type. + strResult := string(result) + if strings.HasPrefix(strResult, "{") || strings.HasPrefix(strResult, "[") { + var dst bytes.Buffer + if err := json.Indent(&dst, result, "", " "); err != nil { + fmt.Fprintf(os.Stderr, "Failed to format result: %v", + err) + os.Exit(1) + } + fmt.Println(dst.String()) + + } else if strings.HasPrefix(strResult, `"`) { + var str string + if err := json.Unmarshal(result, &str); err != nil { + fmt.Fprintf(os.Stderr, "Failed to unmarshal result: %v", + err) + os.Exit(1) + } + fmt.Println(str) + + } else if strResult != "null" { + fmt.Println(strResult) + } +} diff --git a/vendor/github.com/btcsuite/btcd/cmd/btcctl/config.go b/vendor/github.com/btcsuite/btcd/cmd/btcctl/config.go new file mode 100644 index 0000000000000000000000000000000000000000..a6c37f82cc05932e16afe81ef34e9f320108af2a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/cmd/btcctl/config.go @@ -0,0 +1,270 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "net" + "os" + "path/filepath" + "strings" + + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcutil" + flags "github.com/btcsuite/go-flags" +) + +const ( + // unusableFlags are the command usage flags which this utility are not + // able to use. In particular it doesn't support websockets and + // consequently notifications. + unusableFlags = btcjson.UFWebsocketOnly | btcjson.UFNotification +) + +var ( + btcdHomeDir = btcutil.AppDataDir("btcd", false) + btcctlHomeDir = btcutil.AppDataDir("btcctl", false) + btcwalletHomeDir = btcutil.AppDataDir("btcwallet", false) + defaultConfigFile = filepath.Join(btcctlHomeDir, "btcctl.conf") + defaultRPCServer = "localhost" + defaultRPCCertFile = filepath.Join(btcdHomeDir, "rpc.cert") + defaultWalletCertFile = filepath.Join(btcwalletHomeDir, "rpc.cert") +) + +// listCommands categorizes and lists all of the usable commands along with +// their one-line usage. +func listCommands() { + const ( + categoryChain uint8 = iota + categoryWallet + numCategories + ) + + // Get a list of registered commands and categorize and filter them. + cmdMethods := btcjson.RegisteredCmdMethods() + categorized := make([][]string, numCategories) + for _, method := range cmdMethods { + flags, err := btcjson.MethodUsageFlags(method) + if err != nil { + // This should never happen since the method was just + // returned from the package, but be safe. + continue + } + + // Skip the commands that aren't usable from this utility. + if flags&unusableFlags != 0 { + continue + } + + usage, err := btcjson.MethodUsageText(method) + if err != nil { + // This should never happen since the method was just + // returned from the package, but be safe. + continue + } + + // Categorize the command based on the usage flags. + category := categoryChain + if flags&btcjson.UFWalletOnly != 0 { + category = categoryWallet + } + categorized[category] = append(categorized[category], usage) + } + + // Display the command according to their categories. + categoryTitles := make([]string, numCategories) + categoryTitles[categoryChain] = "Chain Server Commands:" + categoryTitles[categoryWallet] = "Wallet Server Commands (--wallet):" + for category := uint8(0); category < numCategories; category++ { + fmt.Println(categoryTitles[category]) + for _, usage := range categorized[category] { + fmt.Println(usage) + } + fmt.Println() + } +} + +// config defines the configuration options for btcctl. +// +// See loadConfig for details on the configuration load process. +type config struct { + ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"` + ListCommands bool `short:"l" long:"listcommands" description:"List all of the supported commands and exit"` + ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"` + RPCUser string `short:"u" long:"rpcuser" description:"RPC username"` + RPCPassword string `short:"P" long:"rpcpass" default-mask:"-" description:"RPC password"` + RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"` + RPCCert string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"` + NoTLS bool `long:"notls" description:"Disable TLS"` + Proxy string `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"` + ProxyUser string `long:"proxyuser" description:"Username for proxy server"` + ProxyPass string `long:"proxypass" default-mask:"-" description:"Password for proxy server"` + TestNet3 bool `long:"testnet" description:"Connect to testnet"` + SimNet bool `long:"simnet" description:"Connect to the simulation test network"` + TLSSkipVerify bool `long:"skipverify" description:"Do not verify tls certificates (not recommended!)"` + Wallet bool `long:"wallet" description:"Connect to wallet"` +} + +// normalizeAddress returns addr with the passed default port appended if +// there is not already a port specified. +func normalizeAddress(addr string, useTestNet3, useSimNet, useWallet bool) string { + _, _, err := net.SplitHostPort(addr) + if err != nil { + var defaultPort string + switch { + case useTestNet3: + if useWallet { + defaultPort = "18332" + } else { + defaultPort = "18334" + } + case useSimNet: + if useWallet { + defaultPort = "18554" + } else { + defaultPort = "18556" + } + default: + if useWallet { + defaultPort = "8332" + } else { + defaultPort = "8334" + } + } + + return net.JoinHostPort(addr, defaultPort) + } + return addr +} + +// cleanAndExpandPath expands environement variables and leading ~ in the +// passed path, cleans the result, and returns it. +func cleanAndExpandPath(path string) string { + // Expand initial ~ to OS specific home directory. + if strings.HasPrefix(path, "~") { + homeDir := filepath.Dir(btcctlHomeDir) + path = strings.Replace(path, "~", homeDir, 1) + } + + // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%, + // but they variables can still be expanded via POSIX-style $VARIABLE. + return filepath.Clean(os.ExpandEnv(path)) +} + +// loadConfig initializes and parses the config using a config file and command +// line options. +// +// The configuration proceeds as follows: +// 1) Start with a default config with sane settings +// 2) Pre-parse the command line to check for an alternative config file +// 3) Load configuration file overwriting defaults with any specified options +// 4) Parse CLI options and overwrite/add any specified options +// +// The above results in functioning properly without any config settings +// while still allowing the user to override settings with config files and +// command line options. Command line options always take precedence. +func loadConfig() (*config, []string, error) { + // Default config. + cfg := config{ + ConfigFile: defaultConfigFile, + RPCServer: defaultRPCServer, + RPCCert: defaultRPCCertFile, + } + + // Create the home directory if it doesn't already exist. + err := os.MkdirAll(btcdHomeDir, 0700) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(-1) + } + + // Pre-parse the command line options to see if an alternative config + // file, the version flag, or the list commands flag was specified. Any + // errors aside from the help message error can be ignored here since + // they will be caught by the final parse below. + preCfg := cfg + preParser := flags.NewParser(&preCfg, flags.HelpFlag) + _, err = preParser.Parse() + if err != nil { + if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, "") + fmt.Fprintln(os.Stderr, "The special parameter `-` "+ + "indicates that a parameter should be read "+ + "from the\nnext unread line from standard "+ + "input.") + return nil, nil, err + } + } + + // Show the version and exit if the version flag was specified. + appName := filepath.Base(os.Args[0]) + appName = strings.TrimSuffix(appName, filepath.Ext(appName)) + usageMessage := fmt.Sprintf("Use %s -h to show options", appName) + if preCfg.ShowVersion { + fmt.Println(appName, "version", version()) + os.Exit(0) + } + + // Show the available commands and exit if the associated flag was + // specified. + if preCfg.ListCommands { + listCommands() + os.Exit(0) + } + + // Load additional config from file. + parser := flags.NewParser(&cfg, flags.Default) + err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile) + if err != nil { + if _, ok := err.(*os.PathError); !ok { + fmt.Fprintf(os.Stderr, "Error parsing config file: %v\n", + err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + } + + // Parse command line options again to ensure they take precedence. + remainingArgs, err := parser.Parse() + if err != nil { + if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { + fmt.Fprintln(os.Stderr, usageMessage) + } + return nil, nil, err + } + + // Multiple networks can't be selected simultaneously. + numNets := 0 + if cfg.TestNet3 { + numNets++ + } + if cfg.SimNet { + numNets++ + } + if numNets > 1 { + str := "%s: The testnet and simnet params can't be used " + + "together -- choose one of the two" + err := fmt.Errorf(str, "loadConfig") + fmt.Fprintln(os.Stderr, err) + return nil, nil, err + } + + // Override the RPC certificate if the --wallet flag was specified and + // the user did not specify one. + if cfg.Wallet && cfg.RPCCert == defaultRPCCertFile { + cfg.RPCCert = defaultWalletCertFile + } + + // Handle environment variable expansion in the RPC certificate path. + cfg.RPCCert = cleanAndExpandPath(cfg.RPCCert) + + // Add default port to RPC server based on --testnet and --wallet flags + // if needed. + cfg.RPCServer = normalizeAddress(cfg.RPCServer, cfg.TestNet3, + cfg.SimNet, cfg.Wallet) + + return &cfg, remainingArgs, nil +} diff --git a/vendor/github.com/btcsuite/btcd/cmd/btcctl/httpclient.go b/vendor/github.com/btcsuite/btcd/cmd/btcctl/httpclient.go new file mode 100644 index 0000000000000000000000000000000000000000..2a0f6dffd44a141567ef05ef5ee5fbc8bd282335 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/cmd/btcctl/httpclient.go @@ -0,0 +1,128 @@ +package main + +import ( + "bytes" + "crypto/tls" + "crypto/x509" + "encoding/json" + "fmt" + "io/ioutil" + "net" + "net/http" + + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/go-socks/socks" +) + +// newHTTPClient returns a new HTTP client that is configured according to the +// proxy and TLS settings in the associated connection configuration. +func newHTTPClient(cfg *config) (*http.Client, error) { + // Configure proxy if needed. + var dial func(network, addr string) (net.Conn, error) + if cfg.Proxy != "" { + proxy := &socks.Proxy{ + Addr: cfg.Proxy, + Username: cfg.ProxyUser, + Password: cfg.ProxyPass, + } + dial = func(network, addr string) (net.Conn, error) { + c, err := proxy.Dial(network, addr) + if err != nil { + return nil, err + } + return c, nil + } + } + + // Configure TLS if needed. + var tlsConfig *tls.Config + if !cfg.NoTLS && cfg.RPCCert != "" { + pem, err := ioutil.ReadFile(cfg.RPCCert) + if err != nil { + return nil, err + } + + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(pem) + tlsConfig = &tls.Config{ + RootCAs: pool, + InsecureSkipVerify: cfg.TLSSkipVerify, + } + } + + // Create and return the new HTTP client potentially configured with a + // proxy and TLS. + client := http.Client{ + Transport: &http.Transport{ + Dial: dial, + TLSClientConfig: tlsConfig, + }, + } + return &client, nil +} + +// sendPostRequest sends the marshalled JSON-RPC command using HTTP-POST mode +// to the server described in the passed config struct. It also attempts to +// unmarshal the response as a JSON-RPC response and returns either the result +// field or the error field depending on whether or not there is an error. +func sendPostRequest(marshalledJSON []byte, cfg *config) ([]byte, error) { + // Generate a request to the configured RPC server. + protocol := "http" + if !cfg.NoTLS { + protocol = "https" + } + url := protocol + "://" + cfg.RPCServer + bodyReader := bytes.NewReader(marshalledJSON) + httpRequest, err := http.NewRequest("POST", url, bodyReader) + if err != nil { + return nil, err + } + httpRequest.Close = true + httpRequest.Header.Set("Content-Type", "application/json") + + // Configure basic access authorization. + httpRequest.SetBasicAuth(cfg.RPCUser, cfg.RPCPassword) + + // Create the new HTTP client that is configured according to the user- + // specified options and submit the request. + httpClient, err := newHTTPClient(cfg) + if err != nil { + return nil, err + } + httpResponse, err := httpClient.Do(httpRequest) + if err != nil { + return nil, err + } + + // Read the raw bytes and close the response. + respBytes, err := ioutil.ReadAll(httpResponse.Body) + httpResponse.Body.Close() + if err != nil { + err = fmt.Errorf("error reading json reply: %v", err) + return nil, err + } + + // Handle unsuccessful HTTP responses + if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 { + // Generate a standard error to return if the server body is + // empty. This should not happen very often, but it's better + // than showing nothing in case the target server has a poor + // implementation. + if len(respBytes) == 0 { + return nil, fmt.Errorf("%d %s", httpResponse.StatusCode, + http.StatusText(httpResponse.StatusCode)) + } + return nil, fmt.Errorf("%s", respBytes) + } + + // Unmarshal the response. + var resp btcjson.Response + if err := json.Unmarshal(respBytes, &resp); err != nil { + return nil, err + } + + if resp.Error != nil { + return nil, resp.Error + } + return resp.Result, nil +} diff --git a/vendor/github.com/btcsuite/btcd/cmd/btcctl/version.go b/vendor/github.com/btcsuite/btcd/cmd/btcctl/version.go new file mode 100644 index 0000000000000000000000000000000000000000..fb147bcda58c2ab6b6a0e1f080024242cf5bb70a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/cmd/btcctl/version.go @@ -0,0 +1,75 @@ +// Copyright (c) 2013 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "fmt" + "strings" +) + +// semanticAlphabet +const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-" + +// These constants define the application version and follow the semantic +// versioning 2.0.0 spec (http://semver.org/). +const ( + appMajor uint = 0 + appMinor uint = 12 + appPatch uint = 0 + + // appPreRelease MUST only contain characters from semanticAlphabet + // per the semantic versioning spec. + appPreRelease = "beta" +) + +// appBuild is defined as a variable so it can be overridden during the build +// process with '-ldflags "-X main.appBuild foo' if needed. It MUST only +// contain characters from semanticAlphabet per the semantic versioning spec. +var appBuild string + +// version returns the application version as a properly formed string per the +// semantic versioning 2.0.0 spec (http://semver.org/). +func version() string { + // Start with the major, minor, and patch versions. + version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch) + + // Append pre-release version if there is one. The hyphen called for + // by the semantic versioning spec is automatically appended and should + // not be contained in the pre-release string. The pre-release version + // is not appended if it contains invalid characters. + preRelease := normalizeVerString(appPreRelease) + if preRelease != "" { + version = fmt.Sprintf("%s-%s", version, preRelease) + } + + // Append build metadata if there is any. The plus called for + // by the semantic versioning spec is automatically appended and should + // not be contained in the build metadata string. The build metadata + // string is not appended if it contains invalid characters. + build := normalizeVerString(appBuild) + if build != "" { + version = fmt.Sprintf("%s+%s", version, build) + } + + return version +} + +// normalizeVerString returns the passed string stripped of all characters which +// are not valid according to the semantic versioning guidelines for pre-release +// version and build metadata strings. In particular they MUST only contain +// characters in semanticAlphabet. +func normalizeVerString(str string) string { + var result bytes.Buffer + for _, r := range str { + if strings.ContainsRune(semanticAlphabet, r) { + // Ignoring the error here since it can only fail if + // the the system is out of memory and there are much + // bigger issues at that point. + _, _ = result.WriteRune(r) + } + } + return result.String() +} diff --git a/vendor/github.com/btcsuite/btcd/cmd/findcheckpoint/config.go b/vendor/github.com/btcsuite/btcd/cmd/findcheckpoint/config.go new file mode 100644 index 0000000000000000000000000000000000000000..bec25b30c757781595900bb256228513d760d6f0 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/cmd/findcheckpoint/config.go @@ -0,0 +1,150 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + _ "github.com/btcsuite/btcd/database/ffldb" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + flags "github.com/btcsuite/go-flags" +) + +const ( + minCandidates = 1 + maxCandidates = 20 + defaultNumCandidates = 5 + defaultDbType = "ffldb" +) + +var ( + btcdHomeDir = btcutil.AppDataDir("btcd", false) + defaultDataDir = filepath.Join(btcdHomeDir, "data") + knownDbTypes = database.SupportedDrivers() + activeNetParams = &chaincfg.MainNetParams +) + +// config defines the configuration options for findcheckpoint. +// +// See loadConfig for details on the configuration load process. +type config struct { + DataDir string `short:"b" long:"datadir" description:"Location of the btcd data directory"` + DbType string `long:"dbtype" description:"Database backend to use for the Block Chain"` + TestNet3 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"` + NumCandidates int `short:"n" long:"numcandidates" description:"Max num of checkpoint candidates to show {1-20}"` + UseGoOutput bool `short:"g" long:"gooutput" description:"Display the candidates using Go syntax that is ready to insert into the btcchain checkpoint list"` +} + +// validDbType returns whether or not dbType is a supported database type. +func validDbType(dbType string) bool { + for _, knownType := range knownDbTypes { + if dbType == knownType { + return true + } + } + + return false +} + +// netName returns the name used when referring to a bitcoin network. At the +// time of writing, btcd currently places blocks for testnet version 3 in the +// data and log directory "testnet", which does not match the Name field of the +// chaincfg parameters. This function can be used to override this directory name +// as "testnet" when the passed active network matches wire.TestNet3. +// +// A proper upgrade to move the data and log directories for this network to +// "testnet3" is planned for the future, at which point this function can be +// removed and the network parameter's name used instead. +func netName(chainParams *chaincfg.Params) string { + switch chainParams.Net { + case wire.TestNet3: + return "testnet" + default: + return chainParams.Name + } +} + +// loadConfig initializes and parses the config using command line options. +func loadConfig() (*config, []string, error) { + // Default config. + cfg := config{ + DataDir: defaultDataDir, + DbType: defaultDbType, + NumCandidates: defaultNumCandidates, + } + + // Parse command line options. + parser := flags.NewParser(&cfg, flags.Default) + remainingArgs, err := parser.Parse() + if err != nil { + if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { + parser.WriteHelp(os.Stderr) + } + return nil, nil, err + } + + // 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.TestNet3 { + numNets++ + activeNetParams = &chaincfg.TestNet3Params + } + if cfg.RegressionTest { + numNets++ + activeNetParams = &chaincfg.RegressionNetParams + } + if cfg.SimNet { + numNets++ + activeNetParams = &chaincfg.SimNetParams + } + if numNets > 1 { + str := "%s: The testnet, regtest, and simnet params can't be " + + "used together -- choose one of the three" + err := fmt.Errorf(str, funcName) + fmt.Fprintln(os.Stderr, err) + parser.WriteHelp(os.Stderr) + return nil, nil, err + } + + // Validate database type. + if !validDbType(cfg.DbType) { + str := "%s: The specified database type [%v] is invalid -- " + + "supported types %v" + err := fmt.Errorf(str, "loadConfig", cfg.DbType, knownDbTypes) + fmt.Fprintln(os.Stderr, err) + parser.WriteHelp(os.Stderr) + return nil, nil, err + } + + // Append the network type to the data directory so it is "namespaced" + // per network. In addition to the block database, there are other + // pieces of data that are saved to disk such as address manager state. + // All data is specific to a network, so namespacing the data directory + // means each individual piece of serialized data does not have to + // worry about changing names per network and such. + cfg.DataDir = filepath.Join(cfg.DataDir, netName(activeNetParams)) + + // Validate the number of candidates. + if cfg.NumCandidates < minCandidates || cfg.NumCandidates > maxCandidates { + str := "%s: The specified number of candidates is out of " + + "range -- parsed [%v]" + err = fmt.Errorf(str, "loadConfig", cfg.NumCandidates) + fmt.Fprintln(os.Stderr, err) + parser.WriteHelp(os.Stderr) + return nil, nil, err + } + + return &cfg, remainingArgs, nil +} diff --git a/vendor/github.com/btcsuite/btcd/cmd/findcheckpoint/findcheckpoint.go b/vendor/github.com/btcsuite/btcd/cmd/findcheckpoint/findcheckpoint.go new file mode 100644 index 0000000000000000000000000000000000000000..376e3bf8039c897afcadae060ce9e8160c89d4b6 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/cmd/findcheckpoint/findcheckpoint.go @@ -0,0 +1,184 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" +) + +const blockDbNamePrefix = "blocks" + +var ( + cfg *config +) + +// loadBlockDB opens the block database and returns a handle to it. +func loadBlockDB() (database.DB, error) { + // The database name is based on the database type. + dbName := blockDbNamePrefix + "_" + cfg.DbType + dbPath := filepath.Join(cfg.DataDir, dbName) + fmt.Printf("Loading block database from '%s'\n", dbPath) + db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net) + if err != nil { + return nil, err + } + return db, nil +} + +// findCandidates searches the chain backwards for checkpoint candidates and +// returns a slice of found candidates, if any. It also stops searching for +// candidates at the last checkpoint that is already hard coded into btcchain +// since there is no point in finding candidates before already existing +// checkpoints. +func findCandidates(chain *blockchain.BlockChain, latestHash *wire.ShaHash) ([]*chaincfg.Checkpoint, error) { + // Start with the latest block of the main chain. + block, err := chain.BlockByHash(latestHash) + if err != nil { + return nil, err + } + + // Get the latest known checkpoint. + latestCheckpoint := chain.LatestCheckpoint() + if latestCheckpoint == nil { + // Set the latest checkpoint to the genesis block if there isn't + // already one. + latestCheckpoint = &chaincfg.Checkpoint{ + Hash: activeNetParams.GenesisHash, + Height: 0, + } + } + + // The latest known block must be at least the last known checkpoint + // plus required checkpoint confirmations. + checkpointConfirmations := int32(blockchain.CheckpointConfirmations) + requiredHeight := latestCheckpoint.Height + checkpointConfirmations + if block.Height() < requiredHeight { + return nil, fmt.Errorf("the block database is only at height "+ + "%d which is less than the latest checkpoint height "+ + "of %d plus required confirmations of %d", + block.Height(), latestCheckpoint.Height, + checkpointConfirmations) + } + + // For the first checkpoint, the required height is any block after the + // genesis block, so long as the chain has at least the required number + // of confirmations (which is enforced above). + if len(activeNetParams.Checkpoints) == 0 { + requiredHeight = 1 + } + + // Indeterminate progress setup. + numBlocksToTest := block.Height() - requiredHeight + progressInterval := (numBlocksToTest / 100) + 1 // min 1 + fmt.Print("Searching for candidates") + defer fmt.Println() + + // Loop backwards through the chain to find checkpoint candidates. + candidates := make([]*chaincfg.Checkpoint, 0, cfg.NumCandidates) + numTested := int32(0) + for len(candidates) < cfg.NumCandidates && block.Height() > requiredHeight { + // Display progress. + if numTested%progressInterval == 0 { + fmt.Print(".") + } + + // Determine if this block is a checkpoint candidate. + isCandidate, err := chain.IsCheckpointCandidate(block) + if err != nil { + return nil, err + } + + // All checks passed, so this node seems like a reasonable + // checkpoint candidate. + if isCandidate { + checkpoint := chaincfg.Checkpoint{ + Height: block.Height(), + Hash: block.Sha(), + } + candidates = append(candidates, &checkpoint) + } + + prevHash := &block.MsgBlock().Header.PrevBlock + block, err = chain.BlockByHash(prevHash) + if err != nil { + return nil, err + } + numTested++ + } + return candidates, nil +} + +// showCandidate display a checkpoint candidate using and output format +// determined by the configuration parameters. The Go syntax output +// uses the format the btcchain code expects for checkpoints added to the list. +func showCandidate(candidateNum int, checkpoint *chaincfg.Checkpoint) { + if cfg.UseGoOutput { + fmt.Printf("Candidate %d -- {%d, newShaHashFromStr(\"%v\")},\n", + candidateNum, checkpoint.Height, checkpoint.Hash) + return + } + + fmt.Printf("Candidate %d -- Height: %d, Hash: %v\n", candidateNum, + checkpoint.Height, checkpoint.Hash) + +} + +func main() { + // Load configuration and parse command line. + tcfg, _, err := loadConfig() + if err != nil { + return + } + cfg = tcfg + + // Load the block database. + db, err := loadBlockDB() + if err != nil { + fmt.Fprintln(os.Stderr, "failed to load database:", err) + return + } + defer db.Close() + + // Setup chain. Ignore notifications since they aren't needed for this + // util. + chain, err := blockchain.New(&blockchain.Config{ + DB: db, + ChainParams: activeNetParams, + }) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to initialize chain: %v\n", err) + return + } + + // Get the latest block hash and height from the database and report + // status. + best := chain.BestSnapshot() + fmt.Printf("Block database loaded with block height %d\n", best.Height) + + // Find checkpoint candidates. + candidates, err := findCandidates(chain, best.Hash) + if err != nil { + fmt.Fprintln(os.Stderr, "Unable to identify candidates:", err) + return + } + + // No candidates. + if len(candidates) == 0 { + fmt.Println("No candidates found.") + return + } + + // Show the candidates. + for i, checkpoint := range candidates { + showCandidate(i+1, checkpoint) + } +} diff --git a/vendor/github.com/btcsuite/btcd/cmd/gencerts/gencerts.go b/vendor/github.com/btcsuite/btcd/cmd/gencerts/gencerts.go new file mode 100644 index 0000000000000000000000000000000000000000..959b264a3a2282512aa120c27eb601a6aef9c3d0 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/cmd/gencerts/gencerts.go @@ -0,0 +1,102 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" + + "github.com/btcsuite/btcutil" + flags "github.com/btcsuite/go-flags" +) + +type config struct { + Directory string `short:"d" long:"directory" description:"Directory to write certificate pair"` + Years int `short:"y" long:"years" description:"How many years a certificate is valid for"` + Organization string `short:"o" long:"org" description:"Organization in certificate"` + ExtraHosts []string `short:"H" long:"host" description:"Additional hosts/IPs to create certificate for"` + Force bool `short:"f" long:"force" description:"Force overwriting of any old certs and keys"` +} + +func main() { + cfg := config{ + Years: 10, + Organization: "gencerts", + } + parser := flags.NewParser(&cfg, flags.Default) + _, err := parser.Parse() + if err != nil { + if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { + parser.WriteHelp(os.Stderr) + } + return + } + + if cfg.Directory == "" { + var err error + cfg.Directory, err = os.Getwd() + if err != nil { + fmt.Fprintf(os.Stderr, "no directory specified and cannot get working directory\n") + os.Exit(1) + } + } + cfg.Directory = cleanAndExpandPath(cfg.Directory) + certFile := filepath.Join(cfg.Directory, "rpc.cert") + keyFile := filepath.Join(cfg.Directory, "rpc.key") + + if !cfg.Force { + if fileExists(certFile) || fileExists(keyFile) { + fmt.Fprintf(os.Stderr, "%v: certificate and/or key files exist; use -f to force\n", cfg.Directory) + os.Exit(1) + } + } + + validUntil := time.Now().Add(time.Duration(cfg.Years) * 365 * 24 * time.Hour) + cert, key, err := btcutil.NewTLSCertPair(cfg.Organization, validUntil, cfg.ExtraHosts) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot generate certificate pair: %v\n", err) + os.Exit(1) + } + + // Write cert and key files. + if err = ioutil.WriteFile(certFile, cert, 0666); err != nil { + fmt.Fprintf(os.Stderr, "cannot write cert: %v\n", err) + os.Exit(1) + } + if err = ioutil.WriteFile(keyFile, key, 0600); err != nil { + os.Remove(certFile) + fmt.Fprintf(os.Stderr, "cannot write key: %v\n", err) + os.Exit(1) + } +} + +// cleanAndExpandPath expands environement variables and leading ~ in the +// passed path, cleans the result, and returns it. +func cleanAndExpandPath(path string) string { + // Expand initial ~ to OS specific home directory. + if strings.HasPrefix(path, "~") { + appHomeDir := btcutil.AppDataDir("gencerts", false) + homeDir := filepath.Dir(appHomeDir) + path = strings.Replace(path, "~", homeDir, 1) + } + + // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%, + // but they variables can still be expanded via POSIX-style $VARIABLE. + return filepath.Clean(os.ExpandEnv(path)) +} + +// filesExists reports whether the named file or directory exists. +func fileExists(name string) bool { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +} diff --git a/vendor/github.com/btcsuite/btcd/config.go b/vendor/github.com/btcsuite/btcd/config.go new file mode 100644 index 0000000000000000000000000000000000000000..0e00caa0de234abef8b145c5d9c989a7a647848f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/config.go @@ -0,0 +1,907 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "errors" + "fmt" + "net" + "os" + "path/filepath" + "runtime" + "sort" + "strconv" + "strings" + "time" + + "github.com/btcsuite/btcd/database" + _ "github.com/btcsuite/btcd/database/ffldb" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + flags "github.com/btcsuite/go-flags" + "github.com/btcsuite/go-socks/socks" +) + +const ( + defaultConfigFilename = "btcd.conf" + defaultDataDirname = "data" + defaultLogLevel = "info" + defaultLogDirname = "logs" + defaultLogFilename = "btcd.log" + defaultMaxPeers = 125 + defaultBanDuration = time.Hour * 24 + defaultBanThreshold = 100 + defaultMaxRPCClients = 10 + defaultMaxRPCWebsockets = 25 + defaultVerifyEnabled = false + defaultDbType = "ffldb" + defaultFreeTxRelayLimit = 15.0 + defaultBlockMinSize = 0 + defaultBlockMaxSize = 750000 + blockMaxSizeMin = 1000 + blockMaxSizeMax = wire.MaxBlockPayload - 1000 + defaultBlockPrioritySize = 50000 + defaultGenerate = false + defaultMaxOrphanTransactions = 1000 + defaultMaxOrphanTxSize = 5000 + defaultSigCacheMaxSize = 100000 + defaultTxIndex = false + defaultAddrIndex = false +) + +var ( + btcdHomeDir = btcutil.AppDataDir("btcd", false) + defaultConfigFile = filepath.Join(btcdHomeDir, defaultConfigFilename) + defaultDataDir = filepath.Join(btcdHomeDir, defaultDataDirname) + knownDbTypes = database.SupportedDrivers() + defaultRPCKeyFile = filepath.Join(btcdHomeDir, "rpc.key") + defaultRPCCertFile = filepath.Join(btcdHomeDir, "rpc.cert") + defaultLogDir = filepath.Join(btcdHomeDir, defaultLogDirname) +) + +// runServiceCommand is only set to a real function on Windows. It is used +// to parse and execute service commands specified via the -s flag. +var runServiceCommand func(string) error + +// minUint32 is a helper function to return the minimum of two uint32s. +// This avoids a math import and the need to cast to floats. +func minUint32(a, b uint32) uint32 { + if a < b { + return a + } + return b +} + +// config defines the configuration options for btcd. +// +// See loadConfig for details on the configuration load process. +type config struct { + ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"` + ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"` + DataDir string `short:"b" long:"datadir" description:"Directory to store data"` + LogDir string `long:"logdir" description:"Directory to log output."` + AddPeers []string `short:"a" long:"addpeer" description:"Add a peer to connect with at startup"` + ConnectPeers []string `long:"connect" description:"Connect only to the specified peers at startup"` + DisableListen bool `long:"nolisten" description:"Disable listening for incoming connections -- NOTE: Listening is automatically disabled if the --connect or --proxy options are used without also specifying listen interfaces via --listen"` + Listeners []string `long:"listen" description:"Add an interface/port to listen for connections (default all interfaces port: 8333, testnet: 18333)"` + MaxPeers int `long:"maxpeers" description:"Max number of inbound and outbound peers"` + DisableBanning bool `long:"nobanning" description:"Disable banning of misbehaving peers"` + BanDuration time.Duration `long:"banduration" description:"How long to ban misbehaving peers. Valid time units are {s, m, h}. Minimum 1 second"` + BanThreshold uint32 `long:"banthreshold" description:"Maximum allowed ban score before disconnecting and banning misbehaving peers."` + RPCUser string `short:"u" long:"rpcuser" description:"Username for RPC connections"` + RPCPass string `short:"P" long:"rpcpass" default-mask:"-" description:"Password for RPC connections"` + RPCLimitUser string `long:"rpclimituser" description:"Username for limited RPC connections"` + RPCLimitPass string `long:"rpclimitpass" default-mask:"-" description:"Password for limited RPC connections"` + RPCListeners []string `long:"rpclisten" description:"Add an interface/port to listen for RPC connections (default port: 8334, testnet: 18334)"` + RPCCert string `long:"rpccert" description:"File containing the certificate file"` + RPCKey string `long:"rpckey" description:"File containing the certificate key"` + RPCMaxClients int `long:"rpcmaxclients" description:"Max number of RPC clients for standard connections"` + RPCMaxWebsockets int `long:"rpcmaxwebsockets" description:"Max number of RPC websocket connections"` + DisableRPC bool `long:"norpc" description:"Disable built-in RPC server -- NOTE: The RPC server is disabled by default if no rpcuser/rpcpass or rpclimituser/rpclimitpass is specified"` + DisableTLS bool `long:"notls" description:"Disable TLS for the RPC server -- NOTE: This is only allowed if the RPC server is bound to localhost"` + DisableDNSSeed bool `long:"nodnsseed" description:"Disable DNS seeding for peers"` + ExternalIPs []string `long:"externalip" description:"Add an ip to the list of local addresses we claim to listen on to peers"` + Proxy string `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"` + ProxyUser string `long:"proxyuser" description:"Username for proxy server"` + ProxyPass string `long:"proxypass" default-mask:"-" description:"Password for proxy server"` + OnionProxy string `long:"onion" description:"Connect to tor hidden services via SOCKS5 proxy (eg. 127.0.0.1:9050)"` + OnionProxyUser string `long:"onionuser" description:"Username for onion proxy server"` + OnionProxyPass string `long:"onionpass" default-mask:"-" description:"Password for onion proxy server"` + NoOnion bool `long:"noonion" description:"Disable connecting to tor hidden services"` + TorIsolation bool `long:"torisolation" description:"Enable Tor stream isolation by randomizing user credentials for each connection."` + TestNet3 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"` + DisableCheckpoints bool `long:"nocheckpoints" description:"Disable built-in checkpoints. Don't do this unless you know what you're doing."` + DbType string `long:"dbtype" description:"Database backend to use for the Block Chain"` + Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"` + CPUProfile string `long:"cpuprofile" description:"Write CPU profile to the specified file"` + DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify <subsystem>=<level>,<subsystem2>=<level>,... to set the log level for individual subsystems -- Use show to list available subsystems"` + Upnp bool `long:"upnp" description:"Use UPnP to map our listening port outside of NAT"` + MinRelayTxFee float64 `long:"minrelaytxfee" description:"The minimum transaction fee in BTC/kB to be considered a non-zero fee."` + FreeTxRelayLimit float64 `long:"limitfreerelay" description:"Limit relay of transactions with no transaction fee to the given amount in thousands of bytes per minute"` + NoRelayPriority bool `long:"norelaypriority" description:"Do not require free or low-fee transactions to have high priority for relaying"` + MaxOrphanTxs int `long:"maxorphantx" description:"Max number of orphan transactions to keep in memory"` + Generate bool `long:"generate" description:"Generate (mine) bitcoins using the CPU"` + MiningAddrs []string `long:"miningaddr" description:"Add the specified payment address to the list of addresses to use for generated blocks -- At least one address is required if the generate option is set"` + BlockMinSize uint32 `long:"blockminsize" description:"Mininum block size in bytes to be used when creating a block"` + BlockMaxSize uint32 `long:"blockmaxsize" description:"Maximum block size in bytes to be used when creating a block"` + BlockPrioritySize uint32 `long:"blockprioritysize" description:"Size in bytes for high-priority/low-fee transactions when creating a block"` + GetWorkKeys []string `long:"getworkkey" description:"DEPRECATED -- Use the --miningaddr option instead"` + NoPeerBloomFilters bool `long:"nopeerbloomfilters" description:"Disable bloom filtering support"` + SigCacheMaxSize uint `long:"sigcachemaxsize" description:"The maximum number of entries in the signature verification cache"` + BlocksOnly bool `long:"blocksonly" description:"Do not accept transactions from remote peers."` + TxIndex bool `long:"txindex" description:"Maintain a full hash-based transaction index which makes all transactions available via the getrawtransaction RPC"` + DropTxIndex bool `long:"droptxindex" description:"Deletes the hash-based transaction index from the database on start up and then exits."` + AddrIndex bool `long:"addrindex" description:"Maintain a full address-based transaction index which makes the searchrawtransactions RPC available"` + DropAddrIndex bool `long:"dropaddrindex" description:"Deletes the address-based transaction index from the database on start up and then exits."` + onionlookup func(string) ([]net.IP, error) + lookup func(string) ([]net.IP, error) + oniondial func(string, string) (net.Conn, error) + dial func(string, string) (net.Conn, error) + miningAddrs []btcutil.Address + minRelayTxFee btcutil.Amount +} + +// serviceOptions defines the configuration options for btcd as a service on +// Windows. +type serviceOptions struct { + ServiceCommand string `short:"s" long:"service" description:"Service command {install, remove, start, stop}"` +} + +// cleanAndExpandPath expands environment variables and leading ~ in the +// passed path, cleans the result, and returns it. +func cleanAndExpandPath(path string) string { + // Expand initial ~ to OS specific home directory. + if strings.HasPrefix(path, "~") { + homeDir := filepath.Dir(btcdHomeDir) + path = strings.Replace(path, "~", homeDir, 1) + } + + // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%, + // but they variables can still be expanded via POSIX-style $VARIABLE. + return filepath.Clean(os.ExpandEnv(path)) +} + +// validLogLevel returns whether or not logLevel is a valid debug log level. +func validLogLevel(logLevel string) bool { + switch logLevel { + case "trace": + fallthrough + case "debug": + fallthrough + case "info": + fallthrough + case "warn": + fallthrough + case "error": + fallthrough + case "critical": + return true + } + return false +} + +// supportedSubsystems returns a sorted slice of the supported subsystems for +// logging purposes. +func supportedSubsystems() []string { + // Convert the subsystemLoggers map keys to a slice. + subsystems := make([]string, 0, len(subsystemLoggers)) + for subsysID := range subsystemLoggers { + subsystems = append(subsystems, subsysID) + } + + // Sort the subsystems for stable display. + sort.Strings(subsystems) + return subsystems +} + +// parseAndSetDebugLevels attempts to parse the specified debug level and set +// the levels accordingly. An appropriate error is returned if anything is +// invalid. +func parseAndSetDebugLevels(debugLevel string) error { + // When the specified string doesn't have any delimters, treat it as + // the log level for all subsystems. + if !strings.Contains(debugLevel, ",") && !strings.Contains(debugLevel, "=") { + // Validate debug log level. + if !validLogLevel(debugLevel) { + str := "The specified debug level [%v] is invalid" + return fmt.Errorf(str, debugLevel) + } + + // Change the logging level for all subsystems. + setLogLevels(debugLevel) + + return nil + } + + // Split the specified string into subsystem/level pairs while detecting + // issues and update the log levels accordingly. + for _, logLevelPair := range strings.Split(debugLevel, ",") { + if !strings.Contains(logLevelPair, "=") { + str := "The specified debug level contains an invalid " + + "subsystem/level pair [%v]" + return fmt.Errorf(str, logLevelPair) + } + + // Extract the specified subsystem and log level. + fields := strings.Split(logLevelPair, "=") + subsysID, logLevel := fields[0], fields[1] + + // Validate subsystem. + if _, exists := subsystemLoggers[subsysID]; !exists { + str := "The specified subsystem [%v] is invalid -- " + + "supported subsytems %v" + return fmt.Errorf(str, subsysID, supportedSubsystems()) + } + + // Validate log level. + if !validLogLevel(logLevel) { + str := "The specified debug level [%v] is invalid" + return fmt.Errorf(str, logLevel) + } + + setLogLevel(subsysID, logLevel) + } + + return nil +} + +// validDbType returns whether or not dbType is a supported database type. +func validDbType(dbType string) bool { + for _, knownType := range knownDbTypes { + if dbType == knownType { + return true + } + } + + return false +} + +// removeDuplicateAddresses returns a new slice with all duplicate entries in +// addrs removed. +func removeDuplicateAddresses(addrs []string) []string { + result := make([]string, 0, len(addrs)) + seen := map[string]struct{}{} + for _, val := range addrs { + if _, ok := seen[val]; !ok { + result = append(result, val) + seen[val] = struct{}{} + } + } + return result +} + +// normalizeAddress returns addr with the passed default port appended if +// there is not already a port specified. +func normalizeAddress(addr, defaultPort string) string { + _, _, err := net.SplitHostPort(addr) + if err != nil { + return net.JoinHostPort(addr, defaultPort) + } + return addr +} + +// normalizeAddresses returns a new slice with all the passed peer addresses +// normalized with the given default port, and all duplicates removed. +func normalizeAddresses(addrs []string, defaultPort string) []string { + for i, addr := range addrs { + addrs[i] = normalizeAddress(addr, defaultPort) + } + + return removeDuplicateAddresses(addrs) +} + +// filesExists reports whether the named file or directory exists. +func fileExists(name string) bool { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +} + +// newConfigParser returns a new command line flags parser. +func newConfigParser(cfg *config, so *serviceOptions, options flags.Options) *flags.Parser { + parser := flags.NewParser(cfg, options) + if runtime.GOOS == "windows" { + parser.AddGroup("Service Options", "Service Options", so) + } + return parser +} + +// loadConfig initializes and parses the config using a config file and command +// line options. +// +// The configuration proceeds as follows: +// 1) Start with a default config with sane settings +// 2) Pre-parse the command line to check for an alternative config file +// 3) Load configuration file overwriting defaults with any specified options +// 4) Parse CLI options and overwrite/add any specified options +// +// The above results in btcd functioning properly without any config settings +// while still allowing the user to override settings with config files and +// command line options. Command line options always take precedence. +func loadConfig() (*config, []string, error) { + // Default config. + cfg := config{ + ConfigFile: defaultConfigFile, + DebugLevel: defaultLogLevel, + MaxPeers: defaultMaxPeers, + BanDuration: defaultBanDuration, + BanThreshold: defaultBanThreshold, + RPCMaxClients: defaultMaxRPCClients, + RPCMaxWebsockets: defaultMaxRPCWebsockets, + DataDir: defaultDataDir, + LogDir: defaultLogDir, + DbType: defaultDbType, + RPCKey: defaultRPCKeyFile, + RPCCert: defaultRPCCertFile, + MinRelayTxFee: defaultMinRelayTxFee.ToBTC(), + FreeTxRelayLimit: defaultFreeTxRelayLimit, + BlockMinSize: defaultBlockMinSize, + BlockMaxSize: defaultBlockMaxSize, + BlockPrioritySize: defaultBlockPrioritySize, + MaxOrphanTxs: defaultMaxOrphanTransactions, + SigCacheMaxSize: defaultSigCacheMaxSize, + Generate: defaultGenerate, + TxIndex: defaultTxIndex, + AddrIndex: defaultAddrIndex, + } + + // Service options which are only added on Windows. + serviceOpts := serviceOptions{} + + // Pre-parse the command line options to see if an alternative config + // file or the version flag was specified. Any errors aside from the + // help message error can be ignored here since they will be caught by + // the final parse below. + preCfg := cfg + preParser := newConfigParser(&preCfg, &serviceOpts, flags.HelpFlag) + _, err := preParser.Parse() + if err != nil { + if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { + fmt.Fprintln(os.Stderr, err) + return nil, nil, err + } + } + + // Show the version and exit if the version flag was specified. + appName := filepath.Base(os.Args[0]) + appName = strings.TrimSuffix(appName, filepath.Ext(appName)) + usageMessage := fmt.Sprintf("Use %s -h to show usage", appName) + if preCfg.ShowVersion { + fmt.Println(appName, "version", version()) + os.Exit(0) + } + + // Perform service command and exit if specified. Invalid service + // commands show an appropriate error. Only runs on Windows since + // the runServiceCommand function will be nil when not on Windows. + if serviceOpts.ServiceCommand != "" && runServiceCommand != nil { + err := runServiceCommand(serviceOpts.ServiceCommand) + if err != nil { + fmt.Fprintln(os.Stderr, err) + } + os.Exit(0) + } + + // Load additional config from file. + var configFileError error + parser := newConfigParser(&cfg, &serviceOpts, flags.Default) + if !(preCfg.RegressionTest || preCfg.SimNet) || preCfg.ConfigFile != + defaultConfigFile { + + err := flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile) + if err != nil { + if _, ok := err.(*os.PathError); !ok { + fmt.Fprintf(os.Stderr, "Error parsing config "+ + "file: %v\n", err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + configFileError = err + } + } + + // Don't add peers from the config file when in regression test mode. + if preCfg.RegressionTest && len(cfg.AddPeers) > 0 { + cfg.AddPeers = nil + } + + // Parse command line options again to ensure they take precedence. + remainingArgs, err := parser.Parse() + if err != nil { + if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { + fmt.Fprintln(os.Stderr, usageMessage) + } + return nil, nil, err + } + + // Create the home directory if it doesn't already exist. + funcName := "loadConfig" + err = os.MkdirAll(btcdHomeDir, 0700) + if err != nil { + // Show a nicer error message if it's because a symlink is + // linked to a directory that does not exist (probably because + // it's not mounted). + if e, ok := err.(*os.PathError); ok && os.IsExist(err) { + if link, lerr := os.Readlink(e.Path); lerr == nil { + str := "is symlink %s -> %s mounted?" + err = fmt.Errorf(str, e.Path, link) + } + } + + str := "%s: Failed to create home directory: %v" + err := fmt.Errorf(str, funcName, err) + fmt.Fprintln(os.Stderr, err) + return nil, nil, err + } + + // Multiple networks can't be selected simultaneously. + numNets := 0 + // Count number of network flags passed; assign active network params + // while we're at it + if cfg.TestNet3 { + numNets++ + activeNetParams = &testNet3Params + } + if cfg.RegressionTest { + numNets++ + activeNetParams = ®ressionNetParams + } + if cfg.SimNet { + numNets++ + // Also disable dns seeding on the simulation test network. + activeNetParams = &simNetParams + cfg.DisableDNSSeed = true + } + if numNets > 1 { + str := "%s: The testnet, regtest, and simnet params can't be " + + "used together -- choose one of the three" + err := fmt.Errorf(str, funcName) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // Append the network type to the data directory so it is "namespaced" + // per network. In addition to the block database, there are other + // pieces of data that are saved to disk such as address manager state. + // All data is specific to a network, so namespacing the data directory + // means each individual piece of serialized data does not have to + // worry about changing names per network and such. + cfg.DataDir = cleanAndExpandPath(cfg.DataDir) + cfg.DataDir = filepath.Join(cfg.DataDir, netName(activeNetParams)) + + // Append the network type to the log directory so it is "namespaced" + // per network in the same fashion as the data directory. + cfg.LogDir = cleanAndExpandPath(cfg.LogDir) + cfg.LogDir = filepath.Join(cfg.LogDir, netName(activeNetParams)) + + // Special show command to list supported subsystems and exit. + if cfg.DebugLevel == "show" { + fmt.Println("Supported subsystems", supportedSubsystems()) + os.Exit(0) + } + + // Initialize logging at the default logging level. + initSeelogLogger(filepath.Join(cfg.LogDir, defaultLogFilename)) + setLogLevels(defaultLogLevel) + + // Parse, validate, and set debug log level(s). + if err := parseAndSetDebugLevels(cfg.DebugLevel); err != nil { + err := fmt.Errorf("%s: %v", funcName, err.Error()) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // Validate database type. + if !validDbType(cfg.DbType) { + str := "%s: The specified database type [%v] is invalid -- " + + "supported types %v" + err := fmt.Errorf(str, funcName, cfg.DbType, knownDbTypes) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // Validate profile port number + if cfg.Profile != "" { + profilePort, err := strconv.Atoi(cfg.Profile) + if err != nil || profilePort < 1024 || profilePort > 65535 { + str := "%s: The profile port must be between 1024 and 65535" + err := fmt.Errorf(str, funcName) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + } + + // Don't allow ban durations that are too short. + if cfg.BanDuration < time.Duration(time.Second) { + str := "%s: The banduration option may not be less than 1s -- parsed [%v]" + err := fmt.Errorf(str, funcName, cfg.BanDuration) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // --addPeer and --connect do not mix. + if len(cfg.AddPeers) > 0 && len(cfg.ConnectPeers) > 0 { + str := "%s: the --addpeer and --connect options can not be " + + "mixed" + err := fmt.Errorf(str, funcName) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // --proxy or --connect without --listen disables listening. + if (cfg.Proxy != "" || len(cfg.ConnectPeers) > 0) && + len(cfg.Listeners) == 0 { + cfg.DisableListen = true + } + + // Connect means no DNS seeding. + if len(cfg.ConnectPeers) > 0 { + cfg.DisableDNSSeed = true + } + + // Add the default listener if none were specified. The default + // listener is all addresses on the listen port for the network + // we are to connect to. + if len(cfg.Listeners) == 0 { + cfg.Listeners = []string{ + net.JoinHostPort("", activeNetParams.DefaultPort), + } + } + + // Check to make sure limited and admin users don't have the same username + if cfg.RPCUser == cfg.RPCLimitUser && cfg.RPCUser != "" { + str := "%s: --rpcuser and --rpclimituser must not specify the " + + "same username" + err := fmt.Errorf(str, funcName) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // Check to make sure limited and admin users don't have the same password + if cfg.RPCPass == cfg.RPCLimitPass && cfg.RPCPass != "" { + str := "%s: --rpcpass and --rpclimitpass must not specify the " + + "same password" + err := fmt.Errorf(str, funcName) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // The RPC server is disabled if no username or password is provided. + if (cfg.RPCUser == "" || cfg.RPCPass == "") && + (cfg.RPCLimitUser == "" || cfg.RPCLimitPass == "") { + cfg.DisableRPC = true + } + + // Default RPC to listen on localhost only. + if !cfg.DisableRPC && len(cfg.RPCListeners) == 0 { + addrs, err := net.LookupHost("localhost") + if err != nil { + return nil, nil, err + } + cfg.RPCListeners = make([]string, 0, len(addrs)) + for _, addr := range addrs { + addr = net.JoinHostPort(addr, activeNetParams.rpcPort) + cfg.RPCListeners = append(cfg.RPCListeners, addr) + } + } + + // Validate the the minrelaytxfee. + cfg.minRelayTxFee, err = btcutil.NewAmount(cfg.MinRelayTxFee) + if err != nil { + str := "%s: invalid minrelaytxfee: %v" + err := fmt.Errorf(str, funcName, err) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // Limit the max block size to a sane value. + if cfg.BlockMaxSize < blockMaxSizeMin || cfg.BlockMaxSize > + blockMaxSizeMax { + + str := "%s: The blockmaxsize option must be in between %d " + + "and %d -- parsed [%d]" + err := fmt.Errorf(str, funcName, blockMaxSizeMin, + blockMaxSizeMax, cfg.BlockMaxSize) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // Limit the max orphan count to a sane vlue. + if cfg.MaxOrphanTxs < 0 { + str := "%s: The maxorphantx option may not be less than 0 " + + "-- parsed [%d]" + err := fmt.Errorf(str, funcName, cfg.MaxOrphanTxs) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // Limit the block priority and minimum block sizes to max block size. + cfg.BlockPrioritySize = minUint32(cfg.BlockPrioritySize, cfg.BlockMaxSize) + cfg.BlockMinSize = minUint32(cfg.BlockMinSize, cfg.BlockMaxSize) + + // --txindex and --droptxindex do not mix. + if cfg.TxIndex && cfg.DropTxIndex { + err := fmt.Errorf("%s: the --txindex and --droptxindex "+ + "options may not be activated at the same time", + funcName) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // --addrindex and --dropaddrindex do not mix. + if cfg.AddrIndex && cfg.DropAddrIndex { + err := fmt.Errorf("%s: the --addrindex and --dropaddrindex "+ + "options may not be activated at the same time", + funcName) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // --addrindex and --droptxindex do not mix. + if cfg.AddrIndex && cfg.DropTxIndex { + err := fmt.Errorf("%s: the --addrindex and --droptxindex "+ + "options may not be activated at the same time "+ + "because the address index relies on the transaction "+ + "index", + funcName) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // Check getwork keys are valid and saved parsed versions. + cfg.miningAddrs = make([]btcutil.Address, 0, len(cfg.GetWorkKeys)+ + len(cfg.MiningAddrs)) + for _, strAddr := range cfg.GetWorkKeys { + addr, err := btcutil.DecodeAddress(strAddr, + activeNetParams.Params) + if err != nil { + str := "%s: getworkkey '%s' failed to decode: %v" + err := fmt.Errorf(str, funcName, strAddr, err) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + if !addr.IsForNet(activeNetParams.Params) { + str := "%s: getworkkey '%s' is on the wrong network" + err := fmt.Errorf(str, funcName, strAddr) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + cfg.miningAddrs = append(cfg.miningAddrs, addr) + } + + // Check mining addresses are valid and saved parsed versions. + for _, strAddr := range cfg.MiningAddrs { + addr, err := btcutil.DecodeAddress(strAddr, activeNetParams.Params) + if err != nil { + str := "%s: mining address '%s' failed to decode: %v" + err := fmt.Errorf(str, funcName, strAddr, err) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + if !addr.IsForNet(activeNetParams.Params) { + str := "%s: mining address '%s' is on the wrong network" + err := fmt.Errorf(str, funcName, strAddr) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + cfg.miningAddrs = append(cfg.miningAddrs, addr) + } + + // Ensure there is at least one mining address when the generate flag is + // set. + if cfg.Generate && len(cfg.MiningAddrs) == 0 { + str := "%s: the generate flag is set, but there are no mining " + + "addresses specified " + err := fmt.Errorf(str, funcName) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // Add default port to all listener addresses if needed and remove + // duplicate addresses. + cfg.Listeners = normalizeAddresses(cfg.Listeners, + activeNetParams.DefaultPort) + + // Add default port to all rpc listener addresses if needed and remove + // duplicate addresses. + cfg.RPCListeners = normalizeAddresses(cfg.RPCListeners, + activeNetParams.rpcPort) + + // Only allow TLS to be disabled if the RPC is bound to localhost + // addresses. + if !cfg.DisableRPC && cfg.DisableTLS { + allowedTLSListeners := map[string]struct{}{ + "localhost": {}, + "127.0.0.1": {}, + "::1": {}, + } + for _, addr := range cfg.RPCListeners { + host, _, err := net.SplitHostPort(addr) + if err != nil { + str := "%s: RPC listen interface '%s' is " + + "invalid: %v" + err := fmt.Errorf(str, funcName, addr, err) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + if _, ok := allowedTLSListeners[host]; !ok { + str := "%s: the --notls option may not be used " + + "when binding RPC to non localhost " + + "addresses: %s" + err := fmt.Errorf(str, funcName, addr) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + } + } + + // Add default port to all added peer addresses if needed and remove + // duplicate addresses. + cfg.AddPeers = normalizeAddresses(cfg.AddPeers, + activeNetParams.DefaultPort) + cfg.ConnectPeers = normalizeAddresses(cfg.ConnectPeers, + activeNetParams.DefaultPort) + + // Tor stream isolation requires either proxy or onion proxy to be set. + if cfg.TorIsolation && cfg.Proxy == "" && cfg.OnionProxy == "" { + str := "%s: Tor stream isolation requires either proxy or " + + "onionproxy to be set" + err := fmt.Errorf(str, funcName) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + // Setup dial and DNS resolution (lookup) functions depending on the + // specified options. The default is to use the standard net.Dial + // function as well as the system DNS resolver. When a proxy is + // specified, the dial function is set to the proxy specific dial + // function and the lookup is set to use tor (unless --noonion is + // specified in which case the system DNS resolver is used). + cfg.dial = net.Dial + cfg.lookup = net.LookupIP + if cfg.Proxy != "" { + _, _, err := net.SplitHostPort(cfg.Proxy) + if err != nil { + str := "%s: Proxy address '%s' is invalid: %v" + err := fmt.Errorf(str, funcName, cfg.Proxy, err) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + if cfg.TorIsolation && + (cfg.ProxyUser != "" || cfg.ProxyPass != "") { + btcdLog.Warn("Tor isolation set -- overriding " + + "specified proxy user credentials") + } + + proxy := &socks.Proxy{ + Addr: cfg.Proxy, + Username: cfg.ProxyUser, + Password: cfg.ProxyPass, + TorIsolation: cfg.TorIsolation, + } + cfg.dial = proxy.Dial + if !cfg.NoOnion { + cfg.lookup = func(host string) ([]net.IP, error) { + return torLookupIP(host, cfg.Proxy) + } + } + } + + // Setup onion address dial and DNS resolution (lookup) functions + // depending on the specified options. The default is to use the + // same dial and lookup functions selected above. However, when an + // onion-specific proxy is specified, the onion address dial and + // lookup functions are set to use the onion-specific proxy while + // leaving the normal dial and lookup functions as selected above. + // This allows .onion address traffic to be routed through a different + // proxy than normal traffic. + if cfg.OnionProxy != "" { + _, _, err := net.SplitHostPort(cfg.OnionProxy) + if err != nil { + str := "%s: Onion proxy address '%s' is invalid: %v" + err := fmt.Errorf(str, funcName, cfg.OnionProxy, err) + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, usageMessage) + return nil, nil, err + } + + if cfg.TorIsolation && + (cfg.OnionProxyUser != "" || cfg.OnionProxyPass != "") { + btcdLog.Warn("Tor isolation set -- overriding " + + "specified onionproxy user credentials ") + } + + cfg.oniondial = func(a, b string) (net.Conn, error) { + proxy := &socks.Proxy{ + Addr: cfg.OnionProxy, + Username: cfg.OnionProxyUser, + Password: cfg.OnionProxyPass, + TorIsolation: cfg.TorIsolation, + } + return proxy.Dial(a, b) + } + cfg.onionlookup = func(host string) ([]net.IP, error) { + return torLookupIP(host, cfg.OnionProxy) + } + } else { + cfg.oniondial = cfg.dial + cfg.onionlookup = cfg.lookup + } + + // Specifying --noonion means the onion address dial and DNS resolution + // (lookup) functions result in an error. + if cfg.NoOnion { + cfg.oniondial = func(a, b string) (net.Conn, error) { + return nil, errors.New("tor has been disabled") + } + cfg.onionlookup = func(a string) ([]net.IP, error) { + return nil, errors.New("tor has been disabled") + } + } + + // Warn about missing config file only after all other configuration is + // done. This prevents the warning on help messages and invalid + // options. Note this should go directly before the return. + if configFileError != nil { + btcdLog.Warnf("%v", configFileError) + } + + return &cfg, remainingArgs, nil +} + +// btcdDial connects to the address on the named network using the appropriate +// dial function depending on the address and configuration options. For +// example, .onion addresses will be dialed using the onion specific proxy if +// one was specified, but will otherwise use the normal dial function (which +// could itself use a proxy or not). +func btcdDial(network, address string) (net.Conn, error) { + if strings.Contains(address, ".onion:") { + return cfg.oniondial(network, address) + } + return cfg.dial(network, address) +} + +// btcdLookup returns the correct DNS lookup function to use depending on the +// passed host and configuration options. For example, .onion addresses will be +// resolved using the onion specific proxy if one was specified, but will +// otherwise treat the normal proxy as tor unless --noonion was specified in +// which case the lookup will fail. Meanwhile, normal IP addresses will be +// resolved using tor if a proxy was specified unless --noonion was also +// specified in which case the normal system DNS resolver will be used. +func btcdLookup(host string) ([]net.IP, error) { + if strings.HasSuffix(host, ".onion") { + return cfg.onionlookup(host) + } + return cfg.lookup(host) +} diff --git a/vendor/github.com/btcsuite/btcd/cpuminer.go b/vendor/github.com/btcsuite/btcd/cpuminer.go new file mode 100644 index 0000000000000000000000000000000000000000..587288d3c8f80d35bc7e5a02b2143f15a0311771 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/cpuminer.go @@ -0,0 +1,615 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "errors" + "fmt" + "math/rand" + "runtime" + "sync" + "time" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/mining" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +const ( + // maxNonce is the maximum value a nonce can be in a block header. + maxNonce = ^uint32(0) // 2^32 - 1 + + // maxExtraNonce is the maximum value an extra nonce used in a coinbase + // transaction can be. + maxExtraNonce = ^uint64(0) // 2^64 - 1 + + // hpsUpdateSecs is the number of seconds to wait in between each + // update to the hashes per second monitor. + hpsUpdateSecs = 10 + + // hashUpdateSec is the number of seconds each worker waits in between + // notifying the speed monitor with how many hashes have been completed + // while they are actively searching for a solution. This is done to + // reduce the amount of syncs between the workers that must be done to + // keep track of the hashes per second. + hashUpdateSecs = 15 +) + +var ( + // defaultNumWorkers is the default number of workers to use for mining + // and is based on the number of processor cores. This helps ensure the + // system stays reasonably responsive under heavy load. + defaultNumWorkers = uint32(runtime.NumCPU()) +) + +// CPUMiner provides facilities for solving blocks (mining) using the CPU in +// a concurrency-safe manner. It consists of two main goroutines -- a speed +// monitor and a controller for worker goroutines which generate and solve +// blocks. The number of goroutines can be set via the SetMaxGoRoutines +// function, but the default is based on the number of processor cores in the +// system which is typically sufficient. +type CPUMiner struct { + sync.Mutex + policy *mining.Policy + txSource mining.TxSource + server *server + numWorkers uint32 + started bool + discreteMining bool + submitBlockLock sync.Mutex + wg sync.WaitGroup + workerWg sync.WaitGroup + updateNumWorkers chan struct{} + queryHashesPerSec chan float64 + updateHashes chan uint64 + speedMonitorQuit chan struct{} + quit chan struct{} +} + +// speedMonitor handles tracking the number of hashes per second the mining +// process is performing. It must be run as a goroutine. +func (m *CPUMiner) speedMonitor() { + minrLog.Tracef("CPU miner speed monitor started") + + var hashesPerSec float64 + var totalHashes uint64 + ticker := time.NewTicker(time.Second * hpsUpdateSecs) + defer ticker.Stop() + +out: + for { + select { + // Periodic updates from the workers with how many hashes they + // have performed. + case numHashes := <-m.updateHashes: + totalHashes += numHashes + + // Time to update the hashes per second. + case <-ticker.C: + curHashesPerSec := float64(totalHashes) / hpsUpdateSecs + if hashesPerSec == 0 { + hashesPerSec = curHashesPerSec + } + hashesPerSec = (hashesPerSec + curHashesPerSec) / 2 + totalHashes = 0 + if hashesPerSec != 0 { + minrLog.Debugf("Hash speed: %6.0f kilohashes/s", + hashesPerSec/1000) + } + + // Request for the number of hashes per second. + case m.queryHashesPerSec <- hashesPerSec: + // Nothing to do. + + case <-m.speedMonitorQuit: + break out + } + } + + m.wg.Done() + minrLog.Tracef("CPU miner speed monitor done") +} + +// submitBlock submits the passed block to network after ensuring it passes all +// of the consensus validation rules. +func (m *CPUMiner) submitBlock(block *btcutil.Block) bool { + m.submitBlockLock.Lock() + defer m.submitBlockLock.Unlock() + + // Ensure the block is not stale since a new block could have shown up + // while the solution was being found. Typically that condition is + // detected and all work on the stale block is halted to start work on + // a new block, but the check only happens periodically, so it is + // possible a block was found and submitted in between. + latestHash, _ := m.server.blockManager.chainState.Best() + msgBlock := block.MsgBlock() + if !msgBlock.Header.PrevBlock.IsEqual(latestHash) { + minrLog.Debugf("Block submitted via CPU miner with previous "+ + "block %s is stale", msgBlock.Header.PrevBlock) + return false + } + + // Process this block using the same rules as blocks coming from other + // nodes. This will in turn relay it to the network like normal. + isOrphan, err := m.server.blockManager.ProcessBlock(block, blockchain.BFNone) + if err != nil { + // Anything other than a rule violation is an unexpected error, + // so log that error as an internal error. + if _, ok := err.(blockchain.RuleError); !ok { + minrLog.Errorf("Unexpected error while processing "+ + "block submitted via CPU miner: %v", err) + return false + } + + minrLog.Debugf("Block submitted via CPU miner rejected: %v", err) + return false + } + if isOrphan { + minrLog.Debugf("Block submitted via CPU miner is an orphan") + return false + } + + // The block was accepted. + coinbaseTx := block.MsgBlock().Transactions[0].TxOut[0] + minrLog.Infof("Block submitted via CPU miner accepted (hash %s, "+ + "amount %v)", block.Sha(), btcutil.Amount(coinbaseTx.Value)) + return true +} + +// solveBlock attempts to find some combination of a nonce, extra nonce, and +// current timestamp which makes the passed block hash to a value less than the +// target difficulty. The timestamp is updated periodically and the passed +// block is modified with all tweaks during this process. This means that +// when the function returns true, the block is ready for submission. +// +// This function will return early with false when conditions that trigger a +// stale block such as a new block showing up or periodically when there are +// new transactions and enough time has elapsed without finding a solution. +func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32, + ticker *time.Ticker, quit chan struct{}) bool { + + // Choose a random extra nonce offset for this block template and + // worker. + enOffset, err := wire.RandomUint64() + if err != nil { + minrLog.Errorf("Unexpected error while generating random "+ + "extra nonce offset: %v", err) + enOffset = 0 + } + + // Create a couple of convenience variables. + header := &msgBlock.Header + targetDifficulty := blockchain.CompactToBig(header.Bits) + + // Initial state. + lastGenerated := time.Now() + lastTxUpdate := m.txSource.LastUpdated() + hashesCompleted := uint64(0) + + // Note that the entire extra nonce range is iterated and the offset is + // added relying on the fact that overflow will wrap around 0 as + // provided by the Go spec. + for extraNonce := uint64(0); extraNonce < maxExtraNonce; extraNonce++ { + // Update the extra nonce in the block template with the + // new value by regenerating the coinbase script and + // setting the merkle root to the new value. The + UpdateExtraNonce(msgBlock, blockHeight, extraNonce+enOffset) + + // Search through the entire nonce range for a solution while + // periodically checking for early quit and stale block + // conditions along with updates to the speed monitor. + for i := uint32(0); i <= maxNonce; i++ { + select { + case <-quit: + return false + + case <-ticker.C: + m.updateHashes <- hashesCompleted + hashesCompleted = 0 + + // The current block is stale if the best block + // has changed. + bestHash, _ := m.server.blockManager.chainState.Best() + if !header.PrevBlock.IsEqual(bestHash) { + return false + } + + // The current block is stale if the memory pool + // has been updated since the block template was + // generated and it has been at least one + // minute. + if lastTxUpdate != m.txSource.LastUpdated() && + time.Now().After(lastGenerated.Add(time.Minute)) { + + return false + } + + UpdateBlockTime(msgBlock, m.server.blockManager) + + default: + // Non-blocking select to fall through + } + + // Update the nonce and hash the block header. Each + // hash is actually a double sha256 (two hashes), so + // increment the number of hashes completed for each + // attempt accordingly. + header.Nonce = i + hash := header.BlockSha() + hashesCompleted += 2 + + // The block is solved when the new block hash is less + // than the target difficulty. Yay! + if blockchain.ShaHashToBig(&hash).Cmp(targetDifficulty) <= 0 { + m.updateHashes <- hashesCompleted + return true + } + } + } + + return false +} + +// generateBlocks is a worker that is controlled by the miningWorkerController. +// It is self contained in that it creates block templates and attempts to solve +// them while detecting when it is performing stale work and reacting +// accordingly by generating a new block template. When a block is solved, it +// is submitted. +// +// It must be run as a goroutine. +func (m *CPUMiner) generateBlocks(quit chan struct{}) { + minrLog.Tracef("Starting generate blocks worker") + + // Start a ticker which is used to signal checks for stale work and + // updates to the speed monitor. + ticker := time.NewTicker(time.Second * hashUpdateSecs) + defer ticker.Stop() +out: + for { + // Quit when the miner is stopped. + select { + case <-quit: + break out + default: + // Non-blocking select to fall through + } + + // Wait until there is a connection to at least one other peer + // since there is no way to relay a found block or receive + // transactions to work on when there are no connected peers. + if m.server.ConnectedCount() == 0 { + time.Sleep(time.Second) + continue + } + + // No point in searching for a solution before the chain is + // synced. Also, grab the same lock as used for block + // submission, since the current block will be changing and + // this would otherwise end up building a new block template on + // a block that is in the process of becoming stale. + m.submitBlockLock.Lock() + _, curHeight := m.server.blockManager.chainState.Best() + if curHeight != 0 && !m.server.blockManager.IsCurrent() { + m.submitBlockLock.Unlock() + time.Sleep(time.Second) + continue + } + + // Choose a payment address at random. + rand.Seed(time.Now().UnixNano()) + payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] + + // Create a new block template using the available transactions + // in the memory pool as a source of transactions to potentially + // include in the block. + template, err := NewBlockTemplate(m.policy, m.server, payToAddr) + m.submitBlockLock.Unlock() + if err != nil { + errStr := fmt.Sprintf("Failed to create new block "+ + "template: %v", err) + minrLog.Errorf(errStr) + continue + } + + // Attempt to solve the block. The function will exit early + // with false when conditions that trigger a stale block, so + // a new block template can be generated. When the return is + // true a solution was found, so submit the solved block. + if m.solveBlock(template.Block, curHeight+1, ticker, quit) { + block := btcutil.NewBlock(template.Block) + m.submitBlock(block) + } + } + + m.workerWg.Done() + minrLog.Tracef("Generate blocks worker done") +} + +// miningWorkerController launches the worker goroutines that are used to +// generate block templates and solve them. It also provides the ability to +// dynamically adjust the number of running worker goroutines. +// +// It must be run as a goroutine. +func (m *CPUMiner) miningWorkerController() { + // launchWorkers groups common code to launch a specified number of + // workers for generating blocks. + var runningWorkers []chan struct{} + launchWorkers := func(numWorkers uint32) { + for i := uint32(0); i < numWorkers; i++ { + quit := make(chan struct{}) + runningWorkers = append(runningWorkers, quit) + + m.workerWg.Add(1) + go m.generateBlocks(quit) + } + } + + // Launch the current number of workers by default. + runningWorkers = make([]chan struct{}, 0, m.numWorkers) + launchWorkers(m.numWorkers) + +out: + for { + select { + // Update the number of running workers. + case <-m.updateNumWorkers: + // No change. + numRunning := uint32(len(runningWorkers)) + if m.numWorkers == numRunning { + continue + } + + // Add new workers. + if m.numWorkers > numRunning { + launchWorkers(m.numWorkers - numRunning) + continue + } + + // Signal the most recently created goroutines to exit. + for i := numRunning - 1; i >= m.numWorkers; i-- { + close(runningWorkers[i]) + runningWorkers[i] = nil + runningWorkers = runningWorkers[:i] + } + + case <-m.quit: + for _, quit := range runningWorkers { + close(quit) + } + break out + } + } + + // Wait until all workers shut down to stop the speed monitor since + // they rely on being able to send updates to it. + m.workerWg.Wait() + close(m.speedMonitorQuit) + m.wg.Done() +} + +// Start begins the CPU mining process as well as the speed monitor used to +// track hashing metrics. Calling this function when the CPU miner has +// already been started will have no effect. +// +// This function is safe for concurrent access. +func (m *CPUMiner) Start() { + m.Lock() + defer m.Unlock() + + // Nothing to do if the miner is already running or if running in discrete + // mode (using GenerateNBlocks). + if m.started || m.discreteMining { + return + } + + m.quit = make(chan struct{}) + m.speedMonitorQuit = make(chan struct{}) + m.wg.Add(2) + go m.speedMonitor() + go m.miningWorkerController() + + m.started = true + minrLog.Infof("CPU miner started") +} + +// Stop gracefully stops the mining process by signalling all workers, and the +// speed monitor to quit. Calling this function when the CPU miner has not +// already been started will have no effect. +// +// This function is safe for concurrent access. +func (m *CPUMiner) Stop() { + m.Lock() + defer m.Unlock() + + // Nothing to do if the miner is not currently running or if running in + // discrete mode (using GenerateNBlocks). + if !m.started || m.discreteMining { + return + } + + close(m.quit) + m.wg.Wait() + m.started = false + minrLog.Infof("CPU miner stopped") +} + +// IsMining returns whether or not the CPU miner has been started and is +// therefore currenting mining. +// +// This function is safe for concurrent access. +func (m *CPUMiner) IsMining() bool { + m.Lock() + defer m.Unlock() + + return m.started +} + +// HashesPerSecond returns the number of hashes per second the mining process +// is performing. 0 is returned if the miner is not currently running. +// +// This function is safe for concurrent access. +func (m *CPUMiner) HashesPerSecond() float64 { + m.Lock() + defer m.Unlock() + + // Nothing to do if the miner is not currently running. + if !m.started { + return 0 + } + + return <-m.queryHashesPerSec +} + +// SetNumWorkers sets the number of workers to create which solve blocks. Any +// negative values will cause a default number of workers to be used which is +// based on the number of processor cores in the system. A value of 0 will +// cause all CPU mining to be stopped. +// +// This function is safe for concurrent access. +func (m *CPUMiner) SetNumWorkers(numWorkers int32) { + if numWorkers == 0 { + m.Stop() + } + + // Don't lock until after the first check since Stop does its own + // locking. + m.Lock() + defer m.Unlock() + + // Use default if provided value is negative. + if numWorkers < 0 { + m.numWorkers = defaultNumWorkers + } else { + m.numWorkers = uint32(numWorkers) + } + + // When the miner is already running, notify the controller about the + // the change. + if m.started { + m.updateNumWorkers <- struct{}{} + } +} + +// NumWorkers returns the number of workers which are running to solve blocks. +// +// This function is safe for concurrent access. +func (m *CPUMiner) NumWorkers() int32 { + m.Lock() + defer m.Unlock() + + return int32(m.numWorkers) +} + +// GenerateNBlocks generates the requested number of blocks. It is self +// contained in that it creates block templates and attempts to solve them while +// detecting when it is performing stale work and reacting accordingly by +// generating a new block template. When a block is solved, it is submitted. +// The function returns a list of the hashes of generated blocks. +func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*wire.ShaHash, error) { + m.Lock() + + // Respond with an error if there's virtually 0 chance of CPU-mining a block. + if !m.server.chainParams.GenerateSupported { + m.Unlock() + return nil, errors.New("No support for `generate` on the current " + + "network, " + m.server.chainParams.Net.String() + + ", as it's unlikely to be possible to CPU-mine a block.") + } + + // Respond with an error if server is already mining. + if m.started || m.discreteMining { + m.Unlock() + return nil, errors.New("Server is already CPU mining. Please call " + + "`setgenerate 0` before calling discrete `generate` commands.") + } + + m.started = true + m.discreteMining = true + + m.speedMonitorQuit = make(chan struct{}) + m.wg.Add(1) + go m.speedMonitor() + + m.Unlock() + + minrLog.Tracef("Generating %d blocks", n) + + i := uint32(0) + blockHashes := make([]*wire.ShaHash, n, n) + + // Start a ticker which is used to signal checks for stale work and + // updates to the speed monitor. + ticker := time.NewTicker(time.Second * hashUpdateSecs) + defer ticker.Stop() + + for { + // Read updateNumWorkers in case someone tries a `setgenerate` while + // we're generating. We can ignore it as the `generate` RPC call only + // uses 1 worker. + select { + case <-m.updateNumWorkers: + default: + } + + // Grab the lock used for block submission, since the current block will + // be changing and this would otherwise end up building a new block + // template on a block that is in the process of becoming stale. + m.submitBlockLock.Lock() + _, curHeight := m.server.blockManager.chainState.Best() + + // Choose a payment address at random. + rand.Seed(time.Now().UnixNano()) + payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] + + // Create a new block template using the available transactions + // in the memory pool as a source of transactions to potentially + // include in the block. + template, err := NewBlockTemplate(m.policy, m.server, payToAddr) + m.submitBlockLock.Unlock() + if err != nil { + errStr := fmt.Sprintf("Failed to create new block "+ + "template: %v", err) + minrLog.Errorf(errStr) + continue + } + + // Attempt to solve the block. The function will exit early + // with false when conditions that trigger a stale block, so + // a new block template can be generated. When the return is + // true a solution was found, so submit the solved block. + if m.solveBlock(template.Block, curHeight+1, ticker, nil) { + block := btcutil.NewBlock(template.Block) + m.submitBlock(block) + blockHashes[i] = block.Sha() + i++ + if i == n { + minrLog.Tracef("Generated %d blocks", i) + m.Lock() + close(m.speedMonitorQuit) + m.wg.Wait() + m.started = false + m.discreteMining = false + m.Unlock() + return blockHashes, nil + } + } + } +} + +// newCPUMiner returns a new instance of a CPU miner for the provided server. +// Use Start to begin the mining process. See the documentation for CPUMiner +// type for more details. +func newCPUMiner(policy *mining.Policy, s *server) *CPUMiner { + return &CPUMiner{ + policy: policy, + txSource: s.txMemPool, + server: s, + numWorkers: defaultNumWorkers, + updateNumWorkers: make(chan struct{}), + queryHashesPerSec: make(chan float64), + updateHashes: make(chan uint64), + } +} diff --git a/vendor/github.com/btcsuite/btcd/database/README.md b/vendor/github.com/btcsuite/btcd/database/README.md new file mode 100644 index 0000000000000000000000000000000000000000..bcf5a1d569d257901f028e74f84786665ae99847 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/README.md @@ -0,0 +1,62 @@ +database +======== + +[] +(https://travis-ci.org/btcsuite/btcd) [![ISC License] +(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[] +(http://godoc.org/github.com/btcsuite/btcd/database) + +Package database provides a block and metadata storage database. + +Please note that this package is intended to enable btcd to support different +database backends and is not something that a client can directly access as only +one entity can have the database open at a time (for most database backends), +and that entity will be btcd. + +When a client wants programmatic access to the data provided by btcd, they'll +likely want to use the [btcrpcclient](https://github.com/btcsuite/btcrpcclient) +package which makes use of the [JSON-RPC API] +(https://github.com/btcsuite/btcd/tree/master/docs/json_rpc_api.md). + +However, this package could be extremely useful for any applications requiring +Bitcoin block storage capabilities. + +The default backend, ffldb, has a strong focus on speed, efficiency, and +robustness. It makes use of leveldb for the metadata, flat files for block +storage, and strict checksums in key areas to ensure data integrity. + +## Feature Overview + +- Key/value metadata store +- Bitcoin block storage +- Efficient retrieval of block headers and regions (transactions, scripts, etc) +- Read-only and read-write transactions with both manual and managed modes +- Nested buckets +- Iteration support including cursors with seek capability +- Supports registration of backend databases +- Comprehensive test coverage + +## Installation and Updating + +```bash +$ go get -u github.com/btcsuite/btcd/database +``` + +## Examples + +* [Basic Usage Example] + (http://godoc.org/github.com/btcsuite/btcd/database#example-package--BasicUsage) + Demonstrates creating a new database and using a managed read-write + transaction to store and retrieve metadata. + +* [Block Storage and Retrieval Example] + (http://godoc.org/github.com/btcsuite/btcd/database#example-package--BlockStorageAndRetrieval) + Demonstrates creating a new database, using a managed read-write transaction + to store a block, and then using a managed read-only transaction to fetch the + block. + +## License + +Package database is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/fetchblock.go b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/fetchblock.go new file mode 100644 index 0000000000000000000000000000000000000000..5bb7b6a916f212297690580eca0af00a73eb38a0 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/fetchblock.go @@ -0,0 +1,62 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/hex" + "errors" + "time" + + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" +) + +// fetchBlockCmd defines the configuration options for the fetchblock command. +type fetchBlockCmd struct{} + +var ( + // fetchBlockCfg defines the configuration options for the command. + fetchBlockCfg = fetchBlockCmd{} +) + +// Execute is the main entry point for the command. It's invoked by the parser. +func (cmd *fetchBlockCmd) Execute(args []string) error { + // Setup the global config options and ensure they are valid. + if err := setupGlobalConfig(); err != nil { + return err + } + + if len(args) < 1 { + return errors.New("required block hash parameter not specified") + } + blockHash, err := wire.NewShaHashFromStr(args[0]) + if err != nil { + return err + } + + // Load the block database. + db, err := loadBlockDB() + if err != nil { + return err + } + defer db.Close() + + return db.View(func(tx database.Tx) error { + log.Infof("Fetching block %s", blockHash) + startTime := time.Now() + blockBytes, err := tx.FetchBlock(blockHash) + if err != nil { + return err + } + log.Infof("Loaded block in %v", time.Now().Sub(startTime)) + log.Infof("Block Hex: %s", hex.EncodeToString(blockBytes)) + return nil + }) +} + +// Usage overrides the usage display for the command. +func (cmd *fetchBlockCmd) Usage() string { + return "<block-hash>" +} diff --git a/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/fetchblockregion.go b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/fetchblockregion.go new file mode 100644 index 0000000000000000000000000000000000000000..070d26d29f835b4c669378f50ed7ce064b1c482e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/fetchblockregion.go @@ -0,0 +1,90 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/hex" + "errors" + "strconv" + "time" + + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" +) + +// blockRegionCmd defines the configuration options for the fetchblockregion +// command. +type blockRegionCmd struct{} + +var ( + // blockRegionCfg defines the configuration options for the command. + blockRegionCfg = blockRegionCmd{} +) + +// Execute is the main entry point for the command. It's invoked by the parser. +func (cmd *blockRegionCmd) Execute(args []string) error { + // Setup the global config options and ensure they are valid. + if err := setupGlobalConfig(); err != nil { + return err + } + + // Ensure expected arguments. + if len(args) < 1 { + return errors.New("required block hash parameter not specified") + } + if len(args) < 2 { + return errors.New("required start offset parameter not " + + "specified") + } + if len(args) < 3 { + return errors.New("required region length parameter not " + + "specified") + } + + // Parse arguments. + blockHash, err := wire.NewShaHashFromStr(args[0]) + if err != nil { + return err + } + startOffset, err := strconv.ParseUint(args[1], 10, 32) + if err != nil { + return err + } + regionLen, err := strconv.ParseUint(args[2], 10, 32) + if err != nil { + return err + } + + // Load the block database. + db, err := loadBlockDB() + if err != nil { + return err + } + defer db.Close() + + return db.View(func(tx database.Tx) error { + log.Infof("Fetching block region %s<%d:%d>", blockHash, + startOffset, startOffset+regionLen-1) + region := database.BlockRegion{ + Hash: blockHash, + Offset: uint32(startOffset), + Len: uint32(regionLen), + } + startTime := time.Now() + regionBytes, err := tx.FetchBlockRegion(®ion) + if err != nil { + return err + } + log.Infof("Loaded block region in %v", time.Now().Sub(startTime)) + log.Infof("Double SHA256: %s", wire.DoubleSha256SH(regionBytes)) + log.Infof("Region Hex: %s", hex.EncodeToString(regionBytes)) + return nil + }) +} + +// Usage overrides the usage display for the command. +func (cmd *blockRegionCmd) Usage() string { + return "<block-hash> <start-offset> <length-of-region>" +} diff --git a/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/globalconfig.go b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/globalconfig.go new file mode 100644 index 0000000000000000000000000000000000000000..5cbd9b3af9da803e1d02760fe536101bc5ae261b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/globalconfig.go @@ -0,0 +1,121 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + _ "github.com/btcsuite/btcd/database/ffldb" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +var ( + btcdHomeDir = btcutil.AppDataDir("btcd", false) + knownDbTypes = database.SupportedDrivers() + activeNetParams = &chaincfg.MainNetParams + + // Default global config. + cfg = &config{ + DataDir: filepath.Join(btcdHomeDir, "data"), + DbType: "ffldb", + } +) + +// config defines the global configuration options. +type config struct { + DataDir string `short:"b" long:"datadir" description:"Location of the btcd data directory"` + DbType string `long:"dbtype" description:"Database backend to use for the Block Chain"` + TestNet3 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"` +} + +// fileExists reports whether the named file or directory exists. +func fileExists(name string) bool { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +} + +// validDbType returns whether or not dbType is a supported database type. +func validDbType(dbType string) bool { + for _, knownType := range knownDbTypes { + if dbType == knownType { + return true + } + } + + return false +} + +// netName returns the name used when referring to a bitcoin network. At the +// time of writing, btcd currently places blocks for testnet version 3 in the +// data and log directory "testnet", which does not match the Name field of the +// chaincfg parameters. This function can be used to override this directory name +// as "testnet" when the passed active network matches wire.TestNet3. +// +// A proper upgrade to move the data and log directories for this network to +// "testnet3" is planned for the future, at which point this function can be +// removed and the network parameter's name used instead. +func netName(chainParams *chaincfg.Params) string { + switch chainParams.Net { + case wire.TestNet3: + return "testnet" + default: + return chainParams.Name + } +} + +// setupGlobalConfig examine the global configuration options for any conditions +// which are invalid as well as performs any addition setup necessary after the +// initial parse. +func setupGlobalConfig() error { + // Multiple networks can't be selected simultaneously. + // Count number of network flags passed; assign active network params + // while we're at it + numNets := 0 + if cfg.TestNet3 { + numNets++ + activeNetParams = &chaincfg.TestNet3Params + } + if cfg.RegressionTest { + numNets++ + activeNetParams = &chaincfg.RegressionNetParams + } + if cfg.SimNet { + numNets++ + activeNetParams = &chaincfg.SimNetParams + } + if numNets > 1 { + return errors.New("The testnet, regtest, and simnet params " + + "can't be used together -- choose one of the three") + } + + // Validate database type. + if !validDbType(cfg.DbType) { + str := "The specified database type [%v] is invalid -- " + + "supported types %v" + return fmt.Errorf(str, cfg.DbType, knownDbTypes) + } + + // Append the network type to the data directory so it is "namespaced" + // per network. In addition to the block database, there are other + // pieces of data that are saved to disk such as address manager state. + // All data is specific to a network, so namespacing the data directory + // means each individual piece of serialized data does not have to + // worry about changing names per network and such. + cfg.DataDir = filepath.Join(cfg.DataDir, netName(activeNetParams)) + + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/insecureimport.go b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/insecureimport.go new file mode 100644 index 0000000000000000000000000000000000000000..86a051175eff9bbe76f776a8b18ce1aa0c11b8fb --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/insecureimport.go @@ -0,0 +1,401 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/binary" + "fmt" + "io" + "os" + "sync" + "time" + + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// importCmd defines the configuration options for the insecureimport command. +type importCmd struct { + InFile string `short:"i" long:"infile" description:"File containing the block(s)"` + Progress int `short:"p" long:"progress" description:"Show a progress message each time this number of seconds have passed -- Use 0 to disable progress announcements"` +} + +var ( + // importCfg defines the configuration options for the command. + importCfg = importCmd{ + InFile: "bootstrap.dat", + Progress: 10, + } + + // zeroHash is a simply a hash with all zeros. It is defined here to + // avoid creating it multiple times. + zeroHash = wire.ShaHash{} +) + +// importResults houses the stats and result as an import operation. +type importResults struct { + blocksProcessed int64 + blocksImported int64 + err error +} + +// blockImporter houses information about an ongoing import from a block data +// file to the block database. +type blockImporter struct { + db database.DB + r io.ReadSeeker + processQueue chan []byte + doneChan chan bool + errChan chan error + quit chan struct{} + wg sync.WaitGroup + blocksProcessed int64 + blocksImported int64 + receivedLogBlocks int64 + receivedLogTx int64 + lastHeight int64 + lastBlockTime time.Time + lastLogTime time.Time +} + +// readBlock reads the next block from the input file. +func (bi *blockImporter) readBlock() ([]byte, error) { + // The block file format is: + // <network> <block length> <serialized block> + var net uint32 + err := binary.Read(bi.r, binary.LittleEndian, &net) + if err != nil { + if err != io.EOF { + return nil, err + } + + // No block and no error means there are no more blocks to read. + return nil, nil + } + if net != uint32(activeNetParams.Net) { + return nil, fmt.Errorf("network mismatch -- got %x, want %x", + net, uint32(activeNetParams.Net)) + } + + // Read the block length and ensure it is sane. + var blockLen uint32 + if err := binary.Read(bi.r, binary.LittleEndian, &blockLen); err != nil { + return nil, err + } + if blockLen > wire.MaxBlockPayload { + return nil, fmt.Errorf("block payload of %d bytes is larger "+ + "than the max allowed %d bytes", blockLen, + wire.MaxBlockPayload) + } + + serializedBlock := make([]byte, blockLen) + if _, err := io.ReadFull(bi.r, serializedBlock); err != nil { + return nil, err + } + + return serializedBlock, nil +} + +// processBlock potentially imports the block into the database. It first +// deserializes the raw block while checking for errors. Already known blocks +// are skipped and orphan blocks are considered errors. Returns whether the +// block was imported along with any potential errors. +// +// NOTE: This is not a safe import as it does not verify chain rules. +func (bi *blockImporter) processBlock(serializedBlock []byte) (bool, error) { + // Deserialize the block which includes checks for malformed blocks. + block, err := btcutil.NewBlockFromBytes(serializedBlock) + if err != nil { + return false, err + } + + // update progress statistics + bi.lastBlockTime = block.MsgBlock().Header.Timestamp + bi.receivedLogTx += int64(len(block.MsgBlock().Transactions)) + + // Skip blocks that already exist. + var exists bool + err = bi.db.View(func(tx database.Tx) error { + exists, err = tx.HasBlock(block.Sha()) + if err != nil { + return err + } + return nil + }) + if err != nil { + return false, err + } + if exists { + return false, nil + } + + // Don't bother trying to process orphans. + prevHash := &block.MsgBlock().Header.PrevBlock + if !prevHash.IsEqual(&zeroHash) { + var exists bool + err := bi.db.View(func(tx database.Tx) error { + exists, err = tx.HasBlock(prevHash) + if err != nil { + return err + } + return nil + }) + if err != nil { + return false, err + } + if !exists { + return false, fmt.Errorf("import file contains block "+ + "%v which does not link to the available "+ + "block chain", prevHash) + } + } + + // Put the blocks into the database with no checking of chain rules. + err = bi.db.Update(func(tx database.Tx) error { + return tx.StoreBlock(block) + }) + if err != nil { + return false, err + } + + return true, nil +} + +// readHandler is the main handler for reading blocks from the import file. +// This allows block processing to take place in parallel with block reads. +// It must be run as a goroutine. +func (bi *blockImporter) readHandler() { +out: + for { + // Read the next block from the file and if anything goes wrong + // notify the status handler with the error and bail. + serializedBlock, err := bi.readBlock() + if err != nil { + bi.errChan <- fmt.Errorf("Error reading from input "+ + "file: %v", err.Error()) + break out + } + + // A nil block with no error means we're done. + if serializedBlock == nil { + break out + } + + // Send the block or quit if we've been signalled to exit by + // the status handler due to an error elsewhere. + select { + case bi.processQueue <- serializedBlock: + case <-bi.quit: + break out + } + } + + // Close the processing channel to signal no more blocks are coming. + close(bi.processQueue) + bi.wg.Done() +} + +// logProgress logs block progress as an information message. In order to +// prevent spam, it limits logging to one message every importCfg.Progress +// seconds with duration and totals included. +func (bi *blockImporter) logProgress() { + bi.receivedLogBlocks++ + + now := time.Now() + duration := now.Sub(bi.lastLogTime) + if duration < time.Second*time.Duration(importCfg.Progress) { + return + } + + // Truncate the duration to 10s of milliseconds. + durationMillis := int64(duration / time.Millisecond) + tDuration := 10 * time.Millisecond * time.Duration(durationMillis/10) + + // Log information about new block height. + blockStr := "blocks" + if bi.receivedLogBlocks == 1 { + blockStr = "block" + } + txStr := "transactions" + if bi.receivedLogTx == 1 { + txStr = "transaction" + } + log.Infof("Processed %d %s in the last %s (%d %s, height %d, %s)", + bi.receivedLogBlocks, blockStr, tDuration, bi.receivedLogTx, + txStr, bi.lastHeight, bi.lastBlockTime) + + bi.receivedLogBlocks = 0 + bi.receivedLogTx = 0 + bi.lastLogTime = now +} + +// processHandler is the main handler for processing blocks. This allows block +// processing to take place in parallel with block reads from the import file. +// It must be run as a goroutine. +func (bi *blockImporter) processHandler() { +out: + for { + select { + case serializedBlock, ok := <-bi.processQueue: + // We're done when the channel is closed. + if !ok { + break out + } + + bi.blocksProcessed++ + bi.lastHeight++ + imported, err := bi.processBlock(serializedBlock) + if err != nil { + bi.errChan <- err + break out + } + + if imported { + bi.blocksImported++ + } + + bi.logProgress() + + case <-bi.quit: + break out + } + } + bi.wg.Done() +} + +// statusHandler waits for updates from the import operation and notifies +// the passed doneChan with the results of the import. It also causes all +// goroutines to exit if an error is reported from any of them. +func (bi *blockImporter) statusHandler(resultsChan chan *importResults) { + select { + // An error from either of the goroutines means we're done so signal + // caller with the error and signal all goroutines to quit. + case err := <-bi.errChan: + resultsChan <- &importResults{ + blocksProcessed: bi.blocksProcessed, + blocksImported: bi.blocksImported, + err: err, + } + close(bi.quit) + + // The import finished normally. + case <-bi.doneChan: + resultsChan <- &importResults{ + blocksProcessed: bi.blocksProcessed, + blocksImported: bi.blocksImported, + err: nil, + } + } +} + +// Import is the core function which handles importing the blocks from the file +// associated with the block importer to the database. It returns a channel +// on which the results will be returned when the operation has completed. +func (bi *blockImporter) Import() chan *importResults { + // Start up the read and process handling goroutines. This setup allows + // blocks to be read from disk in parallel while being processed. + bi.wg.Add(2) + go bi.readHandler() + go bi.processHandler() + + // Wait for the import to finish in a separate goroutine and signal + // the status handler when done. + go func() { + bi.wg.Wait() + bi.doneChan <- true + }() + + // Start the status handler and return the result channel that it will + // send the results on when the import is done. + resultChan := make(chan *importResults) + go bi.statusHandler(resultChan) + return resultChan +} + +// newBlockImporter returns a new importer for the provided file reader seeker +// and database. +func newBlockImporter(db database.DB, r io.ReadSeeker) *blockImporter { + return &blockImporter{ + db: db, + r: r, + processQueue: make(chan []byte, 2), + doneChan: make(chan bool), + errChan: make(chan error), + quit: make(chan struct{}), + lastLogTime: time.Now(), + } +} + +// Execute is the main entry point for the command. It's invoked by the parser. +func (cmd *importCmd) Execute(args []string) error { + // Setup the global config options and ensure they are valid. + if err := setupGlobalConfig(); err != nil { + return err + } + + // Ensure the specified block file exists. + if !fileExists(cmd.InFile) { + str := "The specified block file [%v] does not exist" + return fmt.Errorf(str, cmd.InFile) + } + + // Load the block database. + db, err := loadBlockDB() + if err != nil { + return err + } + defer db.Close() + + // Ensure the database is sync'd and closed on Ctrl+C. + addInterruptHandler(func() { + log.Infof("Gracefully shutting down the database...") + db.Close() + }) + + fi, err := os.Open(importCfg.InFile) + if err != nil { + return err + } + defer fi.Close() + + // Create a block importer for the database and input file and start it. + // The results channel returned from start will contain an error if + // anything went wrong. + importer := newBlockImporter(db, fi) + + // Perform the import asynchronously and signal the main goroutine when + // done. This allows blocks to be processed and read in parallel. The + // results channel returned from Import contains the statistics about + // the import including an error if something went wrong. This is done + // in a separate goroutine rather than waiting directly so the main + // goroutine can be signaled for shutdown by either completion, error, + // or from the main interrupt handler. This is necessary since the main + // goroutine must be kept running long enough for the interrupt handler + // goroutine to finish. + go func() { + log.Info("Starting import") + resultsChan := importer.Import() + results := <-resultsChan + if results.err != nil { + dbErr, ok := results.err.(database.Error) + if !ok || ok && dbErr.ErrorCode != database.ErrDbNotOpen { + shutdownChannel <- results.err + return + } + } + + log.Infof("Processed a total of %d blocks (%d imported, %d "+ + "already known)", results.blocksProcessed, + results.blocksImported, + results.blocksProcessed-results.blocksImported) + shutdownChannel <- nil + }() + + // Wait for shutdown signal from either a normal completion or from the + // interrupt handler. + err = <-shutdownChannel + return err +} diff --git a/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/loadheaders.go b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/loadheaders.go new file mode 100644 index 0000000000000000000000000000000000000000..e8f20bcd10ba4c52f1c30696b69dab359d12e7ce --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/loadheaders.go @@ -0,0 +1,101 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "time" + + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" +) + +// headersCmd defines the configuration options for the loadheaders command. +type headersCmd struct { + Bulk bool `long:"bulk" description:"Use bulk loading of headers instead of one at a time"` +} + +var ( + // headersCfg defines the configuration options for the command. + headersCfg = headersCmd{ + Bulk: false, + } +) + +// Execute is the main entry point for the command. It's invoked by the parser. +func (cmd *headersCmd) Execute(args []string) error { + // Setup the global config options and ensure they are valid. + if err := setupGlobalConfig(); err != nil { + return err + } + + // Load the block database. + db, err := loadBlockDB() + if err != nil { + return err + } + defer db.Close() + + // NOTE: This code will only work for ffldb. Ideally the package using + // the database would keep a metadata index of its own. + blockIdxName := []byte("ffldb-blockidx") + if !headersCfg.Bulk { + err = db.View(func(tx database.Tx) error { + totalHdrs := 0 + blockIdxBucket := tx.Metadata().Bucket(blockIdxName) + blockIdxBucket.ForEach(func(k, v []byte) error { + totalHdrs++ + return nil + }) + log.Infof("Loading headers for %d blocks...", totalHdrs) + numLoaded := 0 + startTime := time.Now() + blockIdxBucket.ForEach(func(k, v []byte) error { + var hash wire.ShaHash + copy(hash[:], k) + _, err := tx.FetchBlockHeader(&hash) + if err != nil { + return err + } + numLoaded++ + return nil + }) + log.Infof("Loaded %d headers in %v", numLoaded, + time.Now().Sub(startTime)) + return nil + }) + if err != nil { + return err + } + + return nil + } + + // Bulk load headers. + err = db.View(func(tx database.Tx) error { + blockIdxBucket := tx.Metadata().Bucket(blockIdxName) + hashes := make([]wire.ShaHash, 0, 500000) + blockIdxBucket.ForEach(func(k, v []byte) error { + var hash wire.ShaHash + copy(hash[:], k) + hashes = append(hashes, hash) + return nil + }) + + log.Infof("Loading headers for %d blocks...", len(hashes)) + startTime := time.Now() + hdrs, err := tx.FetchBlockHeaders(hashes) + if err != nil { + return err + } + log.Infof("Loaded %d headers in %v", len(hdrs), + time.Now().Sub(startTime)) + return nil + }) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/main.go b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/main.go new file mode 100644 index 0000000000000000000000000000000000000000..04a167694628e641d2de72fe046542e34ead6d7a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/main.go @@ -0,0 +1,116 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btclog" + flags "github.com/btcsuite/go-flags" +) + +const ( + // blockDbNamePrefix is the prefix for the btcd block database. + blockDbNamePrefix = "blocks" +) + +var ( + log btclog.Logger + shutdownChannel = make(chan error) +) + +// loadBlockDB opens the block database and returns a handle to it. +func loadBlockDB() (database.DB, error) { + // The database name is based on the database type. + dbName := blockDbNamePrefix + "_" + cfg.DbType + dbPath := filepath.Join(cfg.DataDir, dbName) + + log.Infof("Loading block database from '%s'", dbPath) + db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net) + if err != nil { + // Return the error if it's not because the database doesn't + // exist. + if dbErr, ok := err.(database.Error); !ok || dbErr.ErrorCode != + database.ErrDbDoesNotExist { + + return nil, err + } + + // Create the db if it does not exist. + err = os.MkdirAll(cfg.DataDir, 0700) + if err != nil { + return nil, err + } + db, err = database.Create(cfg.DbType, dbPath, activeNetParams.Net) + if err != nil { + return nil, err + } + } + + log.Info("Block database loaded") + return db, nil +} + +// realMain is the real main function for the utility. It is necessary to work +// around the fact that deferred functions do not run when os.Exit() is called. +func realMain() error { + // Setup logging. + backendLogger := btclog.NewDefaultBackendLogger() + defer backendLogger.Flush() + log = btclog.NewSubsystemLogger(backendLogger, "") + dbLog := btclog.NewSubsystemLogger(backendLogger, "BCDB: ") + dbLog.SetLevel(btclog.DebugLvl) + database.UseLogger(dbLog) + + // Setup the parser options and commands. + appName := filepath.Base(os.Args[0]) + appName = strings.TrimSuffix(appName, filepath.Ext(appName)) + parserFlags := flags.Options(flags.HelpFlag | flags.PassDoubleDash) + parser := flags.NewNamedParser(appName, parserFlags) + parser.AddGroup("Global Options", "", cfg) + parser.AddCommand("insecureimport", + "Insecurely import bulk block data from bootstrap.dat", + "Insecurely import bulk block data from bootstrap.dat. "+ + "WARNING: This is NOT secure because it does NOT "+ + "verify chain rules. It is only provided for testing "+ + "purposes.", &importCfg) + parser.AddCommand("loadheaders", + "Time how long to load headers for all blocks in the database", + "", &headersCfg) + parser.AddCommand("fetchblock", + "Fetch the specific block hash from the database", "", + &fetchBlockCfg) + parser.AddCommand("fetchblockregion", + "Fetch the specified block region from the database", "", + &blockRegionCfg) + + // Parse command line and invoke the Execute function for the specified + // command. + if _, err := parser.Parse(); err != nil { + if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { + parser.WriteHelp(os.Stderr) + } else { + log.Error(err) + } + + return err + } + + return nil +} + +func main() { + // Use all processor cores. + runtime.GOMAXPROCS(runtime.NumCPU()) + + // Work around defer not working after os.Exit() + if err := realMain(); err != nil { + os.Exit(1) + } +} diff --git a/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/signal.go b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/signal.go new file mode 100644 index 0000000000000000000000000000000000000000..a32f1b376d099aaf0a9f090eb8256e63a8f99853 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/cmd/dbtool/signal.go @@ -0,0 +1,82 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "os" + "os/signal" +) + +// interruptChannel is used to receive SIGINT (Ctrl+C) signals. +var interruptChannel chan os.Signal + +// addHandlerChannel is used to add an interrupt handler to the list of handlers +// to be invoked on SIGINT (Ctrl+C) signals. +var addHandlerChannel = make(chan func()) + +// mainInterruptHandler listens for SIGINT (Ctrl+C) signals on the +// interruptChannel and invokes the registered interruptCallbacks accordingly. +// It also listens for callback registration. It must be run as a goroutine. +func mainInterruptHandler() { + // interruptCallbacks is a list of callbacks to invoke when a + // SIGINT (Ctrl+C) is received. + var interruptCallbacks []func() + + // isShutdown is a flag which is used to indicate whether or not + // the shutdown signal has already been received and hence any future + // attempts to add a new interrupt handler should invoke them + // immediately. + var isShutdown bool + + for { + select { + case <-interruptChannel: + // Ignore more than one shutdown signal. + if isShutdown { + log.Infof("Received SIGINT (Ctrl+C). " + + "Already shutting down...") + continue + } + + isShutdown = true + log.Infof("Received SIGINT (Ctrl+C). Shutting down...") + + // Run handlers in LIFO order. + for i := range interruptCallbacks { + idx := len(interruptCallbacks) - 1 - i + callback := interruptCallbacks[idx] + callback() + } + + // Signal the main goroutine to shutdown. + go func() { + shutdownChannel <- nil + }() + + case handler := <-addHandlerChannel: + // The shutdown signal has already been received, so + // just invoke and new handlers immediately. + if isShutdown { + handler() + } + + interruptCallbacks = append(interruptCallbacks, handler) + } + } +} + +// addInterruptHandler adds a handler to call when a SIGINT (Ctrl+C) is +// received. +func addInterruptHandler(handler func()) { + // Create the channel and start the main interrupt handler which invokes + // all other callbacks and exits if not already done. + if interruptChannel == nil { + interruptChannel = make(chan os.Signal, 1) + signal.Notify(interruptChannel, os.Interrupt) + go mainInterruptHandler() + } + + addHandlerChannel <- handler +} diff --git a/vendor/github.com/btcsuite/btcd/database/doc.go b/vendor/github.com/btcsuite/btcd/database/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..5895abe24809dfe61ed8437387b171a1fd5fb6ba --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/doc.go @@ -0,0 +1,94 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package database provides a block and metadata storage database. + +Overview + +As of Feb 2016, there are over 400,000 blocks in the Bitcoin block chain and +and over 112 million transactions (which turns out to be over 60GB of data). +This package provides a database layer to store and retrieve this data in a +simple and efficient manner. + +The default backend, ffldb, has a strong focus on speed, efficiency, and +robustness. It makes use leveldb for the metadata, flat files for block +storage, and strict checksums in key areas to ensure data integrity. + +A quick overview of the features database provides are as follows: + + - Key/value metadata store + - Bitcoin block storage + - Efficient retrieval of block headers and regions (transactions, scripts, etc) + - Read-only and read-write transactions with both manual and managed modes + - Nested buckets + - Supports registration of backend databases + - Comprehensive test coverage + +Database + +The main entry point is the DB interface. It exposes functionality for +transactional-based access and storage of metadata and block data. It is +obtained via the Create and Open functions which take a database type string +that identifies the specific database driver (backend) to use as well as +arguments specific to the specified driver. + +Namespaces + +The Namespace interface is an abstraction that provides facilities for obtaining +transactions (the Tx interface) that are the basis of all database reads and +writes. Unlike some database interfaces that support reading and writing +without transactions, this interface requires transactions even when only +reading or writing a single key. + +The Begin function provides an unmanaged transaction while the View and Update +functions provide a managed transaction. These are described in more detail +below. + +Transactions + +The Tx interface provides facilities for rolling back or committing changes that +took place while the transaction was active. It also provides the root metadata +bucket under which all keys, values, and nested buckets are stored. A +transaction can either be read-only or read-write and managed or unmanaged. + +Managed versus Unmanaged Transactions + +A managed transaction is one where the caller provides a function to execute +within the context of the transaction and the commit or rollback is handled +automatically depending on whether or not the provided function returns an +error. Attempting to manually call Rollback or Commit on the managed +transaction will result in a panic. + +An unmanaged transaction, on the other hand, requires the caller to manually +call Commit or Rollback when they are finished with it. Leaving transactions +open for long periods of time can have several adverse effects, so it is +recommended that managed transactions are used instead. + +Buckets + +The Bucket interface provides the ability to manipulate key/value pairs and +nested buckets as well as iterate through them. + +The Get, Put, and Delete functions work with key/value pairs, while the Bucket, +CreateBucket, CreateBucketIfNotExists, and DeleteBucket functions work with +buckets. The ForEach function allows the caller to provide a function to be +called with each key/value pair and nested bucket in the current bucket. + +Metadata Bucket + +As discussed above, all of the functions which are used to manipulate key/value +pairs and nested buckets exist on the Bucket interface. The root metadata +bucket is the upper-most bucket in which data is stored and is created at the +same time as the database. Use the Metadata function on the Tx interface +to retrieve it. + +Nested Buckets + +The CreateBucket and CreateBucketIfNotExists functions on the Bucket interface +provide the ability to create an arbitrary number of nested buckets. It is +a good idea to avoid a lot of buckets with little data in them as it could lead +to poor page utilization depending on the specific driver in use. +*/ +package database diff --git a/vendor/github.com/btcsuite/btcd/database/driver.go b/vendor/github.com/btcsuite/btcd/database/driver.go new file mode 100644 index 0000000000000000000000000000000000000000..765ab4d11eec25856cb123d02ff103f3fcfaf1d6 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/driver.go @@ -0,0 +1,89 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package database + +import ( + "fmt" + + "github.com/btcsuite/btclog" +) + +// Driver defines a structure for backend drivers to use when they registered +// themselves as a backend which implements the DB interface. +type Driver struct { + // DbType is the identifier used to uniquely identify a specific + // database driver. There can be only one driver with the same name. + DbType string + + // Create is the function that will be invoked with all user-specified + // arguments to create the database. This function must return + // ErrDbExists if the database already exists. + Create func(args ...interface{}) (DB, error) + + // Open is the function that will be invoked with all user-specified + // arguments to open the database. This function must return + // ErrDbDoesNotExist if the database has not already been created. + Open func(args ...interface{}) (DB, error) + + // UseLogger uses a specified Logger to output package logging info. + UseLogger func(logger btclog.Logger) +} + +// driverList holds all of the registered database backends. +var drivers = make(map[string]*Driver) + +// RegisterDriver adds a backend database driver to available interfaces. +// ErrDbTypeRegistered will be retruned if the database type for the driver has +// already been registered. +func RegisterDriver(driver Driver) error { + if _, exists := drivers[driver.DbType]; exists { + str := fmt.Sprintf("driver %q is already registered", + driver.DbType) + return makeError(ErrDbTypeRegistered, str, nil) + } + + drivers[driver.DbType] = &driver + return nil +} + +// SupportedDrivers returns a slice of strings that represent the database +// drivers that have been registered and are therefore supported. +func SupportedDrivers() []string { + supportedDBs := make([]string, 0, len(drivers)) + for _, drv := range drivers { + supportedDBs = append(supportedDBs, drv.DbType) + } + return supportedDBs +} + +// Create initializes and opens a database for the specified type. The +// arguments are specific to the database type driver. See the documentation +// for the database driver for further details. +// +// ErrDbUnknownType will be returned if the the database type is not registered. +func Create(dbType string, args ...interface{}) (DB, error) { + drv, exists := drivers[dbType] + if !exists { + str := fmt.Sprintf("driver %q is not registered", dbType) + return nil, makeError(ErrDbUnknownType, str, nil) + } + + return drv.Create(args...) +} + +// Open opens an existing database for the specified type. The arguments are +// specific to the database type driver. See the documentation for the database +// driver for further details. +// +// ErrDbUnknownType will be returned if the the database type is not registered. +func Open(dbType string, args ...interface{}) (DB, error) { + drv, exists := drivers[dbType] + if !exists { + str := fmt.Sprintf("driver %q is not registered", dbType) + return nil, makeError(ErrDbUnknownType, str, nil) + } + + return drv.Open(args...) +} diff --git a/vendor/github.com/btcsuite/btcd/database/driver_test.go b/vendor/github.com/btcsuite/btcd/database/driver_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3bb48de198ee852c1466b285d199ef1ca8b03c63 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/driver_test.go @@ -0,0 +1,136 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package database_test + +import ( + "fmt" + "testing" + + "github.com/btcsuite/btcd/database" + _ "github.com/btcsuite/btcd/database/ffldb" +) + +var ( + // ignoreDbTypes are types which should be ignored when running tests + // that iterate all supported DB types. This allows some tests to add + // bogus drivers for testing purposes while still allowing other tests + // to easily iterate all supported drivers. + ignoreDbTypes = map[string]bool{"createopenfail": true} +) + +// checkDbError ensures the passed error is a database.Error with an error code +// that matches the passed error code. +func checkDbError(t *testing.T, testName string, gotErr error, wantErrCode database.ErrorCode) bool { + dbErr, ok := gotErr.(database.Error) + if !ok { + t.Errorf("%s: unexpected error type - got %T, want %T", + testName, gotErr, database.Error{}) + return false + } + if dbErr.ErrorCode != wantErrCode { + t.Errorf("%s: unexpected error code - got %s (%s), want %s", + testName, dbErr.ErrorCode, dbErr.Description, + wantErrCode) + return false + } + + return true +} + +// TestAddDuplicateDriver ensures that adding a duplicate driver does not +// overwrite an existing one. +func TestAddDuplicateDriver(t *testing.T) { + supportedDrivers := database.SupportedDrivers() + if len(supportedDrivers) == 0 { + t.Errorf("no backends to test") + return + } + dbType := supportedDrivers[0] + + // bogusCreateDB is a function which acts as a bogus create and open + // driver function and intentionally returns a failure that can be + // detected if the interface allows a duplicate driver to overwrite an + // existing one. + bogusCreateDB := func(args ...interface{}) (database.DB, error) { + return nil, fmt.Errorf("duplicate driver allowed for database "+ + "type [%v]", dbType) + } + + // Create a driver that tries to replace an existing one. Set its + // create and open functions to a function that causes a test failure if + // they are invoked. + driver := database.Driver{ + DbType: dbType, + Create: bogusCreateDB, + Open: bogusCreateDB, + } + testName := "duplicate driver registration" + err := database.RegisterDriver(driver) + if !checkDbError(t, testName, err, database.ErrDbTypeRegistered) { + return + } +} + +// TestCreateOpenFail ensures that errors which occur while opening or closing +// a database are handled properly. +func TestCreateOpenFail(t *testing.T) { + // bogusCreateDB is a function which acts as a bogus create and open + // driver function that intentionally returns a failure which can be + // detected. + dbType := "createopenfail" + openError := fmt.Errorf("failed to create or open database for "+ + "database type [%v]", dbType) + bogusCreateDB := func(args ...interface{}) (database.DB, error) { + return nil, openError + } + + // Create and add driver that intentionally fails when created or opened + // to ensure errors on database open and create are handled properly. + driver := database.Driver{ + DbType: dbType, + Create: bogusCreateDB, + Open: bogusCreateDB, + } + database.RegisterDriver(driver) + + // Ensure creating a database with the new type fails with the expected + // error. + _, err := database.Create(dbType) + if err != openError { + t.Errorf("expected error not received - got: %v, want %v", err, + openError) + return + } + + // Ensure opening a database with the new type fails with the expected + // error. + _, err = database.Open(dbType) + if err != openError { + t.Errorf("expected error not received - got: %v, want %v", err, + openError) + return + } +} + +// TestCreateOpenUnsupported ensures that attempting to create or open an +// unsupported database type is handled properly. +func TestCreateOpenUnsupported(t *testing.T) { + // Ensure creating a database with an unsupported type fails with the + // expected error. + testName := "create with unsupported database type" + dbType := "unsupported" + _, err := database.Create(dbType) + if !checkDbError(t, testName, err, database.ErrDbUnknownType) { + return + } + + // Ensure opening a database with the an unsupported type fails with the + // expected error. + testName = "open with unsupported database type" + _, err = database.Open(dbType) + if !checkDbError(t, testName, err, database.ErrDbUnknownType) { + return + } +} diff --git a/vendor/github.com/btcsuite/btcd/database/error.go b/vendor/github.com/btcsuite/btcd/database/error.go new file mode 100644 index 0000000000000000000000000000000000000000..49c250eef58233c00513eee90aeecbb19d1cd822 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/error.go @@ -0,0 +1,197 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package database + +import "fmt" + +// ErrorCode identifies a kind of error. +type ErrorCode int + +// These constants are used to identify a specific database Error. +const ( + // ************************************** + // Errors related to driver registration. + // ************************************** + + // ErrDbTypeRegistered indicates two different database drivers + // attempt to register with the name database type. + ErrDbTypeRegistered ErrorCode = iota + + // ************************************* + // Errors related to database functions. + // ************************************* + + // ErrDbUnknownType indicates there is no driver registered for + // the specified database type. + ErrDbUnknownType + + // ErrDbDoesNotExist indicates open is called for a database that + // does not exist. + ErrDbDoesNotExist + + // ErrDbExists indicates create is called for a database that + // already exists. + ErrDbExists + + // ErrDbNotOpen indicates a database instance is accessed before + // it is opened or after it is closed. + ErrDbNotOpen + + // ErrDbAlreadyOpen indicates open was called on a database that + // is already open. + ErrDbAlreadyOpen + + // ErrInvalid indicates the specified database is not valid. + ErrInvalid + + // ErrCorruption indicates a checksum failure occurred which invariably + // means the database is corrupt. + ErrCorruption + + // **************************************** + // Errors related to database transactions. + // **************************************** + + // ErrTxClosed indicates an attempt was made to commit or rollback a + // transaction that has already had one of those operations performed. + ErrTxClosed + + // ErrTxNotWritable indicates an operation that requires write access to + // the database was attempted against a read-only transaction. + ErrTxNotWritable + + // ************************************** + // Errors related to metadata operations. + // ************************************** + + // ErrBucketNotFound indicates an attempt to access a bucket that has + // not been created yet. + ErrBucketNotFound + + // ErrBucketExists indicates an attempt to create a bucket that already + // exists. + ErrBucketExists + + // ErrBucketNameRequired indicates an attempt to create a bucket with a + // blank name. + ErrBucketNameRequired + + // ErrKeyRequired indicates at attempt to insert a zero-length key. + ErrKeyRequired + + // ErrKeyTooLarge indicates an attmempt to insert a key that is larger + // than the max allowed key size. The max key size depends on the + // specific backend driver being used. As a general rule, key sizes + // should be relatively, so this should rarely be an issue. + ErrKeyTooLarge + + // ErrValueTooLarge indicates an attmpt to insert a value that is larger + // than max allowed value size. The max key size depends on the + // specific backend driver being used. + ErrValueTooLarge + + // ErrIncompatibleValue indicates the value in question is invalid for + // the specific requested operation. For example, trying create or + // delete a bucket with an existing non-bucket key, attempting to create + // or delete a non-bucket key with an existing bucket key, or trying to + // delete a value via a cursor when it points to a nested bucket. + ErrIncompatibleValue + + // *************************************** + // Errors related to block I/O operations. + // *************************************** + + // ErrBlockNotFound indicates a block with the provided hash does not + // exist in the database. + ErrBlockNotFound + + // ErrBlockExists indicates a block with the provided hash already + // exists in the database. + ErrBlockExists + + // ErrBlockRegionInvalid indicates a region that exceeds the bounds of + // the specified block was requested. When the hash provided by the + // region does not correspond to an existing block, the error will be + // ErrBlockNotFound instead. + ErrBlockRegionInvalid + + // *********************************** + // Support for driver-specific errors. + // *********************************** + + // ErrDriverSpecific indicates the Err field is a driver-specific error. + // This provides a mechanism for drivers to plug-in their own custom + // errors for any situations which aren't already covered by the error + // codes provided by this package. + ErrDriverSpecific + + // numErrorCodes is the maximum error code number used in tests. + numErrorCodes +) + +// Map of ErrorCode values back to their constant names for pretty printing. +var errorCodeStrings = map[ErrorCode]string{ + ErrDbTypeRegistered: "ErrDbTypeRegistered", + ErrDbUnknownType: "ErrDbUnknownType", + ErrDbDoesNotExist: "ErrDbDoesNotExist", + ErrDbExists: "ErrDbExists", + ErrDbNotOpen: "ErrDbNotOpen", + ErrDbAlreadyOpen: "ErrDbAlreadyOpen", + ErrInvalid: "ErrInvalid", + ErrCorruption: "ErrCorruption", + ErrTxClosed: "ErrTxClosed", + ErrTxNotWritable: "ErrTxNotWritable", + ErrBucketNotFound: "ErrBucketNotFound", + ErrBucketExists: "ErrBucketExists", + ErrBucketNameRequired: "ErrBucketNameRequired", + ErrKeyRequired: "ErrKeyRequired", + ErrKeyTooLarge: "ErrKeyTooLarge", + ErrValueTooLarge: "ErrValueTooLarge", + ErrIncompatibleValue: "ErrIncompatibleValue", + ErrBlockNotFound: "ErrBlockNotFound", + ErrBlockExists: "ErrBlockExists", + ErrBlockRegionInvalid: "ErrBlockRegionInvalid", + ErrDriverSpecific: "ErrDriverSpecific", +} + +// String returns the ErrorCode as a human-readable name. +func (e ErrorCode) String() string { + if s := errorCodeStrings[e]; s != "" { + return s + } + return fmt.Sprintf("Unknown ErrorCode (%d)", int(e)) +} + +// Error provides a single type for errors that can happen during database +// operation. It is used to indicate several types of failures including errors +// with caller requests such as specifying invalid block regions or attempting +// to access data against closed database transactions, driver errors, errors +// retrieving data, and errors communicating with database servers. +// +// The caller can use type assertions to determine if an error is an Error and +// access the ErrorCode field to ascertain the specific reason for the failure. +// +// The ErrDriverSpecific error code will also have the Err field set with the +// underlying error. Depending on the backend driver, the Err field might be +// set to the underlying error for other error codes as well. +type Error struct { + ErrorCode ErrorCode // Describes the kind of error + Description string // Human readable description of the issue + Err error // Underlying error +} + +// Error satisfies the error interface and prints human-readable errors. +func (e Error) Error() string { + if e.Err != nil { + return e.Description + ": " + e.Err.Error() + } + return e.Description +} + +// makeError creates an Error given a set of arguments. The error code must +// be one of the error codes provided by this package. +func makeError(c ErrorCode, desc string, err error) Error { + return Error{ErrorCode: c, Description: desc, Err: err} +} diff --git a/vendor/github.com/btcsuite/btcd/database/error_test.go b/vendor/github.com/btcsuite/btcd/database/error_test.go new file mode 100644 index 0000000000000000000000000000000000000000..759d26e1648bff65ab42ebd33370696d6b876bbf --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/error_test.go @@ -0,0 +1,97 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package database_test + +import ( + "errors" + "testing" + + "github.com/btcsuite/btcd/database" +) + +// TestErrorCodeStringer tests the stringized output for the ErrorCode type. +func TestErrorCodeStringer(t *testing.T) { + tests := []struct { + in database.ErrorCode + want string + }{ + {database.ErrDbTypeRegistered, "ErrDbTypeRegistered"}, + {database.ErrDbUnknownType, "ErrDbUnknownType"}, + {database.ErrDbDoesNotExist, "ErrDbDoesNotExist"}, + {database.ErrDbExists, "ErrDbExists"}, + {database.ErrDbNotOpen, "ErrDbNotOpen"}, + {database.ErrDbAlreadyOpen, "ErrDbAlreadyOpen"}, + {database.ErrInvalid, "ErrInvalid"}, + {database.ErrCorruption, "ErrCorruption"}, + {database.ErrTxClosed, "ErrTxClosed"}, + {database.ErrTxNotWritable, "ErrTxNotWritable"}, + {database.ErrBucketNotFound, "ErrBucketNotFound"}, + {database.ErrBucketExists, "ErrBucketExists"}, + {database.ErrBucketNameRequired, "ErrBucketNameRequired"}, + {database.ErrKeyRequired, "ErrKeyRequired"}, + {database.ErrKeyTooLarge, "ErrKeyTooLarge"}, + {database.ErrValueTooLarge, "ErrValueTooLarge"}, + {database.ErrIncompatibleValue, "ErrIncompatibleValue"}, + {database.ErrBlockNotFound, "ErrBlockNotFound"}, + {database.ErrBlockExists, "ErrBlockExists"}, + {database.ErrBlockRegionInvalid, "ErrBlockRegionInvalid"}, + {database.ErrDriverSpecific, "ErrDriverSpecific"}, + + {0xffff, "Unknown ErrorCode (65535)"}, + } + + // Detect additional error codes that don't have the stringer added. + if len(tests)-1 != int(database.TstNumErrorCodes) { + t.Errorf("It appears an error code was added without adding " + + "an associated stringer test") + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.in.String() + if result != test.want { + t.Errorf("String #%d\ngot: %s\nwant: %s", i, result, + test.want) + continue + } + } +} + +// TestError tests the error output for the Error type. +func TestError(t *testing.T) { + t.Parallel() + + tests := []struct { + in database.Error + want string + }{ + { + database.Error{Description: "some error"}, + "some error", + }, + { + database.Error{Description: "human-readable error"}, + "human-readable error", + }, + { + database.Error{ + ErrorCode: database.ErrDriverSpecific, + Description: "some error", + Err: errors.New("driver-specific error"), + }, + "some error: driver-specific error", + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.in.Error() + if result != test.want { + t.Errorf("Error #%d\n got: %s want: %s", i, result, + test.want) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/database/example_test.go b/vendor/github.com/btcsuite/btcd/database/example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8b6fe7bce4297bcee2ebd4ca2b89268bbfcfaf32 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/example_test.go @@ -0,0 +1,177 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package database_test + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + _ "github.com/btcsuite/btcd/database/ffldb" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// This example demonstrates creating a new database. +func ExampleCreate() { + // This example assumes the ffldb driver is imported. + // + // import ( + // "github.com/btcsuite/btcd/database" + // _ "github.com/btcsuite/btcd/database/ffldb" + // ) + + // Create a database and schedule it to be closed and removed on exit. + // Typically you wouldn't want to remove the database right away like + // this, nor put it in the temp directory, but it's done here to ensure + // the example cleans up after itself. + dbPath := filepath.Join(os.TempDir(), "examplecreate") + db, err := database.Create("ffldb", dbPath, wire.MainNet) + if err != nil { + fmt.Println(err) + return + } + defer os.RemoveAll(dbPath) + defer db.Close() + + // Output: +} + +// This example demonstrates creating a new database and using a managed +// read-write transaction to store and retrieve metadata. +func Example_basicUsage() { + // This example assumes the ffldb driver is imported. + // + // import ( + // "github.com/btcsuite/btcd/database" + // _ "github.com/btcsuite/btcd/database/ffldb" + // ) + + // Create a database and schedule it to be closed and removed on exit. + // Typically you wouldn't want to remove the database right away like + // this, nor put it in the temp directory, but it's done here to ensure + // the example cleans up after itself. + dbPath := filepath.Join(os.TempDir(), "exampleusage") + db, err := database.Create("ffldb", dbPath, wire.MainNet) + if err != nil { + fmt.Println(err) + return + } + defer os.RemoveAll(dbPath) + defer db.Close() + + // Use the Update function of the database to perform a managed + // read-write transaction. The transaction will automatically be rolled + // back if the supplied inner function returns a non-nil error. + err = db.Update(func(tx database.Tx) error { + // Store a key/value pair directly in the metadata bucket. + // Typically a nested bucket would be used for a given feature, + // but this example is using the metadata bucket directly for + // simplicity. + key := []byte("mykey") + value := []byte("myvalue") + if err := tx.Metadata().Put(key, value); err != nil { + return err + } + + // Read the key back and ensure it matches. + if !bytes.Equal(tx.Metadata().Get(key), value) { + return fmt.Errorf("unexpected value for key '%s'", key) + } + + // Create a new nested bucket under the metadata bucket. + nestedBucketKey := []byte("mybucket") + nestedBucket, err := tx.Metadata().CreateBucket(nestedBucketKey) + if err != nil { + return err + } + + // The key from above that was set in the metadata bucket does + // not exist in this new nested bucket. + if nestedBucket.Get(key) != nil { + return fmt.Errorf("key '%s' is not expected nil", key) + } + + return nil + }) + if err != nil { + fmt.Println(err) + return + } + + // Output: +} + +// This example demonstrates creating a new database, using a managed read-write +// transaction to store a block, and using a managed read-only transaction to +// fetch the block. +func Example_blockStorageAndRetrieval() { + // This example assumes the ffldb driver is imported. + // + // import ( + // "github.com/btcsuite/btcd/database" + // _ "github.com/btcsuite/btcd/database/ffldb" + // ) + + // Create a database and schedule it to be closed and removed on exit. + // Typically you wouldn't want to remove the database right away like + // this, nor put it in the temp directory, but it's done here to ensure + // the example cleans up after itself. + dbPath := filepath.Join(os.TempDir(), "exampleblkstorage") + db, err := database.Create("ffldb", dbPath, wire.MainNet) + if err != nil { + fmt.Println(err) + return + } + defer os.RemoveAll(dbPath) + defer db.Close() + + // Use the Update function of the database to perform a managed + // read-write transaction and store a genesis block in the database as + // and example. + err = db.Update(func(tx database.Tx) error { + genesisBlock := chaincfg.MainNetParams.GenesisBlock + return tx.StoreBlock(btcutil.NewBlock(genesisBlock)) + }) + if err != nil { + fmt.Println(err) + return + } + + // Use the View function of the database to perform a managed read-only + // transaction and fetch the block stored above. + var loadedBlockBytes []byte + err = db.Update(func(tx database.Tx) error { + genesisHash := chaincfg.MainNetParams.GenesisHash + blockBytes, err := tx.FetchBlock(genesisHash) + if err != nil { + return err + } + + // As documented, all data fetched from the database is only + // valid during a database transaction in order to support + // zero-copy backends. Thus, make a copy of the data so it + // can be used outside of the transaction. + loadedBlockBytes = make([]byte, len(blockBytes)) + copy(loadedBlockBytes, blockBytes) + return nil + }) + if err != nil { + fmt.Println(err) + return + } + + // Typically at this point, the block could be deserialized via the + // wire.MsgBlock.Deserialize function or used in its serialized form + // depending on need. However, for this example, just display the + // number of serialized bytes to show it was loaded as expected. + fmt.Printf("Serialized block size: %d bytes\n", len(loadedBlockBytes)) + + // Output: + // Serialized block size: 285 bytes +} diff --git a/vendor/github.com/btcsuite/btcd/database/export_test.go b/vendor/github.com/btcsuite/btcd/database/export_test.go new file mode 100644 index 0000000000000000000000000000000000000000..49373d5d0080449d4b1ff1d8a2cdae44c23945c1 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/export_test.go @@ -0,0 +1,17 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +This test file is part of the database package rather than than the +database_test package so it can bridge access to the internals to properly test +cases which are either not possible or can't reliably be tested via the public +interface. The functions, constants, and variables are only exported while the +tests are being run. +*/ + +package database + +// TstNumErrorCodes makes the internal numErrorCodes parameter available to the +// test package. +const TstNumErrorCodes = numErrorCodes diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/README.md b/vendor/github.com/btcsuite/btcd/database/ffldb/README.md new file mode 100644 index 0000000000000000000000000000000000000000..dcb8308c59e8827268b723c5ab0a3abeb1e59b7e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/README.md @@ -0,0 +1,52 @@ +ffldb +===== + +[] +(https://travis-ci.org/btcsuite/btcd) + +Package ffldb implements a driver for the database package that uses leveldb for +the backing metadata and flat files for block storage. + +This driver is the recommended driver for use with btcd. It makes use leveldb +for the metadata, flat files for block storage, and checksums in key areas to +ensure data integrity. + +Package ffldb is licensed under the copyfree ISC license. + +## Usage + +This package is a driver to the database package and provides the database type +of "ffldb". The parameters the Open and Create functions take are the +database path as a string and the block network. + +```Go +db, err := database.Open("ffldb", "path/to/database", wire.MainNet) +if err != nil { + // Handle error +} +``` + +```Go +db, err := database.Create("ffldb", "path/to/database", wire.MainNet) +if err != nil { + // Handle error +} +``` + +## Documentation + +[] +(http://godoc.org/github.com/btcsuite/btcd/database/ffldb) + +Full `go doc` style documentation for the project can be viewed online without +installing this package by using the GoDoc site here: +http://godoc.org/github.com/btcsuite/btcd/database/ffldb + +You can also view the documentation locally once the package is installed with +the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to +http://localhost:6060/pkg/github.com/btcsuite/btcd/database/ffldb + +## License + +Package ffldb is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/bench_test.go b/vendor/github.com/btcsuite/btcd/database/ffldb/bench_test.go new file mode 100644 index 0000000000000000000000000000000000000000..deb7b700f9253e77f98de0356ff278e4a7f9f37e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/bench_test.go @@ -0,0 +1,103 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package ffldb + +import ( + "os" + "path/filepath" + "testing" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcutil" +) + +// BenchmarkBlockHeader benchmarks how long it takes to load the mainnet genesis +// block header. +func BenchmarkBlockHeader(b *testing.B) { + // Start by creating a new database and populating it with the mainnet + // genesis block. + dbPath := filepath.Join(os.TempDir(), "ffldb-benchblkhdr") + _ = os.RemoveAll(dbPath) + db, err := database.Create("ffldb", dbPath, blockDataNet) + if err != nil { + b.Fatal(err) + } + defer os.RemoveAll(dbPath) + defer db.Close() + err = db.Update(func(tx database.Tx) error { + block := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) + if err := tx.StoreBlock(block); err != nil { + return err + } + return nil + }) + if err != nil { + b.Fatal(err) + } + + b.ReportAllocs() + b.ResetTimer() + err = db.View(func(tx database.Tx) error { + blockHash := chaincfg.MainNetParams.GenesisHash + for i := 0; i < b.N; i++ { + _, err := tx.FetchBlockHeader(blockHash) + if err != nil { + return err + } + } + return nil + }) + if err != nil { + b.Fatal(err) + } + + // Don't benchmark teardown. + b.StopTimer() +} + +// BenchmarkBlockHeader benchmarks how long it takes to load the mainnet genesis +// block. +func BenchmarkBlock(b *testing.B) { + // Start by creating a new database and populating it with the mainnet + // genesis block. + dbPath := filepath.Join(os.TempDir(), "ffldb-benchblk") + _ = os.RemoveAll(dbPath) + db, err := database.Create("ffldb", dbPath, blockDataNet) + if err != nil { + b.Fatal(err) + } + defer os.RemoveAll(dbPath) + defer db.Close() + err = db.Update(func(tx database.Tx) error { + block := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) + if err := tx.StoreBlock(block); err != nil { + return err + } + return nil + }) + if err != nil { + b.Fatal(err) + } + + b.ReportAllocs() + b.ResetTimer() + err = db.View(func(tx database.Tx) error { + blockHash := chaincfg.MainNetParams.GenesisHash + for i := 0; i < b.N; i++ { + _, err := tx.FetchBlock(blockHash) + if err != nil { + return err + } + } + return nil + }) + if err != nil { + b.Fatal(err) + } + + // Don't benchmark teardown. + b.StopTimer() +} diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/blockio.go b/vendor/github.com/btcsuite/btcd/database/ffldb/blockio.go new file mode 100644 index 0000000000000000000000000000000000000000..260bf552a8e0112f3b90640b79cc23a3fea326f0 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/blockio.go @@ -0,0 +1,769 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// This file contains the implementation functions for reading, writing, and +// otherwise working with the flat files that house the actual blocks. + +package ffldb + +import ( + "container/list" + "encoding/binary" + "fmt" + "hash/crc32" + "io" + "os" + "path/filepath" + "sync" + + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" +) + +const ( + // The Bitcoin protocol encodes block height as int32, so max number of + // blocks is 2^31. Max block size per the protocol is 32MiB per block. + // So the theoretical max at the time this comment was written is 64PiB + // (pebibytes). With files @ 512MiB each, this would require a maximum + // of 134,217,728 files. Thus, choose 9 digits of precision for the + // filenames. An additional benefit is 9 digits provides 10^9 files @ + // 512MiB each for a total of ~476.84PiB (roughly 7.4 times the current + // theoretical max), so there is room for the max block size to grow in + // the future. + blockFilenameTemplate = "%09d.fdb" + + // maxOpenFiles is the max number of open files to maintain in the + // open blocks cache. Note that this does not include the current + // write file, so there will typically be one more than this value open. + maxOpenFiles = 25 + + // maxBlockFileSize is the maximum size for each file used to store + // blocks. + // + // NOTE: The current code uses uint32 for all offsets, so this value + // must be less than 2^32 (4 GiB). This is also why it's a typed + // constant. + maxBlockFileSize uint32 = 512 * 1024 * 1024 // 512 MiB + + // blockLocSize is the number of bytes the serialized block location + // data that is stored in the block index. + // + // The serialized block location format is: + // + // [0:4] Block file (4 bytes) + // [4:8] File offset (4 bytes) + // [8:12] Block length (4 bytes) + blockLocSize = 12 +) + +var ( + // castagnoli houses the Catagnoli polynomial used for CRC-32 checksums. + castagnoli = crc32.MakeTable(crc32.Castagnoli) +) + +// filer is an interface which acts very similar to a *os.File and is typically +// implemented by it. It exists so the test code can provide mock files for +// properly testing corruption and file system issues. +type filer interface { + io.Closer + io.WriterAt + io.ReaderAt + Truncate(size int64) error + Sync() error +} + +// lockableFile represents a block file on disk that has been opened for either +// read or read/write access. It also contains a read-write mutex to support +// multiple concurrent readers. +type lockableFile struct { + sync.RWMutex + file filer +} + +// writeCursor represents the current file and offset of the block file on disk +// for performing all writes. It also contains a read-write mutex to support +// multiple concurrent readers which can reuse the file handle. +type writeCursor struct { + sync.RWMutex + + // curFile is the current block file that will be appended to when + // writing new blocks. + curFile *lockableFile + + // curFileNum is the current block file number and is used to allow + // readers to use the same open file handle. + curFileNum uint32 + + // curOffset is the offset in the current write block file where the + // next new block will be written. + curOffset uint32 +} + +// blockStore houses information used to handle reading and writing blocks (and +// part of blocks) into flat files with support for multiple concurrent readers. +type blockStore struct { + // network is the specific network to use in the flat files for each + // block. + network wire.BitcoinNet + + // basePath is the base path used for the flat block files and metadata. + basePath string + + // maxBlockFileSize is the maximum size for each file used to store + // blocks. It is defined on the store so the whitebox tests can + // override the value. + maxBlockFileSize uint32 + + // The following fields are related to the flat files which hold the + // actual blocks. The number of open files is limited by maxOpenFiles. + // + // obfMutex protects concurrent access to the openBlockFiles map. It is + // a RWMutex so multiple readers can simultaneously access open files. + // + // openBlockFiles houses the open file handles for existing block files + // which have been opened read-only along with an individual RWMutex. + // This scheme allows multiple concurrent readers to the same file while + // preventing the file from being closed out from under them. + // + // lruMutex protects concurrent access to the least recently used list + // and lookup map. + // + // openBlocksLRU tracks how the open files are refenced by pushing the + // most recently used files to the front of the list thereby trickling + // the least recently used files to end of the list. When a file needs + // to be closed due to exceeding the the max number of allowed open + // files, the one at the end of the list is closed. + // + // fileNumToLRUElem is a mapping between a specific block file number + // and the associated list element on the least recently used list. + // + // Thus, with the combination of these fields, the database supports + // concurrent non-blocking reads across multiple and individual files + // along with intelligently limiting the number of open file handles by + // closing the least recently used files as needed. + // + // NOTE: The locking order used throughout is well-defined and MUST be + // followed. Failure to do so could lead to deadlocks. In particular, + // the locking order is as follows: + // 1) obfMutex + // 2) lruMutex + // 3) writeCursor mutex + // 4) specific file mutexes + // + // None of the mutexes are required to be locked at the same time, and + // often aren't. However, if they are to be locked simultaneously, they + // MUST be locked in the order previously specified. + // + // Due to the high performance and multi-read concurrency requirements, + // write locks should only be held for the minimum time necessary. + obfMutex sync.RWMutex + lruMutex sync.Mutex + openBlocksLRU *list.List // Contains uint32 block file numbers. + fileNumToLRUElem map[uint32]*list.Element + openBlockFiles map[uint32]*lockableFile + + // writeCursor houses the state for the current file and location that + // new blocks are written to. + writeCursor *writeCursor + + // These functions are set to openFile, openWriteFile, and deleteFile by + // default, but are exposed here to allow the whitebox tests to replace + // them when working with mock files. + openFileFunc func(fileNum uint32) (*lockableFile, error) + openWriteFileFunc func(fileNum uint32) (filer, error) + deleteFileFunc func(fileNum uint32) error +} + +// blockLocation identifies a particular block file and location. +type blockLocation struct { + blockFileNum uint32 + fileOffset uint32 + blockLen uint32 +} + +// deserializeBlockLoc deserializes the passed serialized block location +// information. This is data stored into the block index metadata for each +// block. The serialized data passed to this function MUST be at least +// blockLocSize bytes or it will panic. The error check is avoided here because +// this information will always be coming from the block index which includes a +// checksum to detect corruption. Thus it is safe to use this unchecked here. +func deserializeBlockLoc(serializedLoc []byte) blockLocation { + // The serialized block location format is: + // + // [0:4] Block file (4 bytes) + // [4:8] File offset (4 bytes) + // [8:12] Block length (4 bytes) + return blockLocation{ + blockFileNum: byteOrder.Uint32(serializedLoc[0:4]), + fileOffset: byteOrder.Uint32(serializedLoc[4:8]), + blockLen: byteOrder.Uint32(serializedLoc[8:12]), + } +} + +// serializeBlockLoc returns the serialization of the passed block location. +// This is data to be stored into the block index metadata for each block. +func serializeBlockLoc(loc blockLocation) []byte { + // The serialized block location format is: + // + // [0:4] Block file (4 bytes) + // [4:8] File offset (4 bytes) + // [8:12] Block length (4 bytes) + var serializedData [12]byte + byteOrder.PutUint32(serializedData[0:4], loc.blockFileNum) + byteOrder.PutUint32(serializedData[4:8], loc.fileOffset) + byteOrder.PutUint32(serializedData[8:12], loc.blockLen) + return serializedData[:] +} + +// blockFilePath return the file path for the provided block file number. +func blockFilePath(dbPath string, fileNum uint32) string { + fileName := fmt.Sprintf(blockFilenameTemplate, fileNum) + return filepath.Join(dbPath, fileName) +} + +// openWriteFile returns a file handle for the passed flat file number in +// read/write mode. The file will be created if needed. It is typically used +// for the current file that will have all new data appended. Unlike openFile, +// this function does not keep track of the open file and it is not subject to +// the maxOpenFiles limit. +func (s *blockStore) openWriteFile(fileNum uint32) (filer, error) { + // The current block file needs to be read-write so it is possible to + // append to it. Also, it shouldn't be part of the least recently used + // file. + filePath := blockFilePath(s.basePath, fileNum) + file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + str := fmt.Sprintf("failed to open file %q: %v", filePath, err) + return nil, makeDbErr(database.ErrDriverSpecific, str, err) + } + + return file, nil +} + +// openFile returns a read-only file handle for the passed flat file number. +// The function also keeps track of the open files, performs least recently +// used tracking, and limits the number of open files to maxOpenFiles by closing +// the least recently used file as needed. +// +// This function MUST be called with the overall files mutex (s.obfMutex) locked +// for WRITES. +func (s *blockStore) openFile(fileNum uint32) (*lockableFile, error) { + // Open the appropriate file as read-only. + filePath := blockFilePath(s.basePath, fileNum) + file, err := os.Open(filePath) + if err != nil { + return nil, makeDbErr(database.ErrDriverSpecific, err.Error(), + err) + } + blockFile := &lockableFile{file: file} + + // Close the least recently used file if the file exceeds the max + // allowed open files. This is not done until after the file open in + // case the file fails to open, there is no need to close any files. + // + // A write lock is required on the LRU list here to protect against + // modifications happening as already open files are read from and + // shuffled to the front of the list. + // + // Also, add the file that was just opened to the front of the least + // recently used list to indicate it is the most recently used file and + // therefore should be closed last. + s.lruMutex.Lock() + lruList := s.openBlocksLRU + if lruList.Len() >= maxOpenFiles { + lruFileNum := lruList.Remove(lruList.Back()).(uint32) + oldBlockFile := s.openBlockFiles[lruFileNum] + + // Close the old file under the write lock for the file in case + // any readers are currently reading from it so it's not closed + // out from under them. + oldBlockFile.Lock() + _ = oldBlockFile.file.Close() + oldBlockFile.Unlock() + + delete(s.openBlockFiles, lruFileNum) + delete(s.fileNumToLRUElem, lruFileNum) + } + s.fileNumToLRUElem[fileNum] = lruList.PushFront(fileNum) + s.lruMutex.Unlock() + + // Store a reference to it in the open block files map. + s.openBlockFiles[fileNum] = blockFile + + return blockFile, nil +} + +// deleteFile removes the block file for the passed flat file number. The file +// must already be closed and it is the responsibility of the caller to do any +// other state cleanup necessary. +func (s *blockStore) deleteFile(fileNum uint32) error { + filePath := blockFilePath(s.basePath, fileNum) + if err := os.Remove(filePath); err != nil { + return makeDbErr(database.ErrDriverSpecific, err.Error(), err) + } + + return nil +} + +// blockFile attempts to return an existing file handle for the passed flat file +// number if it is already open as well as marking it as most recently used. It +// will also open the file when it's not already open subject to the rules +// described in openFile. +// +// NOTE: The returned block file will already have the read lock acquired and +// the caller MUST call .RUnlock() to release it once it has finished all read +// operations. This is necessary because otherwise it would be possible for a +// separate goroutine to close the file after it is returned from here, but +// before the caller has acquired a read lock. +func (s *blockStore) blockFile(fileNum uint32) (*lockableFile, error) { + // When the requested block file is open for writes, return it. + wc := s.writeCursor + wc.RLock() + if fileNum == wc.curFileNum && wc.curFile.file != nil { + obf := wc.curFile + obf.RLock() + wc.RUnlock() + return obf, nil + } + wc.RUnlock() + + // Try to return an open file under the overall files read lock. + s.obfMutex.RLock() + if obf, ok := s.openBlockFiles[fileNum]; ok { + s.lruMutex.Lock() + s.openBlocksLRU.MoveToFront(s.fileNumToLRUElem[fileNum]) + s.lruMutex.Unlock() + + obf.RLock() + s.obfMutex.RUnlock() + return obf, nil + } + s.obfMutex.RUnlock() + + // Since the file isn't open already, need to check the open block files + // map again under write lock in case multiple readers got here and a + // separate one is already opening the file. + s.obfMutex.Lock() + if obf, ok := s.openBlockFiles[fileNum]; ok { + obf.RLock() + s.obfMutex.Unlock() + return obf, nil + } + + // The file isn't open, so open it while potentially closing the least + // recently used one as needed. + obf, err := s.openFileFunc(fileNum) + if err != nil { + s.obfMutex.Unlock() + return nil, err + } + obf.RLock() + s.obfMutex.Unlock() + return obf, nil +} + +// writeData is a helper function for writeBlock which writes the provided data +// at the current write offset and updates the write cursor accordingly. The +// field name parameter is only used when there is an error to provide a nicer +// error message. +// +// The write cursor will be advanced the number of bytes actually written in the +// event of failure. +// +// NOTE: This function MUST be called with the write cursor current file lock +// held and must only be called during a write transaction so it is effectively +// locked for writes. Also, the write cursor current file must NOT be nil. +func (s *blockStore) writeData(data []byte, fieldName string) error { + wc := s.writeCursor + n, err := wc.curFile.file.WriteAt(data, int64(wc.curOffset)) + wc.curOffset += uint32(n) + if err != nil { + str := fmt.Sprintf("failed to write %s to file %d at "+ + "offset %d: %v", fieldName, wc.curFileNum, + wc.curOffset-uint32(n), err) + return makeDbErr(database.ErrDriverSpecific, str, err) + } + + return nil +} + +// writeBlock appends the specified raw block bytes to the store's write cursor +// location and increments it accordingly. When the block would exceed the max +// file size for the current flat file, this function will close the current +// file, create the next file, update the write cursor, and write the block to +// the new file. +// +// The write cursor will also be advanced the number of bytes actually written +// in the event of failure. +// +// Format: <network><block length><serialized block><checksum> +func (s *blockStore) writeBlock(rawBlock []byte) (blockLocation, error) { + // Compute how many bytes will be written. + // 4 bytes each for block network + 4 bytes for block length + + // length of raw block + 4 bytes for checksum. + blockLen := uint32(len(rawBlock)) + fullLen := blockLen + 12 + + // Move to the next block file if adding the new block would exceed the + // max allowed size for the current block file. Also detect overflow + // to be paranoid, even though it isn't possible currently, numbers + // might change in the future to make it possible. + // + // NOTE: The writeCursor.offset field isn't protected by the mutex + // since it's only read/changed during this function which can only be + // called during a write transaction, of which there can be only one at + // a time. + wc := s.writeCursor + finalOffset := wc.curOffset + fullLen + if finalOffset < wc.curOffset || finalOffset > s.maxBlockFileSize { + // This is done under the write cursor lock since the curFileNum + // field is accessed elsewhere by readers. + // + // Close the current write file to force a read-only reopen + // with LRU tracking. The close is done under the write lock + // for the file to prevent it from being closed out from under + // any readers currently reading from it. + wc.Lock() + wc.curFile.Lock() + if wc.curFile.file != nil { + _ = wc.curFile.file.Close() + wc.curFile.file = nil + } + wc.curFile.Unlock() + + // Start writes into next file. + wc.curFileNum++ + wc.curOffset = 0 + wc.Unlock() + } + + // All writes are done under the write lock for the file to ensure any + // readers are finished and blocked first. + wc.curFile.Lock() + defer wc.curFile.Unlock() + + // Open the current file if needed. This will typically only be the + // case when moving to the next file to write to or on initial database + // load. However, it might also be the case if rollbacks happened after + // file writes started during a transaction commit. + if wc.curFile.file == nil { + file, err := s.openWriteFileFunc(wc.curFileNum) + if err != nil { + return blockLocation{}, err + } + wc.curFile.file = file + } + + // Bitcoin network. + origOffset := wc.curOffset + hasher := crc32.New(castagnoli) + var scratch [4]byte + byteOrder.PutUint32(scratch[:], uint32(s.network)) + if err := s.writeData(scratch[:], "network"); err != nil { + return blockLocation{}, err + } + _, _ = hasher.Write(scratch[:]) + + // Block length. + byteOrder.PutUint32(scratch[:], blockLen) + if err := s.writeData(scratch[:], "block length"); err != nil { + return blockLocation{}, err + } + _, _ = hasher.Write(scratch[:]) + + // Serialized block. + if err := s.writeData(rawBlock[:], "block"); err != nil { + return blockLocation{}, err + } + _, _ = hasher.Write(rawBlock) + + // Castagnoli CRC-32 as a checksum of all the previous. + if err := s.writeData(hasher.Sum(nil), "checksum"); err != nil { + return blockLocation{}, err + } + + loc := blockLocation{ + blockFileNum: wc.curFileNum, + fileOffset: origOffset, + blockLen: fullLen, + } + return loc, nil +} + +// readBlock reads the specified block record and returns the serialized block. +// It ensures the integrity of the block data by checking that the serialized +// network matches the current network associated with the block store and +// comparing the calculated checksum against the one stored in the flat file. +// This function also automatically handles all file management such as opening +// and closing files as necessary to stay within the maximum allowed open files +// limit. +// +// Returns ErrDriverSpecific if the data fails to read for any reason and +// ErrCorruption if the checksum of the read data doesn't match the checksum +// read from the file. +// +// Format: <network><block length><serialized block><checksum> +func (s *blockStore) readBlock(hash *wire.ShaHash, loc blockLocation) ([]byte, error) { + // Get the referenced block file handle opening the file as needed. The + // function also handles closing files as needed to avoid going over the + // max allowed open files. + blockFile, err := s.blockFile(loc.blockFileNum) + if err != nil { + return nil, err + } + + serializedData := make([]byte, loc.blockLen) + n, err := blockFile.file.ReadAt(serializedData, int64(loc.fileOffset)) + blockFile.RUnlock() + if err != nil { + str := fmt.Sprintf("failed to read block %s from file %d, "+ + "offset %d: %v", hash, loc.blockFileNum, loc.fileOffset, + err) + return nil, makeDbErr(database.ErrDriverSpecific, str, err) + } + + // Calculate the checksum of the read data and ensure it matches the + // serialized checksum. This will detect any data corruption in the + // flat file without having to do much more expensive merkle root + // calculations on the loaded block. + serializedChecksum := binary.BigEndian.Uint32(serializedData[n-4:]) + calculatedChecksum := crc32.Checksum(serializedData[:n-4], castagnoli) + if serializedChecksum != calculatedChecksum { + str := fmt.Sprintf("block data for block %s checksum "+ + "does not match - got %x, want %x", hash, + calculatedChecksum, serializedChecksum) + return nil, makeDbErr(database.ErrCorruption, str, nil) + } + + // The network associated with the block must match the current active + // network, otherwise somebody probably put the block files for the + // wrong network in the directory. + serializedNet := byteOrder.Uint32(serializedData[:4]) + if serializedNet != uint32(s.network) { + str := fmt.Sprintf("block data for block %s is for the "+ + "wrong network - got %d, want %d", hash, serializedNet, + uint32(s.network)) + return nil, makeDbErr(database.ErrDriverSpecific, str, nil) + } + + // The raw block excludes the network, length of the block, and + // checksum. + return serializedData[8 : n-4], nil +} + +// readBlockRegion reads the specified amount of data at the provided offset for +// a given block location. The offset is relative to the start of the +// serialized block (as opposed to the beginning of the block record). This +// function automatically handles all file management such as opening and +// closing files as necessary to stay within the maximum allowed open files +// limit. +// +// Returns ErrDriverSpecific if the data fails to read for any reason. +func (s *blockStore) readBlockRegion(loc blockLocation, offset, numBytes uint32) ([]byte, error) { + // Get the referenced block file handle opening the file as needed. The + // function also handles closing files as needed to avoid going over the + // max allowed open files. + blockFile, err := s.blockFile(loc.blockFileNum) + if err != nil { + return nil, err + } + + // Regions are offsets into the actual block, however the serialized + // data for a block includes an initial 4 bytes for network + 4 bytes + // for block length. Thus, add 8 bytes to adjust. + readOffset := loc.fileOffset + 8 + offset + serializedData := make([]byte, numBytes) + _, err = blockFile.file.ReadAt(serializedData, int64(readOffset)) + blockFile.RUnlock() + if err != nil { + str := fmt.Sprintf("failed to read region from block file %d, "+ + "offset %d, len %d: %v", loc.blockFileNum, readOffset, + numBytes, err) + return nil, makeDbErr(database.ErrDriverSpecific, str, err) + } + + return serializedData, nil +} + +// syncBlocks performs a file system sync on the flat file associated with the +// store's current write cursor. It is safe to call even when there is not a +// current write file in which case it will have no effect. +// +// This is used when flushing cached metadata updates to disk to ensure all the +// block data is fully written before updating the metadata. This ensures the +// metadata and block data can be properly reconciled in failure scenarios. +func (s *blockStore) syncBlocks() error { + wc := s.writeCursor + wc.RLock() + defer wc.RUnlock() + + // Nothing to do if there is no current file associated with the write + // cursor. + wc.curFile.RLock() + defer wc.curFile.RUnlock() + if wc.curFile.file == nil { + return nil + } + + // Sync the file to disk. + if err := wc.curFile.file.Sync(); err != nil { + str := fmt.Sprintf("failed to sync file %d: %v", wc.curFileNum, + err) + return makeDbErr(database.ErrDriverSpecific, str, err) + } + + return nil +} + +// handleRollback rolls the block files on disk back to the provided file number +// and offset. This involves potentially deleting and truncating the files that +// were partially written. +// +// There are effectively two scenarios to consider here: +// 1) Transient write failures from which recovery is possible +// 2) More permanent failures such as hard disk death and/or removal +// +// In either case, the write cursor will be repositioned to the old block file +// offset regardless of any other errors that occur while attempting to undo +// writes. +// +// For the first scenario, this will lead to any data which failed to be undone +// being overwritten and thus behaves as desired as the system continues to run. +// +// For the second scenario, the metadata which stores the current write cursor +// position within the block files will not have been updated yet and thus if +// the system eventually recovers (perhaps the hard drive is reconnected), it +// will also lead to any data which failed to be undone being overwritten and +// thus behaves as desired. +// +// Therefore, any errors are simply logged at a warning level rather than being +// returned since there is nothing more that could be done about it anyways. +func (s *blockStore) handleRollback(oldBlockFileNum, oldBlockOffset uint32) { + // Grab the write cursor mutex since it is modified throughout this + // function. + wc := s.writeCursor + wc.Lock() + defer wc.Unlock() + + // Nothing to do if the rollback point is the same as the current write + // cursor. + if wc.curFileNum == oldBlockFileNum && wc.curOffset == oldBlockOffset { + return + } + + // Regardless of any failures that happen below, reposition the write + // cursor to the old block file and offset. + defer func() { + wc.curFileNum = oldBlockFileNum + wc.curOffset = oldBlockOffset + }() + + log.Debugf("ROLLBACK: Rolling back to file %d, offset %d", + oldBlockFileNum, oldBlockOffset) + + // Close the current write file if it needs to be deleted. Then delete + // all files that are newer than the provided rollback file while + // also moving the write cursor file backwards accordingly. + if wc.curFileNum > oldBlockFileNum { + wc.curFile.Lock() + if wc.curFile.file != nil { + _ = wc.curFile.file.Close() + wc.curFile.file = nil + } + wc.curFile.Unlock() + } + for ; wc.curFileNum > oldBlockFileNum; wc.curFileNum-- { + if err := s.deleteFileFunc(wc.curFileNum); err != nil { + _ = log.Warnf("ROLLBACK: Failed to delete block file "+ + "number %d: %v", wc.curFileNum, err) + return + } + } + + // Open the file for the current write cursor if needed. + wc.curFile.Lock() + if wc.curFile.file == nil { + obf, err := s.openWriteFileFunc(wc.curFileNum) + if err != nil { + wc.curFile.Unlock() + _ = log.Warnf("ROLLBACK: %v", err) + return + } + wc.curFile.file = obf + } + + // Truncate the to the provided rollback offset. + if err := wc.curFile.file.Truncate(int64(oldBlockOffset)); err != nil { + wc.curFile.Unlock() + _ = log.Warnf("ROLLBACK: Failed to truncate file %d: %v", + wc.curFileNum, err) + return + } + + // Sync the file to disk. + err := wc.curFile.file.Sync() + wc.curFile.Unlock() + if err != nil { + _ = log.Warnf("ROLLBACK: Failed to sync file %d: %v", + wc.curFileNum, err) + return + } + return +} + +// scanBlockFiles searches the database directory for all flat block files to +// find the end of the most recent file. This position is considered the +// current write cursor which is also stored in the metadata. Thus, it is used +// to detect unexpected shutdowns in the middle of writes so the block files +// can be reconciled. +func scanBlockFiles(dbPath string) (int, uint32) { + lastFile := -1 + fileLen := uint32(0) + for i := 0; ; i++ { + filePath := blockFilePath(dbPath, uint32(i)) + st, err := os.Stat(filePath) + if err != nil { + break + } + lastFile = i + + fileLen = uint32(st.Size()) + } + + log.Tracef("Scan found latest block file #%d with length %d", lastFile, + fileLen) + return lastFile, fileLen +} + +// newBlockStore returns a new block store with the current block file number +// and offset set and all fields initialized. +func newBlockStore(basePath string, network wire.BitcoinNet) *blockStore { + // Look for the end of the latest block to file to determine what the + // write cursor position is from the viewpoing of the block files on + // disk. + fileNum, fileOff := scanBlockFiles(basePath) + if fileNum == -1 { + fileNum = 0 + fileOff = 0 + } + + store := &blockStore{ + network: network, + basePath: basePath, + maxBlockFileSize: maxBlockFileSize, + openBlockFiles: make(map[uint32]*lockableFile), + openBlocksLRU: list.New(), + fileNumToLRUElem: make(map[uint32]*list.Element), + + writeCursor: &writeCursor{ + curFile: &lockableFile{}, + curFileNum: uint32(fileNum), + curOffset: uint32(fileOff), + }, + } + store.openFileFunc = store.openFile + store.openWriteFileFunc = store.openWriteFile + store.deleteFileFunc = store.deleteFile + return store +} diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/db.go b/vendor/github.com/btcsuite/btcd/database/ffldb/db.go new file mode 100644 index 0000000000000000000000000000000000000000..caf89dcec6fae6546b435267da066deef871db40 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/db.go @@ -0,0 +1,2083 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package ffldb + +import ( + "bytes" + "encoding/binary" + "fmt" + "os" + "path/filepath" + "runtime" + "sort" + "sync" + + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/database/internal/treap" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/goleveldb/leveldb" + "github.com/btcsuite/goleveldb/leveldb/comparer" + ldberrors "github.com/btcsuite/goleveldb/leveldb/errors" + "github.com/btcsuite/goleveldb/leveldb/filter" + "github.com/btcsuite/goleveldb/leveldb/iterator" + "github.com/btcsuite/goleveldb/leveldb/opt" + "github.com/btcsuite/goleveldb/leveldb/util" +) + +const ( + // metadataDbName is the name used for the metadata database. + metadataDbName = "metadata" + + // blockHdrSize is the size of a block header. This is simply the + // constant from wire and is only provided here for convenience since + // wire.MaxBlockHeaderPayload is quite long. + blockHdrSize = wire.MaxBlockHeaderPayload + + // blockHdrOffset defines the offsets into a block index row for the + // block header. + // + // The serialized block index row format is: + // <blocklocation><blockheader> + blockHdrOffset = blockLocSize +) + +var ( + // byteOrder is the preferred byte order used through the database and + // block files. Sometimes big endian will be used to allow ordered byte + // sortable integer values. + byteOrder = binary.LittleEndian + + // bucketIndexPrefix is the prefix used for all entries in the bucket + // index. + bucketIndexPrefix = []byte("bidx") + + // curBucketIDKeyName is the name of the key used to keep track of the + // current bucket ID counter. + curBucketIDKeyName = []byte("bidx-cbid") + + // metadataBucketID is the ID of the top-level metadata bucket. + // It is the value 0 encoded as an unsigned big-endian uint32. + metadataBucketID = [4]byte{} + + // blockIdxBucketID is the ID of the internal block metadata bucket. + // It is the value 1 encoded as an unsigned big-endian uint32. + blockIdxBucketID = [4]byte{0x00, 0x00, 0x00, 0x01} + + // blockIdxBucketName is the bucket used internally to track block + // metadata. + blockIdxBucketName = []byte("ffldb-blockidx") + + // writeLocKeyName is the key used to store the current write file + // location. + writeLocKeyName = []byte("ffldb-writeloc") +) + +// Common error strings. +const ( + // errDbNotOpenStr is the text to use for the database.ErrDbNotOpen + // error code. + errDbNotOpenStr = "database is not open" + + // errTxClosedStr is the text to use for the database.ErrTxClosed error + // code. + errTxClosedStr = "database tx is closed" +) + +// bulkFetchData is allows a block location to be specified along with the +// index it was requested from. This in turn allows the bulk data loading +// functions to sort the data accesses based on the location to improve +// performance while keeping track of which result the data is for. +type bulkFetchData struct { + *blockLocation + replyIndex int +} + +// bulkFetchDataSorter implements sort.Interface to allow a slice of +// bulkFetchData to be sorted. In particular it sorts by file and then +// offset so that reads from files are grouped and linear. +type bulkFetchDataSorter []bulkFetchData + +// Len returns the number of items in the slice. It is part of the +// sort.Interface implementation. +func (s bulkFetchDataSorter) Len() int { + return len(s) +} + +// Swap swaps the items at the passed indices. It is part of the +// sort.Interface implementation. +func (s bulkFetchDataSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Less returns whether the item with index i should sort before the item with +// index j. It is part of the sort.Interface implementation. +func (s bulkFetchDataSorter) Less(i, j int) bool { + if s[i].blockFileNum < s[j].blockFileNum { + return true + } + if s[i].blockFileNum > s[j].blockFileNum { + return false + } + + return s[i].fileOffset < s[j].fileOffset +} + +// makeDbErr creates a database.Error given a set of arguments. +func makeDbErr(c database.ErrorCode, desc string, err error) database.Error { + return database.Error{ErrorCode: c, Description: desc, Err: err} +} + +// convertErr converts the passed leveldb error into a database error with an +// equivalent error code and the passed description. It also sets the passed +// error as the underlying error. +func convertErr(desc string, ldbErr error) database.Error { + // Use the driver-specific error code by default. The code below will + // update this with the converted error if it's recognized. + var code = database.ErrDriverSpecific + + switch { + // Database corruption errors. + case ldberrors.IsCorrupted(ldbErr): + code = database.ErrCorruption + + // Database open/create errors. + case ldbErr == leveldb.ErrClosed: + code = database.ErrDbNotOpen + + // Transaction errors. + case ldbErr == leveldb.ErrSnapshotReleased: + code = database.ErrTxClosed + case ldbErr == leveldb.ErrIterReleased: + code = database.ErrTxClosed + } + + return database.Error{ErrorCode: code, Description: desc, Err: ldbErr} +} + +// copySlice returns a copy of the passed slice. This is mostly used to copy +// leveldb iterator keys and values since they are only valid until the iterator +// is moved instead of during the entirety of the transaction. +func copySlice(slice []byte) []byte { + ret := make([]byte, len(slice)) + copy(ret, slice) + return ret +} + +// cursor is an internal type used to represent a cursor over key/value pairs +// and nested buckets of a bucket and implements the database.Cursor interface. +type cursor struct { + bucket *bucket + dbIter iterator.Iterator + pendingIter iterator.Iterator + currentIter iterator.Iterator +} + +// Enforce cursor implements the database.Cursor interface. +var _ database.Cursor = (*cursor)(nil) + +// Bucket returns the bucket the cursor was created for. +// +// This function is part of the database.Cursor interface implementation. +func (c *cursor) Bucket() database.Bucket { + // Ensure transaction state is valid. + if err := c.bucket.tx.checkClosed(); err != nil { + return nil + } + + return c.bucket +} + +// Delete removes the current key/value pair the cursor is at without +// invalidating the cursor. +// +// Returns the following errors as required by the interface contract: +// - ErrIncompatibleValue if attempted when the cursor points to a nested +// bucket +// - ErrTxNotWritable if attempted against a read-only transaction +// - ErrTxClosed if the transaction has already been closed +// +// This function is part of the database.Cursor interface implementation. +func (c *cursor) Delete() error { + // Ensure transaction state is valid. + if err := c.bucket.tx.checkClosed(); err != nil { + return err + } + + // Error if the cursor is exhausted. + if c.currentIter == nil { + str := "cursor is exhausted" + return makeDbErr(database.ErrIncompatibleValue, str, nil) + } + + // Do not allow buckets to be deleted via the cursor. + key := c.currentIter.Key() + if bytes.HasPrefix(key, bucketIndexPrefix) { + str := "buckets may not be deleted from a cursor" + return makeDbErr(database.ErrIncompatibleValue, str, nil) + } + + c.bucket.tx.deleteKey(copySlice(key), true) + return nil +} + +// skipPendingUpdates skips any keys at the current database iterator position +// that are being updated by the transaction. The forwards flag indicates the +// direction the cursor is moving. +func (c *cursor) skipPendingUpdates(forwards bool) { + for c.dbIter.Valid() { + var skip bool + key := c.dbIter.Key() + if c.bucket.tx.pendingRemove.Has(key) { + skip = true + } else if c.bucket.tx.pendingKeys.Has(key) { + skip = true + } + if !skip { + break + } + + if forwards { + c.dbIter.Next() + } else { + c.dbIter.Prev() + } + } +} + +// chooseIterator first skips any entries in the database iterator that are +// being updated by the transaction and sets the current iterator to the +// appropriate iterator depending on their validity and the order they compare +// in while taking into account the direction flag. When the cursor is being +// moved forwards and both iterators are valid, the iterator with the smaller +// key is chosen and vice versa when the cursor is being moved backwards. +func (c *cursor) chooseIterator(forwards bool) bool { + // Skip any keys at the current database iterator position that are + // being updated by the transaction. + c.skipPendingUpdates(forwards) + + // When both iterators are exhausted, the cursor is exhausted too. + if !c.dbIter.Valid() && !c.pendingIter.Valid() { + c.currentIter = nil + return false + } + + // Choose the database iterator when the pending keys iterator is + // exhausted. + if !c.pendingIter.Valid() { + c.currentIter = c.dbIter + return true + } + + // Choose the pending keys iterator when the database iterator is + // exhausted. + if !c.dbIter.Valid() { + c.currentIter = c.pendingIter + return true + } + + // Both iterators are valid, so choose the iterator with either the + // smaller or larger key depending on the forwards flag. + compare := bytes.Compare(c.dbIter.Key(), c.pendingIter.Key()) + if (forwards && compare > 0) || (!forwards && compare < 0) { + c.currentIter = c.pendingIter + } else { + c.currentIter = c.dbIter + } + return true +} + +// First positions the cursor at the first key/value pair and returns whether or +// not the pair exists. +// +// This function is part of the database.Cursor interface implementation. +func (c *cursor) First() bool { + // Ensure transaction state is valid. + if err := c.bucket.tx.checkClosed(); err != nil { + return false + } + + // Seek to the first key in both the database and pending iterators and + // choose the iterator that is both valid and has the smaller key. + c.dbIter.First() + c.pendingIter.First() + return c.chooseIterator(true) +} + +// Last positions the cursor at the last key/value pair and returns whether or +// not the pair exists. +// +// This function is part of the database.Cursor interface implementation. +func (c *cursor) Last() bool { + // Ensure transaction state is valid. + if err := c.bucket.tx.checkClosed(); err != nil { + return false + } + + // Seek to the last key in both the database and pending iterators and + // choose the iterator that is both valid and has the larger key. + c.dbIter.Last() + c.pendingIter.Last() + return c.chooseIterator(false) +} + +// Next moves the cursor one key/value pair forward and returns whether or not +// the pair exists. +// +// This function is part of the database.Cursor interface implementation. +func (c *cursor) Next() bool { + // Ensure transaction state is valid. + if err := c.bucket.tx.checkClosed(); err != nil { + return false + } + + // Nothing to return if cursor is exhausted. + if c.currentIter == nil { + return false + } + + // Move the current iterator to the next entry and choose the iterator + // that is both valid and has the smaller key. + c.currentIter.Next() + return c.chooseIterator(true) +} + +// Prev moves the cursor one key/value pair backward and returns whether or not +// the pair exists. +// +// This function is part of the database.Cursor interface implementation. +func (c *cursor) Prev() bool { + // Ensure transaction state is valid. + if err := c.bucket.tx.checkClosed(); err != nil { + return false + } + + // Nothing to return if cursor is exhausted. + if c.currentIter == nil { + return false + } + + // Move the current iterator to the previous entry and choose the + // iterator that is both valid and has the larger key. + c.currentIter.Prev() + return c.chooseIterator(false) +} + +// Seek positions the cursor at the first key/value pair that is greater than or +// equal to the passed seek key. Returns false if no suitable key was found. +// +// This function is part of the database.Cursor interface implementation. +func (c *cursor) Seek(seek []byte) bool { + // Ensure transaction state is valid. + if err := c.bucket.tx.checkClosed(); err != nil { + return false + } + + // Seek to the provided key in both the database and pending iterators + // then choose the iterator that is both valid and has the larger key. + seekKey := bucketizedKey(c.bucket.id, seek) + c.dbIter.Seek(seekKey) + c.pendingIter.Seek(seekKey) + return c.chooseIterator(true) +} + +// rawKey returns the current key the cursor is pointing to without stripping +// the current bucket prefix or bucket index prefix. +func (c *cursor) rawKey() []byte { + // Nothing to return if cursor is exhausted. + if c.currentIter == nil { + return nil + } + + return copySlice(c.currentIter.Key()) +} + +// Key returns the current key the cursor is pointing to. +// +// This function is part of the database.Cursor interface implementation. +func (c *cursor) Key() []byte { + // Ensure transaction state is valid. + if err := c.bucket.tx.checkClosed(); err != nil { + return nil + } + + // Nothing to return if cursor is exhausted. + if c.currentIter == nil { + return nil + } + + // Slice out the actual key name and make a copy since it is no longer + // valid after iterating to the next item. + // + // The key is after the bucket index prefix and parent ID when the + // cursor is pointing to a nested bucket. + key := c.currentIter.Key() + if bytes.HasPrefix(key, bucketIndexPrefix) { + key = key[len(bucketIndexPrefix)+4:] + return copySlice(key) + } + + // The key is after the bucket ID when the cursor is pointing to a + // normal entry. + key = key[len(c.bucket.id):] + return copySlice(key) +} + +// rawValue returns the current value the cursor is pointing to without +// stripping without filtering bucket index values. +func (c *cursor) rawValue() []byte { + // Nothing to return if cursor is exhausted. + if c.currentIter == nil { + return nil + } + + return copySlice(c.currentIter.Value()) +} + +// Value returns the current value the cursor is pointing to. This will be nil +// for nested buckets. +// +// This function is part of the database.Cursor interface implementation. +func (c *cursor) Value() []byte { + // Ensure transaction state is valid. + if err := c.bucket.tx.checkClosed(); err != nil { + return nil + } + + // Nothing to return if cursor is exhausted. + if c.currentIter == nil { + return nil + } + + // Return nil for the value when the cursor is pointing to a nested + // bucket. + if bytes.HasPrefix(c.currentIter.Key(), bucketIndexPrefix) { + return nil + } + + return copySlice(c.currentIter.Value()) +} + +// cursorType defines the type of cursor to create. +type cursorType int + +// The following constants define the allowed cursor types. +const ( + // ctKeys iterates through all of the keys in a given bucket. + ctKeys cursorType = iota + + // ctBuckets iterates through all directly nested buckets in a given + // bucket. + ctBuckets + + // ctFull iterates through both the keys and the directly nested buckets + // in a given bucket. + ctFull +) + +// cursorFinalizer is either invoked when a cursor is being garbage collected or +// called manually to ensure the underlying cursor iterators are released. +func cursorFinalizer(c *cursor) { + c.dbIter.Release() + c.pendingIter.Release() +} + +// newCursor returns a new cursor for the given bucket, bucket ID, and cursor +// type. +// +// NOTE: The caller is responsible for calling the cursorFinalizer function on +// the returned cursor. +func newCursor(b *bucket, bucketID []byte, cursorTyp cursorType) *cursor { + var dbIter, pendingIter iterator.Iterator + switch cursorTyp { + case ctKeys: + keyRange := util.BytesPrefix(bucketID) + dbIter = b.tx.snapshot.NewIterator(keyRange) + pendingKeyIter := newLdbTreapIter(b.tx, keyRange) + pendingIter = pendingKeyIter + + case ctBuckets: + // The serialized bucket index key format is: + // <bucketindexprefix><parentbucketid><bucketname> + + // Create an iterator for the both the database and the pending + // keys which are prefixed by the bucket index identifier and + // the provided bucket ID. + prefix := make([]byte, len(bucketIndexPrefix)+4) + copy(prefix, bucketIndexPrefix) + copy(prefix[len(bucketIndexPrefix):], bucketID) + bucketRange := util.BytesPrefix(prefix) + + dbIter = b.tx.snapshot.NewIterator(bucketRange) + pendingBucketIter := newLdbTreapIter(b.tx, bucketRange) + pendingIter = pendingBucketIter + + case ctFull: + fallthrough + default: + // The serialized bucket index key format is: + // <bucketindexprefix><parentbucketid><bucketname> + prefix := make([]byte, len(bucketIndexPrefix)+4) + copy(prefix, bucketIndexPrefix) + copy(prefix[len(bucketIndexPrefix):], bucketID) + bucketRange := util.BytesPrefix(prefix) + keyRange := util.BytesPrefix(bucketID) + + // Since both keys and buckets are needed from the database, + // create an individual iterator for each prefix and then create + // a merged iterator from them. + dbKeyIter := b.tx.snapshot.NewIterator(keyRange) + dbBucketIter := b.tx.snapshot.NewIterator(bucketRange) + iters := []iterator.Iterator{dbKeyIter, dbBucketIter} + dbIter = iterator.NewMergedIterator(iters, + comparer.DefaultComparer, true) + + // Since both keys and buckets are needed from the pending keys, + // create an individual iterator for each prefix and then create + // a merged iterator from them. + pendingKeyIter := newLdbTreapIter(b.tx, keyRange) + pendingBucketIter := newLdbTreapIter(b.tx, bucketRange) + iters = []iterator.Iterator{pendingKeyIter, pendingBucketIter} + pendingIter = iterator.NewMergedIterator(iters, + comparer.DefaultComparer, true) + } + + // Create the cursor using the iterators. + return &cursor{bucket: b, dbIter: dbIter, pendingIter: pendingIter} +} + +// bucket is an internal type used to represent a collection of key/value pairs +// and implements the database.Bucket interface. +type bucket struct { + tx *transaction + id [4]byte +} + +// Enforce bucket implements the database.Bucket interface. +var _ database.Bucket = (*bucket)(nil) + +// bucketIndexKey returns the actual key to use for storing and retrieving a +// child bucket in the bucket index. This is required because additional +// information is needed to distinguish nested buckets with the same name. +func bucketIndexKey(parentID [4]byte, key []byte) []byte { + // The serialized bucket index key format is: + // <bucketindexprefix><parentbucketid><bucketname> + indexKey := make([]byte, len(bucketIndexPrefix)+4+len(key)) + copy(indexKey, bucketIndexPrefix) + copy(indexKey[len(bucketIndexPrefix):], parentID[:]) + copy(indexKey[len(bucketIndexPrefix)+4:], key) + return indexKey +} + +// bucketizedKey returns the actual key to use for storing and retrieving a key +// for the provided bucket ID. This is required because bucketizing is handled +// through the use of a unique prefix per bucket. +func bucketizedKey(bucketID [4]byte, key []byte) []byte { + // The serialized block index key format is: + // <bucketid><key> + bKey := make([]byte, 4+len(key)) + copy(bKey, bucketID[:]) + copy(bKey[4:], key) + return bKey +} + +// Bucket retrieves a nested bucket with the given key. Returns nil if +// the bucket does not exist. +// +// This function is part of the database.Bucket interface implementation. +func (b *bucket) Bucket(key []byte) database.Bucket { + // Ensure transaction state is valid. + if err := b.tx.checkClosed(); err != nil { + return nil + } + + // Attempt to fetch the ID for the child bucket. The bucket does not + // exist if the bucket index entry does not exist. + childID := b.tx.fetchKey(bucketIndexKey(b.id, key)) + if childID == nil { + return nil + } + + childBucket := &bucket{tx: b.tx} + copy(childBucket.id[:], childID) + return childBucket +} + +// CreateBucket creates and returns a new nested bucket with the given key. +// +// Returns the following errors as required by the interface contract: +// - ErrBucketExists if the bucket already exists +// - ErrBucketNameRequired if the key is empty +// - ErrIncompatibleValue if the key is otherwise invalid for the particular +// implementation +// - ErrTxNotWritable if attempted against a read-only transaction +// - ErrTxClosed if the transaction has already been closed +// +// This function is part of the database.Bucket interface implementation. +func (b *bucket) CreateBucket(key []byte) (database.Bucket, error) { + // Ensure transaction state is valid. + if err := b.tx.checkClosed(); err != nil { + return nil, err + } + + // Ensure the transaction is writable. + if !b.tx.writable { + str := "create bucket requires a writable database transaction" + return nil, makeDbErr(database.ErrTxNotWritable, str, nil) + } + + // Ensure a key was provided. + if len(key) == 0 { + str := "create bucket requires a key" + return nil, makeDbErr(database.ErrBucketNameRequired, str, nil) + } + + // Ensure bucket does not already exist. + bidxKey := bucketIndexKey(b.id, key) + if b.tx.hasKey(bidxKey) { + str := "bucket already exists" + return nil, makeDbErr(database.ErrBucketExists, str, nil) + } + + // Find the appropriate next bucket ID to use for the new bucket. In + // the case of the special internal block index, keep the fixed ID. + var childID [4]byte + if b.id == metadataBucketID && bytes.Equal(key, blockIdxBucketName) { + childID = blockIdxBucketID + } else { + var err error + childID, err = b.tx.nextBucketID() + if err != nil { + return nil, err + } + } + + // Add the new bucket to the bucket index. + if err := b.tx.putKey(bidxKey, childID[:]); err != nil { + str := fmt.Sprintf("failed to create bucket with key %q", key) + return nil, convertErr(str, err) + } + return &bucket{tx: b.tx, id: childID}, nil +} + +// CreateBucketIfNotExists creates and returns a new nested bucket with the +// given key if it does not already exist. +// +// Returns the following errors as required by the interface contract: +// - ErrBucketNameRequired if the key is empty +// - ErrIncompatibleValue if the key is otherwise invalid for the particular +// implementation +// - ErrTxNotWritable if attempted against a read-only transaction +// - ErrTxClosed if the transaction has already been closed +// +// This function is part of the database.Bucket interface implementation. +func (b *bucket) CreateBucketIfNotExists(key []byte) (database.Bucket, error) { + // Ensure transaction state is valid. + if err := b.tx.checkClosed(); err != nil { + return nil, err + } + + // Ensure the transaction is writable. + if !b.tx.writable { + str := "create bucket requires a writable database transaction" + return nil, makeDbErr(database.ErrTxNotWritable, str, nil) + } + + // Return existing bucket if it already exists, otherwise create it. + if bucket := b.Bucket(key); bucket != nil { + return bucket, nil + } + return b.CreateBucket(key) +} + +// DeleteBucket removes a nested bucket with the given key. +// +// Returns the following errors as required by the interface contract: +// - ErrBucketNotFound if the specified bucket does not exist +// - ErrTxNotWritable if attempted against a read-only transaction +// - ErrTxClosed if the transaction has already been closed +// +// This function is part of the database.Bucket interface implementation. +func (b *bucket) DeleteBucket(key []byte) error { + // Ensure transaction state is valid. + if err := b.tx.checkClosed(); err != nil { + return err + } + + // Ensure the transaction is writable. + if !b.tx.writable { + str := "delete bucket requires a writable database transaction" + return makeDbErr(database.ErrTxNotWritable, str, nil) + } + + // Attempt to fetch the ID for the child bucket. The bucket does not + // exist if the bucket index entry does not exist. In the case of the + // special internal block index, keep the fixed ID. + bidxKey := bucketIndexKey(b.id, key) + childID := b.tx.fetchKey(bidxKey) + if childID == nil { + str := fmt.Sprintf("bucket %q does not exist", key) + return makeDbErr(database.ErrBucketNotFound, str, nil) + } + + // Remove all nested buckets and their keys. + childIDs := [][]byte{childID} + for len(childIDs) > 0 { + childID = childIDs[len(childIDs)-1] + childIDs = childIDs[:len(childIDs)-1] + + // Delete all keys in the nested bucket. + keyCursor := newCursor(b, childID, ctKeys) + for ok := keyCursor.First(); ok; ok = keyCursor.Next() { + b.tx.deleteKey(keyCursor.rawKey(), false) + } + cursorFinalizer(keyCursor) + + // Iterate through all nested buckets. + bucketCursor := newCursor(b, childID, ctBuckets) + for ok := bucketCursor.First(); ok; ok = bucketCursor.Next() { + // Push the id of the nested bucket onto the stack for + // the next iteration. + childID := bucketCursor.rawValue() + childIDs = append(childIDs, childID) + + // Remove the nested bucket from the bucket index. + b.tx.deleteKey(bucketCursor.rawKey(), false) + } + cursorFinalizer(bucketCursor) + } + + // Remove the nested bucket from the bucket index. Any buckets nested + // under it were already removed above. + b.tx.deleteKey(bidxKey, true) + return nil +} + +// Cursor returns a new cursor, allowing for iteration over the bucket's +// key/value pairs and nested buckets in forward or backward order. +// +// You must seek to a position using the First, Last, or Seek functions before +// calling the Next, Prev, Key, or Value functions. Failure to do so will +// result in the same return values as an exhausted cursor, which is false for +// the Prev and Next functions and nil for Key and Value functions. +// +// This function is part of the database.Bucket interface implementation. +func (b *bucket) Cursor() database.Cursor { + // Ensure transaction state is valid. + if err := b.tx.checkClosed(); err != nil { + return &cursor{bucket: b} + } + + // Create the cursor and setup a runtime finalizer to ensure the + // iterators are released when the cursor is garbage collected. + c := newCursor(b, b.id[:], ctFull) + runtime.SetFinalizer(c, cursorFinalizer) + return c +} + +// ForEach invokes the passed function with every key/value pair in the bucket. +// This does not include nested buckets or the key/value pairs within those +// nested buckets. +// +// WARNING: It is not safe to mutate data while iterating with this method. +// Doing so may cause the underlying cursor to be invalidated and return +// unexpected keys and/or values. +// +// Returns the following errors as required by the interface contract: +// - ErrTxClosed if the transaction has already been closed +// +// NOTE: The values returned by this function are only valid during a +// transaction. Attempting to access them after a transaction has ended will +// likely result in an access violation. +// +// This function is part of the database.Bucket interface implementation. +func (b *bucket) ForEach(fn func(k, v []byte) error) error { + // Ensure transaction state is valid. + if err := b.tx.checkClosed(); err != nil { + return err + } + + // Invoke the callback for each cursor item. Return the error returned + // from the callback when it is non-nil. + c := newCursor(b, b.id[:], ctKeys) + defer cursorFinalizer(c) + for ok := c.First(); ok; ok = c.Next() { + err := fn(c.Key(), c.Value()) + if err != nil { + return err + } + } + + return nil +} + +// ForEachBucket invokes the passed function with the key of every nested bucket +// in the current bucket. This does not include any nested buckets within those +// nested buckets. +// +// WARNING: It is not safe to mutate data while iterating with this method. +// Doing so may cause the underlying cursor to be invalidated and return +// unexpected keys. +// +// Returns the following errors as required by the interface contract: +// - ErrTxClosed if the transaction has already been closed +// +// NOTE: The values returned by this function are only valid during a +// transaction. Attempting to access them after a transaction has ended will +// likely result in an access violation. +// +// This function is part of the database.Bucket interface implementation. +func (b *bucket) ForEachBucket(fn func(k []byte) error) error { + // Ensure transaction state is valid. + if err := b.tx.checkClosed(); err != nil { + return err + } + + // Invoke the callback for each cursor item. Return the error returned + // from the callback when it is non-nil. + c := newCursor(b, b.id[:], ctBuckets) + defer cursorFinalizer(c) + for ok := c.First(); ok; ok = c.Next() { + err := fn(c.Key()) + if err != nil { + return err + } + } + + return nil +} + +// Writable returns whether or not the bucket is writable. +// +// This function is part of the database.Bucket interface implementation. +func (b *bucket) Writable() bool { + return b.tx.writable +} + +// Put saves the specified key/value pair to the bucket. Keys that do not +// already exist are added and keys that already exist are overwritten. +// +// Returns the following errors as required by the interface contract: +// - ErrKeyRequired if the key is empty +// - ErrIncompatibleValue if the key is the same as an existing bucket +// - ErrTxNotWritable if attempted against a read-only transaction +// - ErrTxClosed if the transaction has already been closed +// +// This function is part of the database.Bucket interface implementation. +func (b *bucket) Put(key, value []byte) error { + // Ensure transaction state is valid. + if err := b.tx.checkClosed(); err != nil { + return err + } + + // Ensure the transaction is writable. + if !b.tx.writable { + str := "setting a key requires a writable database transaction" + return makeDbErr(database.ErrTxNotWritable, str, nil) + } + + // Ensure a key was provided. + if len(key) == 0 { + str := "put requires a key" + return makeDbErr(database.ErrKeyRequired, str, nil) + } + + return b.tx.putKey(bucketizedKey(b.id, key), value) +} + +// Get returns the value for the given key. Returns nil if the key does not +// exist in this bucket. An empty slice is returned for keys that exist but +// have no value assigned. +// +// NOTE: The value returned by this function is only valid during a transaction. +// Attempting to access it after a transaction has ended results in undefined +// behavior. Additionally, the value must NOT be modified by the caller. +// +// This function is part of the database.Bucket interface implementation. +func (b *bucket) Get(key []byte) []byte { + // Ensure transaction state is valid. + if err := b.tx.checkClosed(); err != nil { + return nil + } + + // Nothing to return if there is no key. + if len(key) == 0 { + return nil + } + + return b.tx.fetchKey(bucketizedKey(b.id, key)) +} + +// Delete removes the specified key from the bucket. Deleting a key that does +// not exist does not return an error. +// +// Returns the following errors as required by the interface contract: +// - ErrKeyRequired if the key is empty +// - ErrIncompatibleValue if the key is the same as an existing bucket +// - ErrTxNotWritable if attempted against a read-only transaction +// - ErrTxClosed if the transaction has already been closed +// +// This function is part of the database.Bucket interface implementation. +func (b *bucket) Delete(key []byte) error { + // Ensure transaction state is valid. + if err := b.tx.checkClosed(); err != nil { + return err + } + + // Ensure the transaction is writable. + if !b.tx.writable { + str := "deleting a value requires a writable database transaction" + return makeDbErr(database.ErrTxNotWritable, str, nil) + } + + // Nothing to do if there is no key. + if len(key) == 0 { + return nil + } + + b.tx.deleteKey(bucketizedKey(b.id, key), true) + return nil +} + +// pendingBlock houses a block that will be written to disk when the database +// transaction is committed. +type pendingBlock struct { + hash *wire.ShaHash + bytes []byte +} + +// transaction represents a database transaction. It can either be read-only or +// read-write and implements the database.Bucket interface. The transaction +// provides a root bucket against which all read and writes occur. +type transaction struct { + managed bool // Is the transaction managed? + closed bool // Is the transaction closed? + writable bool // Is the transaction writable? + db *db // DB instance the tx was created from. + snapshot *dbCacheSnapshot // Underlying snapshot for txns. + metaBucket *bucket // The root metadata bucket. + blockIdxBucket *bucket // The block index bucket. + + // Blocks that need to be stored on commit. The pendingBlocks map is + // kept to allow quick lookups of pending data by block hash. + pendingBlocks map[wire.ShaHash]int + pendingBlockData []pendingBlock + + // Keys that need to be stored or deleted on commit. + pendingKeys *treap.Mutable + pendingRemove *treap.Mutable + + // Active iterators that need to be notified when the pending keys have + // been updated so the cursors can properly handle updates to the + // transaction state. + activeIterLock sync.RWMutex + activeIters []*treap.Iterator +} + +// Enforce transaction implements the database.Tx interface. +var _ database.Tx = (*transaction)(nil) + +// removeActiveIter removes the passed iterator from the list of active +// iterators against the pending keys treap. +func (tx *transaction) removeActiveIter(iter *treap.Iterator) { + // An indexing for loop is intentionally used over a range here as range + // does not reevaluate the slice on each iteration nor does it adjust + // the index for the modified slice. + tx.activeIterLock.Lock() + for i := 0; i < len(tx.activeIters); i++ { + if tx.activeIters[i] == iter { + copy(tx.activeIters[i:], tx.activeIters[i+1:]) + tx.activeIters[len(tx.activeIters)-1] = nil + tx.activeIters = tx.activeIters[:len(tx.activeIters)-1] + } + } + tx.activeIterLock.Unlock() +} + +// addActiveIter adds the passed iterator to the list of active iterators for +// the pending keys treap. +func (tx *transaction) addActiveIter(iter *treap.Iterator) { + tx.activeIterLock.Lock() + tx.activeIters = append(tx.activeIters, iter) + tx.activeIterLock.Unlock() +} + +// notifyActiveIters notifies all of the active iterators for the pending keys +// treap that it has been updated. +func (tx *transaction) notifyActiveIters() { + tx.activeIterLock.RLock() + for _, iter := range tx.activeIters { + iter.ForceReseek() + } + tx.activeIterLock.RUnlock() +} + +// checkClosed returns an error if the the database or transaction is closed. +func (tx *transaction) checkClosed() error { + // The transaction is no longer valid if it has been closed. + if tx.closed { + return makeDbErr(database.ErrTxClosed, errTxClosedStr, nil) + } + + return nil +} + +// hasKey returns whether or not the provided key exists in the database while +// taking into account the current transaction state. +func (tx *transaction) hasKey(key []byte) bool { + // When the transaction is writable, check the pending transaction + // state first. + if tx.writable { + if tx.pendingRemove.Has(key) { + return false + } + if tx.pendingKeys.Has(key) { + return true + } + } + + // Consult the database cache and underlying database. + return tx.snapshot.Has(key) +} + +// putKey adds the provided key to the list of keys to be updated in the +// database when the transaction is committed. +// +// NOTE: This function must only be called on a writable transaction. Since it +// is an internal helper function, it does not check. +func (tx *transaction) putKey(key, value []byte) error { + // Prevent the key from being deleted if it was previously scheduled + // to be deleted on transaction commit. + tx.pendingRemove.Delete(key) + + // Add the key/value pair to the list to be written on transaction + // commit. + tx.pendingKeys.Put(key, value) + tx.notifyActiveIters() + return nil +} + +// fetchKey attempts to fetch the provided key from the database cache (and +// hence underlying database) while taking into account the current transaction +// state. Returns nil if the key does not exist. +func (tx *transaction) fetchKey(key []byte) []byte { + // When the transaction is writable, check the pending transaction + // state first. + if tx.writable { + if tx.pendingRemove.Has(key) { + return nil + } + if value := tx.pendingKeys.Get(key); value != nil { + return value + } + } + + // Consult the database cache and underlying database. + return tx.snapshot.Get(key) +} + +// deleteKey adds the provided key to the list of keys to be deleted from the +// database when the transaction is committed. The notify iterators flag is +// useful to delay notifying iterators about the changes during bulk deletes. +// +// NOTE: This function must only be called on a writable transaction. Since it +// is an internal helper function, it does not check. +func (tx *transaction) deleteKey(key []byte, notifyIterators bool) { + // Remove the key from the list of pendings keys to be written on + // transaction commit if needed. + tx.pendingKeys.Delete(key) + + // Add the key to the list to be deleted on transaction commit. + tx.pendingRemove.Put(key, nil) + + // Notify the active iterators about the change if the flag is set. + if notifyIterators { + tx.notifyActiveIters() + } +} + +// nextBucketID returns the next bucket ID to use for creating a new bucket. +// +// NOTE: This function must only be called on a writable transaction. Since it +// is an internal helper function, it does not check. +func (tx *transaction) nextBucketID() ([4]byte, error) { + // Load the currently highest used bucket ID. + curIDBytes := tx.fetchKey(curBucketIDKeyName) + curBucketNum := binary.BigEndian.Uint32(curIDBytes) + + // Increment and update the current bucket ID and return it. + var nextBucketID [4]byte + binary.BigEndian.PutUint32(nextBucketID[:], curBucketNum+1) + if err := tx.putKey(curBucketIDKeyName, nextBucketID[:]); err != nil { + return [4]byte{}, err + } + return nextBucketID, nil +} + +// Metadata returns the top-most bucket for all metadata storage. +// +// This function is part of the database.Tx interface implementation. +func (tx *transaction) Metadata() database.Bucket { + return tx.metaBucket +} + +// hasBlock returns whether or not a block with the given hash exists. +func (tx *transaction) hasBlock(hash *wire.ShaHash) bool { + // Return true if the block is pending to be written on commit since + // it exists from the viewpoint of this transaction. + if _, exists := tx.pendingBlocks[*hash]; exists { + return true + } + + return tx.hasKey(bucketizedKey(blockIdxBucketID, hash[:])) +} + +// StoreBlock stores the provided block into the database. There are no checks +// to ensure the block connects to a previous block, contains double spends, or +// any additional functionality such as transaction indexing. It simply stores +// the block in the database. +// +// Returns the following errors as required by the interface contract: +// - ErrBlockExists when the block hash already exists +// - ErrTxNotWritable if attempted against a read-only transaction +// - ErrTxClosed if the transaction has already been closed +// +// This function is part of the database.Tx interface implementation. +func (tx *transaction) StoreBlock(block *btcutil.Block) error { + // Ensure transaction state is valid. + if err := tx.checkClosed(); err != nil { + return err + } + + // Ensure the transaction is writable. + if !tx.writable { + str := "store block requires a writable database transaction" + return makeDbErr(database.ErrTxNotWritable, str, nil) + } + + // Reject the block if it already exists. + blockHash := block.Sha() + if tx.hasBlock(blockHash) { + str := fmt.Sprintf("block %s already exists", blockHash) + return makeDbErr(database.ErrBlockExists, str, nil) + } + + blockBytes, err := block.Bytes() + if err != nil { + str := fmt.Sprintf("failed to get serialized bytes for block %s", + blockHash) + return makeDbErr(database.ErrDriverSpecific, str, err) + } + + // Add the block to be stored to the list of pending blocks to store + // when the transaction is committed. Also, add it to pending blocks + // map so it is easy to determine the block is pending based on the + // block hash. + if tx.pendingBlocks == nil { + tx.pendingBlocks = make(map[wire.ShaHash]int) + } + tx.pendingBlocks[*blockHash] = len(tx.pendingBlockData) + tx.pendingBlockData = append(tx.pendingBlockData, pendingBlock{ + hash: blockHash, + bytes: blockBytes, + }) + log.Tracef("Added block %s to pending blocks", blockHash) + + return nil +} + +// HasBlock returns whether or not a block with the given hash exists in the +// database. +// +// Returns the following errors as required by the interface contract: +// - ErrTxClosed if the transaction has already been closed +// +// This function is part of the database.Tx interface implementation. +func (tx *transaction) HasBlock(hash *wire.ShaHash) (bool, error) { + // Ensure transaction state is valid. + if err := tx.checkClosed(); err != nil { + return false, err + } + + return tx.hasBlock(hash), nil +} + +// HasBlocks returns whether or not the blocks with the provided hashes +// exist in the database. +// +// Returns the following errors as required by the interface contract: +// - ErrTxClosed if the transaction has already been closed +// +// This function is part of the database.Tx interface implementation. +func (tx *transaction) HasBlocks(hashes []wire.ShaHash) ([]bool, error) { + // Ensure transaction state is valid. + if err := tx.checkClosed(); err != nil { + return nil, err + } + + results := make([]bool, len(hashes)) + for i := range hashes { + results[i] = tx.hasBlock(&hashes[i]) + } + + return results, nil +} + +// fetchBlockRow fetches the metadata stored in the block index for the provided +// hash. It will return ErrBlockNotFound if there is no entry. +func (tx *transaction) fetchBlockRow(hash *wire.ShaHash) ([]byte, error) { + blockRow := tx.blockIdxBucket.Get(hash[:]) + if blockRow == nil { + str := fmt.Sprintf("block %s does not exist", hash) + return nil, makeDbErr(database.ErrBlockNotFound, str, nil) + } + + return blockRow, nil +} + +// FetchBlockHeader returns the raw serialized bytes for the block header +// identified by the given hash. The raw bytes are in the format returned by +// Serialize on a wire.BlockHeader. +// +// Returns the following errors as required by the interface contract: +// - ErrBlockNotFound if the requested block hash does not exist +// - ErrTxClosed if the transaction has already been closed +// - ErrCorruption if the database has somehow become corrupted +// +// NOTE: The data returned by this function is only valid during a +// database transaction. Attempting to access it after a transaction +// has ended results in undefined behavior. This constraint prevents +// additional data copies and allows support for memory-mapped database +// implementations. +// +// This function is part of the database.Tx interface implementation. +func (tx *transaction) FetchBlockHeader(hash *wire.ShaHash) ([]byte, error) { + // Ensure transaction state is valid. + if err := tx.checkClosed(); err != nil { + return nil, err + } + + // When the block is pending to be written on commit return the bytes + // from there. + if idx, exists := tx.pendingBlocks[*hash]; exists { + blockBytes := tx.pendingBlockData[idx].bytes + return blockBytes[0:blockHdrSize:blockHdrSize], nil + } + + // Fetch the block index row and slice off the header. Notice the use + // of the cap on the subslice to prevent the caller from accidentally + // appending into the db data. + blockRow, err := tx.fetchBlockRow(hash) + if err != nil { + return nil, err + } + endOffset := blockLocSize + blockHdrSize + return blockRow[blockLocSize:endOffset:endOffset], nil +} + +// FetchBlockHeaders returns the raw serialized bytes for the block headers +// identified by the given hashes. The raw bytes are in the format returned by +// Serialize on a wire.BlockHeader. +// +// Returns the following errors as required by the interface contract: +// - ErrBlockNotFound if the any of the requested block hashes do not exist +// - ErrTxClosed if the transaction has already been closed +// - ErrCorruption if the database has somehow become corrupted +// +// NOTE: The data returned by this function is only valid during a database +// transaction. Attempting to access it after a transaction has ended results +// in undefined behavior. This constraint prevents additional data copies and +// allows support for memory-mapped database implementations. +// +// This function is part of the database.Tx interface implementation. +func (tx *transaction) FetchBlockHeaders(hashes []wire.ShaHash) ([][]byte, error) { + // Ensure transaction state is valid. + if err := tx.checkClosed(); err != nil { + return nil, err + } + + // NOTE: This could check for the existence of all blocks before loading + // any of the headers which would be faster in the failure case, however + // callers will not typically be calling this function with invalid + // values, so optimize for the common case. + + // Load the headers. + headers := make([][]byte, len(hashes)) + for i := range hashes { + hash := &hashes[i] + + // When the block is pending to be written on commit return the + // bytes from there. + if idx, exists := tx.pendingBlocks[*hash]; exists { + blkBytes := tx.pendingBlockData[idx].bytes + headers[i] = blkBytes[0:blockHdrSize:blockHdrSize] + continue + } + + // Fetch the block index row and slice off the header. Notice + // the use of the cap on the subslice to prevent the caller + // from accidentally appending into the db data. + blockRow, err := tx.fetchBlockRow(hash) + if err != nil { + return nil, err + } + endOffset := blockLocSize + blockHdrSize + headers[i] = blockRow[blockLocSize:endOffset:endOffset] + } + + return headers, nil +} + +// FetchBlock returns the raw serialized bytes for the block identified by the +// given hash. The raw bytes are in the format returned by Serialize on a +// wire.MsgBlock. +// +// Returns the following errors as required by the interface contract: +// - ErrBlockNotFound if the requested block hash does not exist +// - ErrTxClosed if the transaction has already been closed +// - ErrCorruption if the database has somehow become corrupted +// +// In addition, returns ErrDriverSpecific if any failures occur when reading the +// block files. +// +// NOTE: The data returned by this function is only valid during a database +// transaction. Attempting to access it after a transaction has ended results +// in undefined behavior. This constraint prevents additional data copies and +// allows support for memory-mapped database implementations. +// +// This function is part of the database.Tx interface implementation. +func (tx *transaction) FetchBlock(hash *wire.ShaHash) ([]byte, error) { + // Ensure transaction state is valid. + if err := tx.checkClosed(); err != nil { + return nil, err + } + + // When the block is pending to be written on commit return the bytes + // from there. + if idx, exists := tx.pendingBlocks[*hash]; exists { + return tx.pendingBlockData[idx].bytes, nil + } + + // Lookup the location of the block in the files from the block index. + blockRow, err := tx.fetchBlockRow(hash) + if err != nil { + return nil, err + } + location := deserializeBlockLoc(blockRow) + + // Read the block from the appropriate location. The function also + // performs a checksum over the data to detect data corruption. + blockBytes, err := tx.db.store.readBlock(hash, location) + if err != nil { + return nil, err + } + + return blockBytes, nil +} + +// FetchBlocks returns the raw serialized bytes for the blocks identified by the +// given hashes. The raw bytes are in the format returned by Serialize on a +// wire.MsgBlock. +// +// Returns the following errors as required by the interface contract: +// - ErrBlockNotFound if any of the requested block hashed do not exist +// - ErrTxClosed if the transaction has already been closed +// - ErrCorruption if the database has somehow become corrupted +// +// In addition, returns ErrDriverSpecific if any failures occur when reading the +// block files. +// +// NOTE: The data returned by this function is only valid during a database +// transaction. Attempting to access it after a transaction has ended results +// in undefined behavior. This constraint prevents additional data copies and +// allows support for memory-mapped database implementations. +// +// This function is part of the database.Tx interface implementation. +func (tx *transaction) FetchBlocks(hashes []wire.ShaHash) ([][]byte, error) { + // Ensure transaction state is valid. + if err := tx.checkClosed(); err != nil { + return nil, err + } + + // NOTE: This could check for the existence of all blocks before loading + // any of them which would be faster in the failure case, however + // callers will not typically be calling this function with invalid + // values, so optimize for the common case. + + // Load the blocks. + blocks := make([][]byte, len(hashes)) + for i := range hashes { + var err error + blocks[i], err = tx.FetchBlock(&hashes[i]) + if err != nil { + return nil, err + } + } + + return blocks, nil +} + +// fetchPendingRegion attempts to fetch the provided region from any block which +// are pending to be written on commit. It will return nil for the byte slice +// when the region references a block which is not pending. When the region +// does reference a pending block, it is bounds checked and returns +// ErrBlockRegionInvalid if invalid. +func (tx *transaction) fetchPendingRegion(region *database.BlockRegion) ([]byte, error) { + // Nothing to do if the block is not pending to be written on commit. + idx, exists := tx.pendingBlocks[*region.Hash] + if !exists { + return nil, nil + } + + // Ensure the region is within the bounds of the block. + blockBytes := tx.pendingBlockData[idx].bytes + blockLen := uint32(len(blockBytes)) + endOffset := region.Offset + region.Len + if endOffset < region.Offset || endOffset > blockLen { + str := fmt.Sprintf("block %s region offset %d, length %d "+ + "exceeds block length of %d", region.Hash, + region.Offset, region.Len, blockLen) + return nil, makeDbErr(database.ErrBlockRegionInvalid, str, nil) + } + + // Return the bytes from the pending block. + return blockBytes[region.Offset:endOffset:endOffset], nil +} + +// FetchBlockRegion returns the raw serialized bytes for the given block region. +// +// For example, it is possible to directly extract Bitcoin transactions and/or +// scripts from a block with this function. Depending on the backend +// implementation, this can provide significant savings by avoiding the need to +// load entire blocks. +// +// The raw bytes are in the format returned by Serialize on a wire.MsgBlock and +// the Offset field in the provided BlockRegion is zero-based and relative to +// the start of the block (byte 0). +// +// Returns the following errors as required by the interface contract: +// - ErrBlockNotFound if the requested block hash does not exist +// - ErrBlockRegionInvalid if the region exceeds the bounds of the associated +// block +// - ErrTxClosed if the transaction has already been closed +// - ErrCorruption if the database has somehow become corrupted +// +// In addition, returns ErrDriverSpecific if any failures occur when reading the +// block files. +// +// NOTE: The data returned by this function is only valid during a database +// transaction. Attempting to access it after a transaction has ended results +// in undefined behavior. This constraint prevents additional data copies and +// allows support for memory-mapped database implementations. +// +// This function is part of the database.Tx interface implementation. +func (tx *transaction) FetchBlockRegion(region *database.BlockRegion) ([]byte, error) { + // Ensure transaction state is valid. + if err := tx.checkClosed(); err != nil { + return nil, err + } + + // When the block is pending to be written on commit return the bytes + // from there. + if tx.pendingBlocks != nil { + regionBytes, err := tx.fetchPendingRegion(region) + if err != nil { + return nil, err + } + if regionBytes != nil { + return regionBytes, nil + } + } + + // Lookup the location of the block in the files from the block index. + blockRow, err := tx.fetchBlockRow(region.Hash) + if err != nil { + return nil, err + } + location := deserializeBlockLoc(blockRow) + + // Ensure the region is within the bounds of the block. + endOffset := region.Offset + region.Len + if endOffset < region.Offset || endOffset > location.blockLen { + str := fmt.Sprintf("block %s region offset %d, length %d "+ + "exceeds block length of %d", region.Hash, + region.Offset, region.Len, location.blockLen) + return nil, makeDbErr(database.ErrBlockRegionInvalid, str, nil) + + } + + // Read the region from the appropriate disk block file. + regionBytes, err := tx.db.store.readBlockRegion(location, region.Offset, + region.Len) + if err != nil { + return nil, err + } + + return regionBytes, nil +} + +// FetchBlockRegions returns the raw serialized bytes for the given block +// regions. +// +// For example, it is possible to directly extract Bitcoin transactions and/or +// scripts from various blocks with this function. Depending on the backend +// implementation, this can provide significant savings by avoiding the need to +// load entire blocks. +// +// The raw bytes are in the format returned by Serialize on a wire.MsgBlock and +// the Offset fields in the provided BlockRegions are zero-based and relative to +// the start of the block (byte 0). +// +// Returns the following errors as required by the interface contract: +// - ErrBlockNotFound if any of the request block hashes do not exist +// - ErrBlockRegionInvalid if one or more region exceed the bounds of the +// associated block +// - ErrTxClosed if the transaction has already been closed +// - ErrCorruption if the database has somehow become corrupted +// +// In addition, returns ErrDriverSpecific if any failures occur when reading the +// block files. +// +// NOTE: The data returned by this function is only valid during a database +// transaction. Attempting to access it after a transaction has ended results +// in undefined behavior. This constraint prevents additional data copies and +// allows support for memory-mapped database implementations. +// +// This function is part of the database.Tx interface implementation. +func (tx *transaction) FetchBlockRegions(regions []database.BlockRegion) ([][]byte, error) { + // Ensure transaction state is valid. + if err := tx.checkClosed(); err != nil { + return nil, err + } + + // NOTE: This could check for the existence of all blocks before + // deserializing the locations and building up the fetch list which + // would be faster in the failure case, however callers will not + // typically be calling this function with invalid values, so optimize + // for the common case. + + // NOTE: A potential optimization here would be to combine adjacent + // regions to reduce the number of reads. + + // In order to improve efficiency of loading the bulk data, first grab + // the block location for all of the requested block hashes and sort + // the reads by filenum:offset so that all reads are grouped by file + // and linear within each file. This can result in quite a significant + // performance increase depending on how spread out the requested hashes + // are by reducing the number of file open/closes and random accesses + // needed. The fetchList is intentionally allocated with a cap because + // some of the regions might be fetched from the pending blocks and + // hence there is no need to fetch those from disk. + blockRegions := make([][]byte, len(regions)) + fetchList := make([]bulkFetchData, 0, len(regions)) + for i := range regions { + region := ®ions[i] + + // When the block is pending to be written on commit grab the + // bytes from there. + if tx.pendingBlocks != nil { + regionBytes, err := tx.fetchPendingRegion(region) + if err != nil { + return nil, err + } + if regionBytes != nil { + blockRegions[i] = regionBytes + continue + } + } + + // Lookup the location of the block in the files from the block + // index. + blockRow, err := tx.fetchBlockRow(region.Hash) + if err != nil { + return nil, err + } + location := deserializeBlockLoc(blockRow) + + // Ensure the region is within the bounds of the block. + endOffset := region.Offset + region.Len + if endOffset < region.Offset || endOffset > location.blockLen { + str := fmt.Sprintf("block %s region offset %d, length "+ + "%d exceeds block length of %d", region.Hash, + region.Offset, region.Len, location.blockLen) + return nil, makeDbErr(database.ErrBlockRegionInvalid, str, nil) + } + + fetchList = append(fetchList, bulkFetchData{&location, i}) + } + sort.Sort(bulkFetchDataSorter(fetchList)) + + // Read all of the regions in the fetch list and set the results. + for i := range fetchList { + fetchData := &fetchList[i] + ri := fetchData.replyIndex + region := ®ions[ri] + location := fetchData.blockLocation + regionBytes, err := tx.db.store.readBlockRegion(*location, + region.Offset, region.Len) + if err != nil { + return nil, err + } + blockRegions[ri] = regionBytes + } + + return blockRegions, nil +} + +// close marks the transaction closed then releases any pending data, the +// underlying snapshot, the transaction read lock, and the write lock when the +// transaction is writable. +func (tx *transaction) close() { + tx.closed = true + + // Clear pending blocks that would have been written on commit. + tx.pendingBlocks = nil + tx.pendingBlockData = nil + + // Clear pending keys that would have been written or deleted on commit. + tx.pendingKeys = nil + tx.pendingRemove = nil + + // Release the snapshot. + if tx.snapshot != nil { + tx.snapshot.Release() + tx.snapshot = nil + } + + tx.db.closeLock.RUnlock() + + // Release the writer lock for writable transactions to unblock any + // other write transaction which are possibly waiting. + if tx.writable { + tx.db.writeLock.Unlock() + } +} + +// serializeBlockRow serializes a block row into a format suitable for storage +// into the block index. +func serializeBlockRow(blockLoc blockLocation, blockHdr []byte) []byte { + // The serialized block index row format is: + // + // [0:blockLocSize] Block location + // [blockLocSize:blockLocSize+blockHdrSize] Block header + serializedRow := make([]byte, blockLocSize+blockHdrSize) + copy(serializedRow, serializeBlockLoc(blockLoc)) + copy(serializedRow[blockHdrOffset:], blockHdr) + return serializedRow +} + +// writePendingAndCommit writes pending block data to the flat block files, +// updates the metadata with their locations as well as the new current write +// location, and commits the metadata to the memory database cache. It also +// properly handles rollback in the case of failures. +// +// This function MUST only be called when there is pending data to be written. +func (tx *transaction) writePendingAndCommit() error { + // Save the current block store write position for potential rollback. + // These variables are only updated here in this function and there can + // only be one write transaction active at a time, so it's safe to store + // them for potential rollback. + wc := tx.db.store.writeCursor + wc.RLock() + oldBlkFileNum := wc.curFileNum + oldBlkOffset := wc.curOffset + wc.RUnlock() + + // rollback is a closure that is used to rollback all writes to the + // block files. + rollback := func() { + // Rollback any modifications made to the block files if needed. + tx.db.store.handleRollback(oldBlkFileNum, oldBlkOffset) + } + + // Loop through all of the pending blocks to store and write them. + for _, blockData := range tx.pendingBlockData { + log.Tracef("Storing block %s", blockData.hash) + location, err := tx.db.store.writeBlock(blockData.bytes) + if err != nil { + rollback() + return err + } + + // Add a record in the block index for the block. The record + // includes the location information needed to locate the block + // on the filesystem as well as the block header since they are + // so commonly needed. + blockHdr := blockData.bytes[0:blockHdrSize] + blockRow := serializeBlockRow(location, blockHdr) + err = tx.blockIdxBucket.Put(blockData.hash[:], blockRow) + if err != nil { + rollback() + return err + } + } + + // Update the metadata for the current write file and offset. + writeRow := serializeWriteRow(wc.curFileNum, wc.curOffset) + if err := tx.metaBucket.Put(writeLocKeyName, writeRow); err != nil { + rollback() + return convertErr("failed to store write cursor", err) + } + + // Atomically update the database cache. The cache automatically + // handles flushing to the underlying persistent storage database. + return tx.db.cache.commitTx(tx) +} + +// Commit commits all changes that have been made to the root metadata bucket +// and all of its sub-buckets to the database cache which is periodically synced +// to persistent storage. In addition, it commits all new blocks directly to +// persistent storage bypassing the db cache. Blocks can be rather large, so +// this help increase the amount of cache available for the metadata updates and +// is safe since blocks are immutable. +// +// This function is part of the database.Tx interface implementation. +func (tx *transaction) Commit() error { + // Prevent commits on managed transactions. + if tx.managed { + tx.close() + panic("managed transaction commit not allowed") + } + + // Ensure transaction state is valid. + if err := tx.checkClosed(); err != nil { + return err + } + + // Regardless of whether the commit succeeds, the transaction is closed + // on return. + defer tx.close() + + // Ensure the transaction is writable. + if !tx.writable { + str := "Commit requires a writable database transaction" + return makeDbErr(database.ErrTxNotWritable, str, nil) + } + + // Write pending data. The function will rollback if any errors occur. + return tx.writePendingAndCommit() +} + +// Rollback undoes all changes that have been made to the root bucket and all of +// its sub-buckets. +// +// This function is part of the database.Tx interface implementation. +func (tx *transaction) Rollback() error { + // Prevent rollbacks on managed transactions. + if tx.managed { + tx.close() + panic("managed transaction rollback not allowed") + } + + // Ensure transaction state is valid. + if err := tx.checkClosed(); err != nil { + return err + } + + tx.close() + return nil +} + +// db represents a collection of namespaces which are persisted and implements +// the database.DB interface. All database access is performed through +// transactions which are obtained through the specific Namespace. +type db struct { + writeLock sync.Mutex // Limit to one write transaction at a time. + closeLock sync.RWMutex // Make database close block while txns active. + closed bool // Is the database closed? + store *blockStore // Handles read/writing blocks to flat files. + cache *dbCache // Cache layer which wraps underlying leveldb DB. +} + +// Enforce db implements the database.DB interface. +var _ database.DB = (*db)(nil) + +// Type returns the database driver type the current database instance was +// created with. +// +// This function is part of the database.DB interface implementation. +func (db *db) Type() string { + return dbType +} + +// begin is the implementation function for the Begin database method. See its +// documentation for more details. +// +// This function is only separate because it returns the internal transaction +// which is used by the managed transaction code while the database method +// returns the interface. +func (db *db) begin(writable bool) (*transaction, error) { + // Whenever a new writable transaction is started, grab the write lock + // to ensure only a single write transaction can be active at the same + // time. This lock will not be released until the transaction is + // closed (via Rollback or Commit). + if writable { + db.writeLock.Lock() + } + + // Whenever a new transaction is started, grab a read lock against the + // database to ensure Close will wait for the transaction to finish. + // This lock will not be released until the transaction is closed (via + // Rollback or Commit). + db.closeLock.RLock() + if db.closed { + db.closeLock.RUnlock() + if writable { + db.writeLock.Unlock() + } + return nil, makeDbErr(database.ErrDbNotOpen, errDbNotOpenStr, + nil) + } + + // Grab a snapshot of the database cache (which in turn also handles the + // underlying database). + snapshot, err := db.cache.Snapshot() + if err != nil { + db.closeLock.RUnlock() + if writable { + db.writeLock.Unlock() + } + + return nil, err + } + + // The metadata and block index buckets are internal-only buckets, so + // they have defined IDs. + tx := &transaction{ + writable: writable, + db: db, + snapshot: snapshot, + pendingKeys: treap.NewMutable(), + pendingRemove: treap.NewMutable(), + } + tx.metaBucket = &bucket{tx: tx, id: metadataBucketID} + tx.blockIdxBucket = &bucket{tx: tx, id: blockIdxBucketID} + return tx, nil +} + +// Begin starts a transaction which is either read-only or read-write depending +// on the specified flag. Multiple read-only transactions can be started +// simultaneously while only a single read-write transaction can be started at a +// time. The call will block when starting a read-write transaction when one is +// already open. +// +// NOTE: The transaction must be closed by calling Rollback or Commit on it when +// it is no longer needed. Failure to do so will result in unclaimed memory. +// +// This function is part of the database.DB interface implementation. +func (db *db) Begin(writable bool) (database.Tx, error) { + return db.begin(writable) +} + +// rollbackOnPanic rolls the passed transaction back if the code in the calling +// function panics. This is needed since the mutex on a transaction must be +// released and a panic in called code would prevent that from happening. +// +// NOTE: This can only be handled manually for managed transactions since they +// control the life-cycle of the transaction. As the documentation on Begin +// calls out, callers opting to use manual transactions will have to ensure the +// transaction is rolled back on panic if it desires that functionality as well +// or the database will fail to close since the read-lock will never be +// released. +func rollbackOnPanic(tx *transaction) { + if err := recover(); err != nil { + tx.managed = false + _ = tx.Rollback() + panic(err) + } +} + +// View invokes the passed function in the context of a managed read-only +// transaction with the root bucket for the namespace. Any errors returned from +// the user-supplied function are returned from this function. +// +// This function is part of the database.DB interface implementation. +func (db *db) View(fn func(database.Tx) error) error { + // Start a read-only transaction. + tx, err := db.begin(false) + if err != nil { + return err + } + + // Since the user-provided function might panic, ensure the transaction + // releases all mutexes and resources. There is no guarantee the caller + // won't use recover and keep going. Thus, the database must still be + // in a usable state on panics due to caller issues. + defer rollbackOnPanic(tx) + + tx.managed = true + err = fn(tx) + tx.managed = false + if err != nil { + // The error is ignored here because nothing was written yet + // and regardless of a rollback failure, the tx is closed now + // anyways. + _ = tx.Rollback() + return err + } + + return tx.Rollback() +} + +// Update invokes the passed function in the context of a managed read-write +// transaction with the root bucket for the namespace. Any errors returned from +// the user-supplied function will cause the transaction to be rolled back and +// are returned from this function. Otherwise, the transaction is committed +// when the user-supplied function returns a nil error. +// +// This function is part of the database.DB interface implementation. +func (db *db) Update(fn func(database.Tx) error) error { + // Start a read-write transaction. + tx, err := db.begin(true) + if err != nil { + return err + } + + // Since the user-provided function might panic, ensure the transaction + // releases all mutexes and resources. There is no guarantee the caller + // won't use recover and keep going. Thus, the database must still be + // in a usable state on panics due to caller issues. + defer rollbackOnPanic(tx) + + tx.managed = true + err = fn(tx) + tx.managed = false + if err != nil { + // The error is ignored here because nothing was written yet + // and regardless of a rollback failure, the tx is closed now + // anyways. + _ = tx.Rollback() + return err + } + + return tx.Commit() +} + +// Close cleanly shuts down the database and syncs all data. It will block +// until all database transactions have been finalized (rolled back or +// committed). +// +// This function is part of the database.DB interface implementation. +func (db *db) Close() error { + // Since all transactions have a read lock on this mutex, this will + // cause Close to wait for all readers to complete. + db.closeLock.Lock() + defer db.closeLock.Unlock() + + if db.closed { + return makeDbErr(database.ErrDbNotOpen, errDbNotOpenStr, nil) + } + db.closed = true + + // NOTE: Since the above lock waits for all transactions to finish and + // prevents any new ones from being started, it is safe to flush the + // cache and clear all state without the individual locks. + + // Close the database cache which will flush any existing entries to + // disk and close the underlying leveldb database. Any error is saved + // and returned at the end after the remaining cleanup since the + // database will be marked closed even if this fails given there is no + // good way for the caller to recover from a failure here anyways. + closeErr := db.cache.Close() + + // Close any open flat files that house the blocks. + wc := db.store.writeCursor + if wc.curFile.file != nil { + _ = wc.curFile.file.Close() + wc.curFile.file = nil + } + for _, blockFile := range db.store.openBlockFiles { + _ = blockFile.file.Close() + } + db.store.openBlockFiles = nil + db.store.openBlocksLRU.Init() + db.store.fileNumToLRUElem = nil + + return closeErr +} + +// filesExists reports whether the named file or directory exists. +func fileExists(name string) bool { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +} + +// initDB creates the initial buckets and values used by the package. This is +// mainly in a separate function for testing purposes. +func initDB(ldb *leveldb.DB) error { + // The starting block file write cursor location is file num 0, offset + // 0. + batch := new(leveldb.Batch) + batch.Put(bucketizedKey(metadataBucketID, writeLocKeyName), + serializeWriteRow(0, 0)) + + // Create block index bucket and set the current bucket id. + // + // NOTE: Since buckets are virtualized through the use of prefixes, + // there is no need to store the bucket index data for the metadata + // bucket in the database. However, the first bucket ID to use does + // need to account for it to ensure there are no key collisions. + batch.Put(bucketIndexKey(metadataBucketID, blockIdxBucketName), + blockIdxBucketID[:]) + batch.Put(curBucketIDKeyName, blockIdxBucketID[:]) + + // Write everything as a single batch. + if err := ldb.Write(batch, nil); err != nil { + str := fmt.Sprintf("failed to initialize metadata database: %v", + err) + return convertErr(str, err) + } + + return nil +} + +// openDB opens the database at the provided path. database.ErrDbDoesNotExist +// is returned if the database doesn't exist and the create flag is not set. +func openDB(dbPath string, network wire.BitcoinNet, create bool) (database.DB, error) { + // Error if the database doesn't exist and the create flag is not set. + metadataDbPath := filepath.Join(dbPath, metadataDbName) + dbExists := fileExists(metadataDbPath) + if !create && !dbExists { + str := fmt.Sprintf("database %q does not exist", metadataDbPath) + return nil, makeDbErr(database.ErrDbDoesNotExist, str, nil) + } + + // Ensure the full path to the database exists. + if !dbExists { + // The error can be ignored here since the call to + // leveldb.OpenFile will fail if the directory couldn't be + // created. + _ = os.MkdirAll(dbPath, 0700) + } + + // Open the metadata database (will create it if needed). + opts := opt.Options{ + ErrorIfExist: create, + Strict: opt.DefaultStrict, + Compression: opt.NoCompression, + Filter: filter.NewBloomFilter(10), + } + ldb, err := leveldb.OpenFile(metadataDbPath, &opts) + if err != nil { + return nil, convertErr(err.Error(), err) + } + + // Create the block store which includes scanning the existing flat + // block files to find what the current write cursor position is + // according to the data that is actually on disk. Also create the + // database cache which wraps the underlying leveldb database to provide + // write caching. + store := newBlockStore(dbPath, network) + cache := newDbCache(ldb, store, defaultCacheSize, defaultFlushSecs) + pdb := &db{store: store, cache: cache} + + // Perform any reconciliation needed between the block and metadata as + // well as database initialization, if needed. + return reconcileDB(pdb, create) +} diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/dbcache.go b/vendor/github.com/btcsuite/btcd/database/ffldb/dbcache.go new file mode 100644 index 0000000000000000000000000000000000000000..7068434fdeddb13afe7dfdd30a5e846b0b227e8e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/dbcache.go @@ -0,0 +1,664 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package ffldb + +import ( + "bytes" + "fmt" + "sync" + "time" + + "github.com/btcsuite/btcd/database/internal/treap" + "github.com/btcsuite/goleveldb/leveldb" + "github.com/btcsuite/goleveldb/leveldb/iterator" + "github.com/btcsuite/goleveldb/leveldb/util" +) + +const ( + // defaultCacheSize is the default size for the database cache. + defaultCacheSize = 100 * 1024 * 1024 // 100 MB + + // defaultFlushSecs is the default number of seconds to use as a + // threshold in between database cache flushes when the cache size has + // not been exceeded. + defaultFlushSecs = 300 // 5 minutes + + // ldbBatchHeaderSize is the size of a leveldb batch header which + // includes the sequence header and record counter. + // + // ldbRecordIKeySize is the size of the ikey used internally by leveldb + // when appending a record to a batch. + // + // These are used to help preallocate space needed for a batch in one + // allocation instead of letting leveldb itself constantly grow it. + // This results in far less pressure on the GC and consequently helps + // prevent the GC from allocating a lot of extra unneeded space. + ldbBatchHeaderSize = 12 + ldbRecordIKeySize = 8 +) + +// ldbCacheIter wraps a treap iterator to provide the additional functionality +// needed to satisfy the leveldb iterator.Iterator interface. +type ldbCacheIter struct { + *treap.Iterator +} + +// Enforce ldbCacheIterator implements the leveldb iterator.Iterator interface. +var _ iterator.Iterator = (*ldbCacheIter)(nil) + +// Error is only provided to satisfy the iterator interface as there are no +// errors for this memory-only structure. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *ldbCacheIter) Error() error { + return nil +} + +// SetReleaser is only provided to satisfy the iterator interface as there is no +// need to override it. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *ldbCacheIter) SetReleaser(releaser util.Releaser) { +} + +// Release is only provided to satisfy the iterator interface. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *ldbCacheIter) Release() { +} + +// newLdbCacheIter creates a new treap iterator for the given slice against the +// pending keys for the passed cache snapshot and returns it wrapped in an +// ldbCacheIter so it can be used as a leveldb iterator. +func newLdbCacheIter(snap *dbCacheSnapshot, slice *util.Range) *ldbCacheIter { + iter := snap.pendingKeys.Iterator(slice.Start, slice.Limit) + return &ldbCacheIter{Iterator: iter} +} + +// dbCacheIterator defines an iterator over the key/value pairs in the database +// cache and underlying database. +type dbCacheIterator struct { + cacheSnapshot *dbCacheSnapshot + dbIter iterator.Iterator + cacheIter iterator.Iterator + currentIter iterator.Iterator + released bool +} + +// Enforce dbCacheIterator implements the leveldb iterator.Iterator interface. +var _ iterator.Iterator = (*dbCacheIterator)(nil) + +// skipPendingUpdates skips any keys at the current database iterator position +// that are being updated by the cache. The forwards flag indicates the +// direction the iterator is moving. +func (iter *dbCacheIterator) skipPendingUpdates(forwards bool) { + for iter.dbIter.Valid() { + var skip bool + key := iter.dbIter.Key() + if iter.cacheSnapshot.pendingRemove.Has(key) { + skip = true + } else if iter.cacheSnapshot.pendingKeys.Has(key) { + skip = true + } + if !skip { + break + } + + if forwards { + iter.dbIter.Next() + } else { + iter.dbIter.Prev() + } + } +} + +// chooseIterator first skips any entries in the database iterator that are +// being updated by the cache and sets the current iterator to the appropriate +// iterator depending on their validity and the order they compare in while taking +// into account the direction flag. When the iterator is being moved forwards +// and both iterators are valid, the iterator with the smaller key is chosen and +// vice versa when the iterator is being moved backwards. +func (iter *dbCacheIterator) chooseIterator(forwards bool) bool { + // Skip any keys at the current database iterator position that are + // being updated by the cache. + iter.skipPendingUpdates(forwards) + + // When both iterators are exhausted, the iterator is exhausted too. + if !iter.dbIter.Valid() && !iter.cacheIter.Valid() { + iter.currentIter = nil + return false + } + + // Choose the database iterator when the cache iterator is exhausted. + if !iter.cacheIter.Valid() { + iter.currentIter = iter.dbIter + return true + } + + // Choose the cache iterator when the database iterator is exhausted. + if !iter.dbIter.Valid() { + iter.currentIter = iter.cacheIter + return true + } + + // Both iterators are valid, so choose the iterator with either the + // smaller or larger key depending on the forwards flag. + compare := bytes.Compare(iter.dbIter.Key(), iter.cacheIter.Key()) + if (forwards && compare > 0) || (!forwards && compare < 0) { + iter.currentIter = iter.cacheIter + } else { + iter.currentIter = iter.dbIter + } + return true +} + +// First positions the iterator at the first key/value pair and returns whether +// or not the pair exists. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *dbCacheIterator) First() bool { + // Seek to the first key in both the database and cache iterators and + // choose the iterator that is both valid and has the smaller key. + iter.dbIter.First() + iter.cacheIter.First() + return iter.chooseIterator(true) +} + +// Last positions the iterator at the last key/value pair and returns whether or +// not the pair exists. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *dbCacheIterator) Last() bool { + // Seek to the last key in both the database and cache iterators and + // choose the iterator that is both valid and has the larger key. + iter.dbIter.Last() + iter.cacheIter.Last() + return iter.chooseIterator(false) +} + +// Next moves the iterator one key/value pair forward and returns whether or not +// the pair exists. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *dbCacheIterator) Next() bool { + // Nothing to return if cursor is exhausted. + if iter.currentIter == nil { + return false + } + + // Move the current iterator to the next entry and choose the iterator + // that is both valid and has the smaller key. + iter.currentIter.Next() + return iter.chooseIterator(true) +} + +// Prev moves the iterator one key/value pair backward and returns whether or +// not the pair exists. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *dbCacheIterator) Prev() bool { + // Nothing to return if cursor is exhausted. + if iter.currentIter == nil { + return false + } + + // Move the current iterator to the previous entry and choose the + // iterator that is both valid and has the larger key. + iter.currentIter.Prev() + return iter.chooseIterator(false) +} + +// Seek positions the iterator at the first key/value pair that is greater than +// or equal to the passed seek key. Returns false if no suitable key was found. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *dbCacheIterator) Seek(key []byte) bool { + // Seek to the provided key in both the database and cache iterators + // then choose the iterator that is both valid and has the larger key. + iter.dbIter.Seek(key) + iter.cacheIter.Seek(key) + return iter.chooseIterator(true) +} + +// Valid indicates whether the iterator is positioned at a valid key/value pair. +// It will be considered invalid when the iterator is newly created or exhausted. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *dbCacheIterator) Valid() bool { + return iter.currentIter != nil +} + +// Key returns the current key the iterator is pointing to. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *dbCacheIterator) Key() []byte { + // Nothing to return if iterator is exhausted. + if iter.currentIter == nil { + return nil + } + + return iter.currentIter.Key() +} + +// Value returns the current value the iterator is pointing to. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *dbCacheIterator) Value() []byte { + // Nothing to return if iterator is exhausted. + if iter.currentIter == nil { + return nil + } + + return iter.currentIter.Value() +} + +// SetReleaser is only provided to satisfy the iterator interface as there is no +// need to override it. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *dbCacheIterator) SetReleaser(releaser util.Releaser) { +} + +// Release releases the iterator by removing the underlying treap iterator from +// the list of active iterators against the pending keys treap. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *dbCacheIterator) Release() { + if !iter.released { + iter.dbIter.Release() + iter.cacheIter.Release() + iter.currentIter = nil + iter.released = true + } +} + +// Error is only provided to satisfy the iterator interface as there are no +// errors for this memory-only structure. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *dbCacheIterator) Error() error { + return nil +} + +// dbCacheSnapshot defines a snapshot of the database cache and underlying +// database at a particular point in time. +type dbCacheSnapshot struct { + dbSnapshot *leveldb.Snapshot + pendingKeys *treap.Immutable + pendingRemove *treap.Immutable +} + +// Has returns whether or not the passed key exists. +func (snap *dbCacheSnapshot) Has(key []byte) bool { + // Check the cached entries first. + if snap.pendingRemove.Has(key) { + return false + } + if snap.pendingKeys.Has(key) { + return true + } + + // Consult the database. + hasKey, _ := snap.dbSnapshot.Has(key, nil) + return hasKey +} + +// Get returns the value for the passed key. The function will return nil when +// the key does not exist. +func (snap *dbCacheSnapshot) Get(key []byte) []byte { + // Check the cached entries first. + if snap.pendingRemove.Has(key) { + return nil + } + if value := snap.pendingKeys.Get(key); value != nil { + return value + } + + // Consult the database. + value, err := snap.dbSnapshot.Get(key, nil) + if err != nil { + return nil + } + return value +} + +// Release releases the snapshot. +func (snap *dbCacheSnapshot) Release() { + snap.dbSnapshot.Release() + snap.pendingKeys = nil + snap.pendingRemove = nil +} + +// NewIterator returns a new iterator for the snapshot. The newly returned +// iterator is not pointing to a valid item until a call to one of the methods +// to position it is made. +// +// The slice parameter allows the iterator to be limited to a range of keys. +// The start key is inclusive and the limit key is exclusive. Either or both +// can be nil if the functionality is not desired. +func (snap *dbCacheSnapshot) NewIterator(slice *util.Range) *dbCacheIterator { + return &dbCacheIterator{ + dbIter: snap.dbSnapshot.NewIterator(slice, nil), + cacheIter: newLdbCacheIter(snap, slice), + cacheSnapshot: snap, + } +} + +// dbCache provides a database cache layer backed by an underlying database. It +// allows a maximum cache size and flush interval to be specified such that the +// cache is flushed to the database when the cache size exceeds the maximum +// configured value or it has been longer than the configured interval since the +// last flush. This effectively provides transaction batching so that callers +// can commit transactions at will without incurring large performance hits due +// to frequent disk syncs. +type dbCache struct { + // ldb is the underlying leveldb DB for metadata. + ldb *leveldb.DB + + // store is used to sync blocks to flat files. + store *blockStore + + // The following fields are related to flushing the cache to persistent + // storage. Note that all flushing is performed in an opportunistic + // fashion. This means that it is only flushed during a transaction or + // when the database cache is closed. + // + // maxSize is the maximum size threshold the cache can grow to before + // it is flushed. + // + // flushInterval is the threshold interval of time that is allowed to + // pass before the cache is flushed. + // + // lastFlush is the time the cache was last flushed. It is used in + // conjunction with the current time and the flush interval. + // + // NOTE: These flush related fields are protected by the database write + // lock. + maxSize uint64 + flushInterval time.Duration + lastFlush time.Time + + // The following fields hold the keys that need to be stored or deleted + // from the underlying database once the cache is full, enough time has + // passed, or when the database is shutting down. Note that these are + // stored using immutable treaps to support O(1) MVCC snapshots against + // the cached data. The cacheLock is used to protect concurrent access + // for cache updates and snapshots. + cacheLock sync.RWMutex + cachedKeys *treap.Immutable + cachedRemove *treap.Immutable +} + +// Snapshot returns a snapshot of the database cache and underlying database at +// a particular point in time. +// +// The snapshot must be released after use by calling Release. +func (c *dbCache) Snapshot() (*dbCacheSnapshot, error) { + dbSnapshot, err := c.ldb.GetSnapshot() + if err != nil { + str := "failed to open transaction" + return nil, convertErr(str, err) + } + + // Since the cached keys to be added and removed use an immutable treap, + // a snapshot is simply obtaining the root of the tree under the lock + // which is used to atomically swap the root. + c.cacheLock.RLock() + cacheSnapshot := &dbCacheSnapshot{ + dbSnapshot: dbSnapshot, + pendingKeys: c.cachedKeys, + pendingRemove: c.cachedRemove, + } + c.cacheLock.RUnlock() + return cacheSnapshot, nil +} + +// updateDB invokes the passed function in the context of a managed leveldb +// transaction. Any errors returned from the user-supplied function will cause +// the transaction to be rolled back and are returned from this function. +// Otherwise, the transaction is committed when the user-supplied function +// returns a nil error. +func (c *dbCache) updateDB(fn func(ldbTx *leveldb.Transaction) error) error { + // Start a leveldb transaction. + ldbTx, err := c.ldb.OpenTransaction() + if err != nil { + return convertErr("failed to open ldb transaction", err) + } + + if err := fn(ldbTx); err != nil { + ldbTx.Discard() + return err + } + + // Commit the leveldb transaction and convert any errors as needed. + if err := ldbTx.Commit(); err != nil { + return convertErr("failed to commit leveldb transaction", err) + } + return nil +} + +// TreapForEacher is an interface which allows iteration of a treap in ascending +// order using a user-supplied callback for each key/value pair. It mainly +// exists so both mutable and immutable treaps can be atomically committed to +// the database with the same function. +type TreapForEacher interface { + ForEach(func(k, v []byte) bool) +} + +// commitTreaps atomically commits all of the passed pending add/update/remove +// updates to the underlying database. +func (c *dbCache) commitTreaps(pendingKeys, pendingRemove TreapForEacher) error { + // Perform all leveldb updates using an atomic transaction. + return c.updateDB(func(ldbTx *leveldb.Transaction) error { + var innerErr error + pendingKeys.ForEach(func(k, v []byte) bool { + if dbErr := ldbTx.Put(k, v, nil); dbErr != nil { + str := fmt.Sprintf("failed to put key %q to "+ + "ldb transaction", k) + innerErr = convertErr(str, dbErr) + return false + } + return true + }) + if innerErr != nil { + return innerErr + } + + pendingRemove.ForEach(func(k, v []byte) bool { + if dbErr := ldbTx.Delete(k, nil); dbErr != nil { + str := fmt.Sprintf("failed to delete "+ + "key %q from ldb transaction", + k) + innerErr = convertErr(str, dbErr) + return false + } + return true + }) + return innerErr + }) +} + +// flush flushes the database cache to persistent storage. This involes syncing +// the block store and replaying all transactions that have been applied to the +// cache to the underlying database. +// +// This function MUST be called with the database write lock held. +func (c *dbCache) flush() error { + c.lastFlush = time.Now() + + // Sync the current write file associated with the block store. This is + // necessary before writing the metadata to prevent the case where the + // metadata contains information about a block which actually hasn't + // been written yet in unexpected shutdown scenarios. + if err := c.store.syncBlocks(); err != nil { + return err + } + + // Since the cached keys to be added and removed use an immutable treap, + // a snapshot is simply obtaining the root of the tree under the lock + // which is used to atomically swap the root. + c.cacheLock.RLock() + cachedKeys := c.cachedKeys + cachedRemove := c.cachedRemove + c.cacheLock.RUnlock() + + // Nothing to do if there is no data to flush. + if cachedKeys.Len() == 0 && cachedRemove.Len() == 0 { + return nil + } + + // Perform all leveldb updates using an atomic transaction. + if err := c.commitTreaps(cachedKeys, cachedRemove); err != nil { + return err + } + + // Clear the cache since it has been flushed. + c.cacheLock.Lock() + c.cachedKeys = treap.NewImmutable() + c.cachedRemove = treap.NewImmutable() + c.cacheLock.Unlock() + + return nil +} + +// needsFlush returns whether or not the database cache needs to be flushed to +// persistent storage based on its current size, whether or not adding all of +// the entries in the passed database transaction would cause it to exceed the +// configured limit, and how much time has elapsed since the last time the cache +// was flushed. +// +// This function MUST be called with the database write lock held. +func (c *dbCache) needsFlush(tx *transaction) bool { + // A flush is needed when more time has elapsed than the configured + // flush interval. + if time.Now().Sub(c.lastFlush) > c.flushInterval { + return true + } + + // A flush is needed when the size of the database cache exceeds the + // specified max cache size. The total calculated size is multiplied by + // 1.5 here to account for additional memory consumption that will be + // needed during the flush as well as old nodes in the cache that are + // referenced by the snapshot used by the transaction. + snap := tx.snapshot + totalSize := snap.pendingKeys.Size() + snap.pendingRemove.Size() + totalSize = uint64(float64(totalSize) * 1.5) + if totalSize > c.maxSize { + return true + } + + return false +} + +// commitTx atomically adds all of the pending keys to add and remove into the +// database cache. When adding the pending keys would cause the size of the +// cache to exceed the max cache size, or the time since the last flush exceeds +// the configured flush interval, the cache will be flushed to the underlying +// persistent database. +// +// This is an atomic operation with respect to the cache in that either all of +// the pending keys to add and remove in the transaction will be applied or none +// of them will. +// +// The database cache itself might be flushed to the underlying persistent +// database even if the transaction fails to apply, but it will only be the +// state of the cache without the transaction applied. +// +// This function MUST be called during a database write transaction which in +// turn implies the database write lock will be held. +func (c *dbCache) commitTx(tx *transaction) error { + // Flush the cache and write the current transaction directly to the + // database if a flush is needed. + if c.needsFlush(tx) { + if err := c.flush(); err != nil { + return err + } + + // Perform all leveldb updates using an atomic transaction. + err := c.commitTreaps(tx.pendingKeys, tx.pendingRemove) + if err != nil { + return err + } + + // Clear the transaction entries since they have been committed. + tx.pendingKeys = nil + tx.pendingRemove = nil + return nil + } + + // At this point a database flush is not needed, so atomically commit + // the transaction to the cache. + + // Since the cached keys to be added and removed use an immutable treap, + // a snapshot is simply obtaining the root of the tree under the lock + // which is used to atomically swap the root. + c.cacheLock.RLock() + newCachedKeys := c.cachedKeys + newCachedRemove := c.cachedRemove + c.cacheLock.RUnlock() + + // Apply every key to add in the database transaction to the cache. + tx.pendingKeys.ForEach(func(k, v []byte) bool { + newCachedRemove = newCachedRemove.Delete(k) + newCachedKeys = newCachedKeys.Put(k, v) + return true + }) + tx.pendingKeys = nil + + // Apply every key to remove in the database transaction to the cache. + tx.pendingRemove.ForEach(func(k, v []byte) bool { + newCachedKeys = newCachedKeys.Delete(k) + newCachedRemove = newCachedRemove.Put(k, nil) + return true + }) + tx.pendingRemove = nil + + // Atomically replace the immutable treaps which hold the cached keys to + // add and delete. + c.cacheLock.Lock() + c.cachedKeys = newCachedKeys + c.cachedRemove = newCachedRemove + c.cacheLock.Unlock() + return nil +} + +// Close cleanly shuts down the database cache by syncing all data and closing +// the underlying leveldb database. +// +// This function MUST be called with the database write lock held. +func (c *dbCache) Close() error { + // Flush any outstanding cached entries to disk. + if err := c.flush(); err != nil { + // Even if there is an error while flushing, attempt to close + // the underlying database. The error is ignored since it would + // mask the flush error. + _ = c.ldb.Close() + return err + } + + // Close the underlying leveldb database. + if err := c.ldb.Close(); err != nil { + str := "failed to close underlying leveldb database" + return convertErr(str, err) + } + + return nil +} + +// newDbCache returns a new database cache instance backed by the provided +// leveldb instance. The cache will be flushed to leveldb when the max size +// exceeds the provided value or it has been longer than the provided interval +// since the last flush. +func newDbCache(ldb *leveldb.DB, store *blockStore, maxSize uint64, flushIntervalSecs uint32) *dbCache { + return &dbCache{ + ldb: ldb, + store: store, + maxSize: maxSize, + flushInterval: time.Second * time.Duration(flushIntervalSecs), + lastFlush: time.Now(), + cachedKeys: treap.NewImmutable(), + cachedRemove: treap.NewImmutable(), + } +} diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/doc.go b/vendor/github.com/btcsuite/btcd/database/ffldb/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..96a2992cb9e8723ee42fc7f663dd63e4199e7342 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/doc.go @@ -0,0 +1,29 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package ffldb implements a driver for the database package that uses leveldb +for the backing metadata and flat files for block storage. + +This driver is the recommended driver for use with btcd. It makes use leveldb +for the metadata, flat files for block storage, and checksums in key areas to +ensure data integrity. + +Usage + +This package is a driver to the database package and provides the database type +of "ffldb". The parameters the Open and Create functions take are the +database path as a string and the block network: + + db, err := database.Open("ffldb", "path/to/database", wire.MainNet) + if err != nil { + // Handle error + } + + db, err := database.Create("ffldb", "path/to/database", wire.MainNet) + if err != nil { + // Handle error + } +*/ +package ffldb diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/driver.go b/vendor/github.com/btcsuite/btcd/database/ffldb/driver.go new file mode 100644 index 0000000000000000000000000000000000000000..28ab8277e92bc1713ce8349d617aee2c8feb1e7f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/driver.go @@ -0,0 +1,84 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package ffldb + +import ( + "fmt" + + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btclog" +) + +var log = btclog.Disabled + +const ( + dbType = "ffldb" +) + +// parseArgs parses the arguments from the database Open/Create methods. +func parseArgs(funcName string, args ...interface{}) (string, wire.BitcoinNet, error) { + if len(args) != 2 { + return "", 0, fmt.Errorf("invalid arguments to %s.%s -- "+ + "expected database path and block network", dbType, + funcName) + } + + dbPath, ok := args[0].(string) + if !ok { + return "", 0, fmt.Errorf("first argument to %s.%s is invalid -- "+ + "expected database path string", dbType, funcName) + } + + network, ok := args[1].(wire.BitcoinNet) + if !ok { + return "", 0, fmt.Errorf("second argument to %s.%s is invalid -- "+ + "expected block network", dbType, funcName) + } + + return dbPath, network, nil +} + +// openDBDriver is the callback provided during driver registration that opens +// an existing database for use. +func openDBDriver(args ...interface{}) (database.DB, error) { + dbPath, network, err := parseArgs("Open", args...) + if err != nil { + return nil, err + } + + return openDB(dbPath, network, false) +} + +// createDBDriver is the callback provided during driver registration that +// creates, initializes, and opens a database for use. +func createDBDriver(args ...interface{}) (database.DB, error) { + dbPath, network, err := parseArgs("Create", args...) + if err != nil { + return nil, err + } + + return openDB(dbPath, network, true) +} + +// useLogger is the callback provided during driver registration that sets the +// current logger to the provided one. +func useLogger(logger btclog.Logger) { + log = logger +} + +func init() { + // Register the driver. + driver := database.Driver{ + DbType: dbType, + Create: createDBDriver, + Open: openDBDriver, + UseLogger: useLogger, + } + if err := database.RegisterDriver(driver); err != nil { + panic(fmt.Sprintf("Failed to regiser database driver '%s': %v", + dbType, err)) + } +} diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/driver_test.go b/vendor/github.com/btcsuite/btcd/database/ffldb/driver_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8ba6691af35beffc6dbbb5f5b9279972e482bde5 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/driver_test.go @@ -0,0 +1,288 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package ffldb_test + +import ( + "fmt" + "os" + "path/filepath" + "reflect" + "runtime" + "testing" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/database/ffldb" + "github.com/btcsuite/btcutil" +) + +// dbType is the database type name for this driver. +const dbType = "ffldb" + +// TestCreateOpenFail ensures that errors related to creating and opening a +// database are handled properly. +func TestCreateOpenFail(t *testing.T) { + t.Parallel() + + // Ensure that attempting to open a database that doesn't exist returns + // the expected error. + wantErrCode := database.ErrDbDoesNotExist + _, err := database.Open(dbType, "noexist", blockDataNet) + if !checkDbError(t, "Open", err, wantErrCode) { + return + } + + // Ensure that attempting to open a database with the wrong number of + // parameters returns the expected error. + wantErr := fmt.Errorf("invalid arguments to %s.Open -- expected "+ + "database path and block network", dbType) + _, err = database.Open(dbType, 1, 2, 3) + if err.Error() != wantErr.Error() { + t.Errorf("Open: did not receive expected error - got %v, "+ + "want %v", err, wantErr) + return + } + + // Ensure that attempting to open a database with an invalid type for + // the first parameter returns the expected error. + wantErr = fmt.Errorf("first argument to %s.Open is invalid -- "+ + "expected database path string", dbType) + _, err = database.Open(dbType, 1, blockDataNet) + if err.Error() != wantErr.Error() { + t.Errorf("Open: did not receive expected error - got %v, "+ + "want %v", err, wantErr) + return + } + + // Ensure that attempting to open a database with an invalid type for + // the second parameter returns the expected error. + wantErr = fmt.Errorf("second argument to %s.Open is invalid -- "+ + "expected block network", dbType) + _, err = database.Open(dbType, "noexist", "invalid") + if err.Error() != wantErr.Error() { + t.Errorf("Open: did not receive expected error - got %v, "+ + "want %v", err, wantErr) + return + } + + // Ensure that attempting to create a database with the wrong number of + // parameters returns the expected error. + wantErr = fmt.Errorf("invalid arguments to %s.Create -- expected "+ + "database path and block network", dbType) + _, err = database.Create(dbType, 1, 2, 3) + if err.Error() != wantErr.Error() { + t.Errorf("Create: did not receive expected error - got %v, "+ + "want %v", err, wantErr) + return + } + + // Ensure that attempting to create a database with an invalid type for + // the first parameter returns the expected error. + wantErr = fmt.Errorf("first argument to %s.Create is invalid -- "+ + "expected database path string", dbType) + _, err = database.Create(dbType, 1, blockDataNet) + if err.Error() != wantErr.Error() { + t.Errorf("Create: did not receive expected error - got %v, "+ + "want %v", err, wantErr) + return + } + + // Ensure that attempting to create a database with an invalid type for + // the second parameter returns the expected error. + wantErr = fmt.Errorf("second argument to %s.Create is invalid -- "+ + "expected block network", dbType) + _, err = database.Create(dbType, "noexist", "invalid") + if err.Error() != wantErr.Error() { + t.Errorf("Create: did not receive expected error - got %v, "+ + "want %v", err, wantErr) + return + } + + // Ensure operations against a closed database return the expected + // error. + dbPath := filepath.Join(os.TempDir(), "ffldb-createfail") + _ = os.RemoveAll(dbPath) + db, err := database.Create(dbType, dbPath, blockDataNet) + if err != nil { + t.Errorf("Create: unexpected error: %v", err) + return + } + defer os.RemoveAll(dbPath) + db.Close() + + wantErrCode = database.ErrDbNotOpen + err = db.View(func(tx database.Tx) error { + return nil + }) + if !checkDbError(t, "View", err, wantErrCode) { + return + } + + wantErrCode = database.ErrDbNotOpen + err = db.Update(func(tx database.Tx) error { + return nil + }) + if !checkDbError(t, "Update", err, wantErrCode) { + return + } + + wantErrCode = database.ErrDbNotOpen + _, err = db.Begin(false) + if !checkDbError(t, "Begin(false)", err, wantErrCode) { + return + } + + wantErrCode = database.ErrDbNotOpen + _, err = db.Begin(true) + if !checkDbError(t, "Begin(true)", err, wantErrCode) { + return + } + + wantErrCode = database.ErrDbNotOpen + err = db.Close() + if !checkDbError(t, "Close", err, wantErrCode) { + return + } +} + +// TestPersistence ensures that values stored are still valid after closing and +// reopening the database. +func TestPersistence(t *testing.T) { + t.Parallel() + + // Create a new database to run tests against. + dbPath := filepath.Join(os.TempDir(), "ffldb-persistencetest") + _ = os.RemoveAll(dbPath) + db, err := database.Create(dbType, dbPath, blockDataNet) + if err != nil { + t.Errorf("Failed to create test database (%s) %v", dbType, err) + return + } + defer os.RemoveAll(dbPath) + defer db.Close() + + // Create a bucket, put some values into it, and store a block so they + // can be tested for existence on re-open. + bucket1Key := []byte("bucket1") + storeValues := map[string]string{ + "b1key1": "foo1", + "b1key2": "foo2", + "b1key3": "foo3", + } + genesisBlock := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) + genesisHash := chaincfg.MainNetParams.GenesisHash + err = db.Update(func(tx database.Tx) error { + metadataBucket := tx.Metadata() + if metadataBucket == nil { + return fmt.Errorf("Metadata: unexpected nil bucket") + } + + bucket1, err := metadataBucket.CreateBucket(bucket1Key) + if err != nil { + return fmt.Errorf("CreateBucket: unexpected error: %v", + err) + } + + for k, v := range storeValues { + err := bucket1.Put([]byte(k), []byte(v)) + if err != nil { + return fmt.Errorf("Put: unexpected error: %v", + err) + } + } + + if err := tx.StoreBlock(genesisBlock); err != nil { + return fmt.Errorf("StoreBlock: unexpected error: %v", + err) + } + + return nil + }) + if err != nil { + t.Errorf("Update: unexpected error: %v", err) + return + } + + // Close and reopen the database to ensure the values persist. + db.Close() + db, err = database.Open(dbType, dbPath, blockDataNet) + if err != nil { + t.Errorf("Failed to open test database (%s) %v", dbType, err) + return + } + defer db.Close() + + // Ensure the values previously stored in the 3rd namespace still exist + // and are correct. + err = db.View(func(tx database.Tx) error { + metadataBucket := tx.Metadata() + if metadataBucket == nil { + return fmt.Errorf("Metadata: unexpected nil bucket") + } + + bucket1 := metadataBucket.Bucket(bucket1Key) + if bucket1 == nil { + return fmt.Errorf("Bucket1: unexpected nil bucket") + } + + for k, v := range storeValues { + gotVal := bucket1.Get([]byte(k)) + if !reflect.DeepEqual(gotVal, []byte(v)) { + return fmt.Errorf("Get: key '%s' does not "+ + "match expected value - got %s, want %s", + k, gotVal, v) + } + } + + genesisBlockBytes, _ := genesisBlock.Bytes() + gotBytes, err := tx.FetchBlock(genesisHash) + if err != nil { + return fmt.Errorf("FetchBlock: unexpected error: %v", + err) + } + if !reflect.DeepEqual(gotBytes, genesisBlockBytes) { + return fmt.Errorf("FetchBlock: stored block mismatch") + } + + return nil + }) + if err != nil { + t.Errorf("View: unexpected error: %v", err) + return + } +} + +// TestInterface performs all interfaces tests for this database driver. +func TestInterface(t *testing.T) { + t.Parallel() + + // Create a new database to run tests against. + dbPath := filepath.Join(os.TempDir(), "ffldb-interfacetest") + _ = os.RemoveAll(dbPath) + db, err := database.Create(dbType, dbPath, blockDataNet) + if err != nil { + t.Errorf("Failed to create test database (%s) %v", dbType, err) + return + } + defer os.RemoveAll(dbPath) + defer db.Close() + + // Ensure the driver type is the expected value. + gotDbType := db.Type() + if gotDbType != dbType { + t.Errorf("Type: unepxected driver type - got %v, want %v", + gotDbType, dbType) + return + } + + // Run all of the interface tests against the database. + runtime.GOMAXPROCS(runtime.NumCPU()) + + // Change the maximum file size to a small value to force multiple flat + // files with the test data set. + ffldb.TstRunWithMaxBlockFileSize(db, 2048, func() { + testInterface(t, db) + }) +} diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/export_test.go b/vendor/github.com/btcsuite/btcd/database/ffldb/export_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2d8e4d2a2bcaf7a5574f517722ab10d982aea50f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/export_test.go @@ -0,0 +1,26 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +This test file is part of the ffldb package rather than than the ffldb_test +package so it can bridge access to the internals to properly test cases which +are either not possible or can't reliably be tested via the public interface. +The functions are only exported while the tests are being run. +*/ + +package ffldb + +import "github.com/btcsuite/btcd/database" + +// TstRunWithMaxBlockFileSize runs the passed function with the maximum allowed +// file size for the database set to the provided value. The value will be set +// back to the original value upon completion. +func TstRunWithMaxBlockFileSize(idb database.DB, size uint32, fn func()) { + ffldb := idb.(*db) + origSize := ffldb.store.maxBlockFileSize + + ffldb.store.maxBlockFileSize = size + fn() + ffldb.store.maxBlockFileSize = origSize +} diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/interface_test.go b/vendor/github.com/btcsuite/btcd/database/ffldb/interface_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b36d91b93513e8f0ce14d5dd5779f7233e942eb4 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/interface_test.go @@ -0,0 +1,2322 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// This file intended to be copied into each backend driver directory. Each +// driver should have their own driver_test.go file which creates a database and +// invokes the testInterface function in this file to ensure the driver properly +// implements the interface. +// +// NOTE: When copying this file into the backend driver folder, the package name +// will need to be changed accordingly. + +package ffldb_test + +import ( + "bytes" + "compress/bzip2" + "encoding/binary" + "fmt" + "io" + "os" + "path/filepath" + "reflect" + "sync/atomic" + "testing" + "time" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +var ( + // blockDataNet is the expected network in the test block data. + blockDataNet = wire.MainNet + + // blockDataFile is the path to a file containing the first 256 blocks + // of the block chain. + blockDataFile = filepath.Join("..", "testdata", "blocks1-256.bz2") + + // errSubTestFail is used to signal that a sub test returned false. + errSubTestFail = fmt.Errorf("sub test failure") +) + +// loadBlocks loads the blocks contained in the testdata directory and returns +// a slice of them. +func loadBlocks(t *testing.T, dataFile string, network wire.BitcoinNet) ([]*btcutil.Block, error) { + // Open the file that contains the blocks for reading. + fi, err := os.Open(dataFile) + if err != nil { + t.Errorf("failed to open file %v, err %v", dataFile, err) + return nil, err + } + defer func() { + if err := fi.Close(); err != nil { + t.Errorf("failed to close file %v %v", dataFile, + err) + } + }() + dr := bzip2.NewReader(fi) + + // Set the first block as the genesis block. + blocks := make([]*btcutil.Block, 0, 256) + genesis := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) + blocks = append(blocks, genesis) + + // Load the remaining blocks. + for height := 1; ; height++ { + var net uint32 + err := binary.Read(dr, binary.LittleEndian, &net) + if err == io.EOF { + // Hit end of file at the expected offset. No error. + break + } + if err != nil { + t.Errorf("Failed to load network type for block %d: %v", + height, err) + return nil, err + } + if net != uint32(network) { + t.Errorf("Block doesn't match network: %v expects %v", + net, network) + return nil, err + } + + var blockLen uint32 + err = binary.Read(dr, binary.LittleEndian, &blockLen) + if err != nil { + t.Errorf("Failed to load block size for block %d: %v", + height, err) + return nil, err + } + + // Read the block. + blockBytes := make([]byte, blockLen) + _, err = io.ReadFull(dr, blockBytes) + if err != nil { + t.Errorf("Failed to load block %d: %v", height, err) + return nil, err + } + + // Deserialize and store the block. + block, err := btcutil.NewBlockFromBytes(blockBytes) + if err != nil { + t.Errorf("Failed to parse block %v: %v", height, err) + return nil, err + } + blocks = append(blocks, block) + } + + return blocks, nil +} + +// checkDbError ensures the passed error is a database.Error with an error code +// that matches the passed error code. +func checkDbError(t *testing.T, testName string, gotErr error, wantErrCode database.ErrorCode) bool { + dbErr, ok := gotErr.(database.Error) + if !ok { + t.Errorf("%s: unexpected error type - got %T, want %T", + testName, gotErr, database.Error{}) + return false + } + if dbErr.ErrorCode != wantErrCode { + t.Errorf("%s: unexpected error code - got %s (%s), want %s", + testName, dbErr.ErrorCode, dbErr.Description, + wantErrCode) + return false + } + + return true +} + +// testContext is used to store context information about a running test which +// is passed into helper functions. +type testContext struct { + t *testing.T + db database.DB + bucketDepth int + isWritable bool + blocks []*btcutil.Block +} + +// keyPair houses a key/value pair. It is used over maps so ordering can be +// maintained. +type keyPair struct { + key []byte + value []byte +} + +// lookupKey is a convenience method to lookup the requested key from the +// provided keypair slice along with whether or not the key was found. +func lookupKey(key []byte, values []keyPair) ([]byte, bool) { + for _, item := range values { + if bytes.Equal(item.key, key) { + return item.value, true + } + } + + return nil, false +} + +// toGetValues returns a copy of the provided keypairs with all of the nil +// values set to an empty byte slice. This is used to ensure that keys set to +// nil values result in empty byte slices when retrieved instead of nil. +func toGetValues(values []keyPair) []keyPair { + ret := make([]keyPair, len(values)) + copy(ret, values) + for i := range ret { + if ret[i].value == nil { + ret[i].value = make([]byte, 0) + } + } + return ret +} + +// rollbackValues returns a copy of the provided keypairs with all values set to +// nil. This is used to test that values are properly rolled back. +func rollbackValues(values []keyPair) []keyPair { + ret := make([]keyPair, len(values)) + copy(ret, values) + for i := range ret { + ret[i].value = nil + } + return ret +} + +// testCursorKeyPair checks that the provide key and value match the expected +// keypair at the provided index. It also ensures the index is in range for the +// provided slice of expected keypairs. +func testCursorKeyPair(tc *testContext, k, v []byte, index int, values []keyPair) bool { + if index >= len(values) || index < 0 { + tc.t.Errorf("Cursor: exceeded the expected range of values - "+ + "index %d, num values %d", index, len(values)) + return false + } + + pair := &values[index] + if !bytes.Equal(k, pair.key) { + tc.t.Errorf("Mismatched cursor key: index %d does not match "+ + "the expected key - got %q, want %q", index, k, + pair.key) + return false + } + if !bytes.Equal(v, pair.value) { + tc.t.Errorf("Mismatched cursor value: index %d does not match "+ + "the expected value - got %q, want %q", index, v, + pair.value) + return false + } + + return true +} + +// testGetValues checks that all of the provided key/value pairs can be +// retrieved from the database and the retrieved values match the provided +// values. +func testGetValues(tc *testContext, bucket database.Bucket, values []keyPair) bool { + for _, item := range values { + gotValue := bucket.Get(item.key) + if !reflect.DeepEqual(gotValue, item.value) { + tc.t.Errorf("Get: unexpected value for %q - got %q, "+ + "want %q", item.key, gotValue, item.value) + return false + } + } + + return true +} + +// testPutValues stores all of the provided key/value pairs in the provided +// bucket while checking for errors. +func testPutValues(tc *testContext, bucket database.Bucket, values []keyPair) bool { + for _, item := range values { + if err := bucket.Put(item.key, item.value); err != nil { + tc.t.Errorf("Put: unexpected error: %v", err) + return false + } + } + + return true +} + +// testDeleteValues removes all of the provided key/value pairs from the +// provided bucket. +func testDeleteValues(tc *testContext, bucket database.Bucket, values []keyPair) bool { + for _, item := range values { + if err := bucket.Delete(item.key); err != nil { + tc.t.Errorf("Delete: unexpected error: %v", err) + return false + } + } + + return true +} + +// testCursorInterface ensures the cursor itnerface is working properly by +// exercising all of its functions on the passed bucket. +func testCursorInterface(tc *testContext, bucket database.Bucket) bool { + // Ensure a cursor can be obtained for the bucket. + cursor := bucket.Cursor() + if cursor == nil { + tc.t.Error("Bucket.Cursor: unexpected nil cursor returned") + return false + } + + // Ensure the cursor returns the same bucket it was created for. + if cursor.Bucket() != bucket { + tc.t.Error("Cursor.Bucket: does not match the bucket it was " + + "created for") + return false + } + + if tc.isWritable { + unsortedValues := []keyPair{ + {[]byte("cursor"), []byte("val1")}, + {[]byte("abcd"), []byte("val2")}, + {[]byte("bcd"), []byte("val3")}, + {[]byte("defg"), nil}, + } + sortedValues := []keyPair{ + {[]byte("abcd"), []byte("val2")}, + {[]byte("bcd"), []byte("val3")}, + {[]byte("cursor"), []byte("val1")}, + {[]byte("defg"), nil}, + } + + // Store the values to be used in the cursor tests in unsorted + // order and ensure they were actually stored. + if !testPutValues(tc, bucket, unsortedValues) { + return false + } + if !testGetValues(tc, bucket, toGetValues(unsortedValues)) { + return false + } + + // Ensure the cursor returns all items in byte-sorted order when + // iterating forward. + curIdx := 0 + for ok := cursor.First(); ok; ok = cursor.Next() { + k, v := cursor.Key(), cursor.Value() + if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) { + return false + } + curIdx++ + } + if curIdx != len(unsortedValues) { + tc.t.Errorf("Cursor: expected to iterate %d values, "+ + "but only iterated %d", len(unsortedValues), + curIdx) + return false + } + + // Ensure the cursor returns all items in reverse byte-sorted + // order when iterating in reverse. + curIdx = len(sortedValues) - 1 + for ok := cursor.Last(); ok; ok = cursor.Prev() { + k, v := cursor.Key(), cursor.Value() + if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) { + return false + } + curIdx-- + } + if curIdx > -1 { + tc.t.Errorf("Reverse cursor: expected to iterate %d "+ + "values, but only iterated %d", + len(sortedValues), len(sortedValues)-(curIdx+1)) + return false + } + + // Ensure forward iteration works as expected after seeking. + middleIdx := (len(sortedValues) - 1) / 2 + seekKey := sortedValues[middleIdx].key + curIdx = middleIdx + for ok := cursor.Seek(seekKey); ok; ok = cursor.Next() { + k, v := cursor.Key(), cursor.Value() + if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) { + return false + } + curIdx++ + } + if curIdx != len(sortedValues) { + tc.t.Errorf("Cursor after seek: expected to iterate "+ + "%d values, but only iterated %d", + len(sortedValues)-middleIdx, curIdx-middleIdx) + return false + } + + // Ensure reverse iteration works as expected after seeking. + curIdx = middleIdx + for ok := cursor.Seek(seekKey); ok; ok = cursor.Prev() { + k, v := cursor.Key(), cursor.Value() + if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) { + return false + } + curIdx-- + } + if curIdx > -1 { + tc.t.Errorf("Reverse cursor after seek: expected to "+ + "iterate %d values, but only iterated %d", + len(sortedValues)-middleIdx, middleIdx-curIdx) + return false + } + + // Ensure the cursor deletes items properly. + if !cursor.First() { + tc.t.Errorf("Cursor.First: no value") + return false + } + k := cursor.Key() + if err := cursor.Delete(); err != nil { + tc.t.Errorf("Cursor.Delete: unexpected error: %v", err) + return false + } + if val := bucket.Get(k); val != nil { + tc.t.Errorf("Cursor.Delete: value for key %q was not "+ + "deleted", k) + return false + } + } + + return true +} + +// testNestedBucket reruns the testBucketInterface against a nested bucket along +// with a counter to only test a couple of level deep. +func testNestedBucket(tc *testContext, testBucket database.Bucket) bool { + // Don't go more than 2 nested levels deep. + if tc.bucketDepth > 1 { + return true + } + + tc.bucketDepth++ + defer func() { + tc.bucketDepth-- + }() + if !testBucketInterface(tc, testBucket) { + return false + } + + return true +} + +// testBucketInterface ensures the bucket interface is working properly by +// exercising all of its functions. This includes the cursor interface for the +// cursor returned from the bucket. +func testBucketInterface(tc *testContext, bucket database.Bucket) bool { + if bucket.Writable() != tc.isWritable { + tc.t.Errorf("Bucket writable state does not match.") + return false + } + + if tc.isWritable { + // keyValues holds the keys and values to use when putting + // values into the bucket. + keyValues := []keyPair{ + {[]byte("bucketkey1"), []byte("foo1")}, + {[]byte("bucketkey2"), []byte("foo2")}, + {[]byte("bucketkey3"), []byte("foo3")}, + {[]byte("bucketkey4"), nil}, + } + expectedKeyValues := toGetValues(keyValues) + if !testPutValues(tc, bucket, keyValues) { + return false + } + + if !testGetValues(tc, bucket, expectedKeyValues) { + return false + } + + // Ensure errors returned from the user-supplied ForEach + // function are returned. + forEachError := fmt.Errorf("example foreach error") + err := bucket.ForEach(func(k, v []byte) error { + return forEachError + }) + if err != forEachError { + tc.t.Errorf("ForEach: inner function error not "+ + "returned - got %v, want %v", err, forEachError) + return false + } + + // Iterate all of the keys using ForEach while making sure the + // stored values are the expected values. + keysFound := make(map[string]struct{}, len(keyValues)) + err = bucket.ForEach(func(k, v []byte) error { + wantV, found := lookupKey(k, expectedKeyValues) + if !found { + return fmt.Errorf("ForEach: key '%s' should "+ + "exist", k) + } + + if !reflect.DeepEqual(v, wantV) { + return fmt.Errorf("ForEach: value for key '%s' "+ + "does not match - got %s, want %s", k, + v, wantV) + } + + keysFound[string(k)] = struct{}{} + return nil + }) + if err != nil { + tc.t.Errorf("%v", err) + return false + } + + // Ensure all keys were iterated. + for _, item := range keyValues { + if _, ok := keysFound[string(item.key)]; !ok { + tc.t.Errorf("ForEach: key '%s' was not iterated "+ + "when it should have been", item.key) + return false + } + } + + // Delete the keys and ensure they were deleted. + if !testDeleteValues(tc, bucket, keyValues) { + return false + } + if !testGetValues(tc, bucket, rollbackValues(keyValues)) { + return false + } + + // Ensure creating a new bucket works as expected. + testBucketName := []byte("testbucket") + testBucket, err := bucket.CreateBucket(testBucketName) + if err != nil { + tc.t.Errorf("CreateBucket: unexpected error: %v", err) + return false + } + if !testNestedBucket(tc, testBucket) { + return false + } + + // Ensure errors returned from the user-supplied ForEachBucket + // function are returned. + err = bucket.ForEachBucket(func(k []byte) error { + return forEachError + }) + if err != forEachError { + tc.t.Errorf("ForEachBucket: inner function error not "+ + "returned - got %v, want %v", err, forEachError) + return false + } + + // Ensure creating a bucket that already exists fails with the + // expected error. + wantErrCode := database.ErrBucketExists + _, err = bucket.CreateBucket(testBucketName) + if !checkDbError(tc.t, "CreateBucket", err, wantErrCode) { + return false + } + + // Ensure CreateBucketIfNotExists returns an existing bucket. + testBucket, err = bucket.CreateBucketIfNotExists(testBucketName) + if err != nil { + tc.t.Errorf("CreateBucketIfNotExists: unexpected "+ + "error: %v", err) + return false + } + if !testNestedBucket(tc, testBucket) { + return false + } + + // Ensure retrieving an existing bucket works as expected. + testBucket = bucket.Bucket(testBucketName) + if !testNestedBucket(tc, testBucket) { + return false + } + + // Ensure deleting a bucket works as intended. + if err := bucket.DeleteBucket(testBucketName); err != nil { + tc.t.Errorf("DeleteBucket: unexpected error: %v", err) + return false + } + if b := bucket.Bucket(testBucketName); b != nil { + tc.t.Errorf("DeleteBucket: bucket '%s' still exists", + testBucketName) + return false + } + + // Ensure deleting a bucket that doesn't exist returns the + // expected error. + wantErrCode = database.ErrBucketNotFound + err = bucket.DeleteBucket(testBucketName) + if !checkDbError(tc.t, "DeleteBucket", err, wantErrCode) { + return false + } + + // Ensure CreateBucketIfNotExists creates a new bucket when + // it doesn't already exist. + testBucket, err = bucket.CreateBucketIfNotExists(testBucketName) + if err != nil { + tc.t.Errorf("CreateBucketIfNotExists: unexpected "+ + "error: %v", err) + return false + } + if !testNestedBucket(tc, testBucket) { + return false + } + + // Ensure the cursor interface works as expected. + if !testCursorInterface(tc, testBucket) { + return false + } + + // Delete the test bucket to avoid leaving it around for future + // calls. + if err := bucket.DeleteBucket(testBucketName); err != nil { + tc.t.Errorf("DeleteBucket: unexpected error: %v", err) + return false + } + if b := bucket.Bucket(testBucketName); b != nil { + tc.t.Errorf("DeleteBucket: bucket '%s' still exists", + testBucketName) + return false + } + } else { + // Put should fail with bucket that is not writable. + testName := "unwritable tx put" + wantErrCode := database.ErrTxNotWritable + failBytes := []byte("fail") + err := bucket.Put(failBytes, failBytes) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Delete should fail with bucket that is not writable. + testName = "unwritable tx delete" + err = bucket.Delete(failBytes) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // CreateBucket should fail with bucket that is not writable. + testName = "unwritable tx create bucket" + _, err = bucket.CreateBucket(failBytes) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // CreateBucketIfNotExists should fail with bucket that is not + // writable. + testName = "unwritable tx create bucket if not exists" + _, err = bucket.CreateBucketIfNotExists(failBytes) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // DeleteBucket should fail with bucket that is not writable. + testName = "unwritable tx delete bucket" + err = bucket.DeleteBucket(failBytes) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure the cursor interface works as expected with read-only + // buckets. + if !testCursorInterface(tc, bucket) { + return false + } + } + + return true +} + +// rollbackOnPanic rolls the passed transaction back if the code in the calling +// function panics. This is useful in case the tests unexpectedly panic which +// would leave any manually created transactions with the database mutex locked +// thereby leading to a deadlock and masking the real reason for the panic. It +// also logs a test error and repanics so the original panic can be traced. +func rollbackOnPanic(t *testing.T, tx database.Tx) { + if err := recover(); err != nil { + t.Errorf("Unexpected panic: %v", err) + _ = tx.Rollback() + panic(err) + } +} + +// testMetadataManualTxInterface ensures that the manual transactions metadata +// interface works as expected. +func testMetadataManualTxInterface(tc *testContext) bool { + // populateValues tests that populating values works as expected. + // + // When the writable flag is false, a read-only tranasction is created, + // standard bucket tests for read-only transactions are performed, and + // the Commit function is checked to ensure it fails as expected. + // + // Otherwise, a read-write transaction is created, the values are + // written, standard bucket tests for read-write transactions are + // performed, and then the transaction is either committed or rolled + // back depending on the flag. + bucket1Name := []byte("bucket1") + populateValues := func(writable, rollback bool, putValues []keyPair) bool { + tx, err := tc.db.Begin(writable) + if err != nil { + tc.t.Errorf("Begin: unexpected error %v", err) + return false + } + defer rollbackOnPanic(tc.t, tx) + + metadataBucket := tx.Metadata() + if metadataBucket == nil { + tc.t.Errorf("Metadata: unexpected nil bucket") + _ = tx.Rollback() + return false + } + + bucket1 := metadataBucket.Bucket(bucket1Name) + if bucket1 == nil { + tc.t.Errorf("Bucket1: unexpected nil bucket") + return false + } + + tc.isWritable = writable + if !testBucketInterface(tc, bucket1) { + _ = tx.Rollback() + return false + } + + if !writable { + // The transaction is not writable, so it should fail + // the commit. + testName := "unwritable tx commit" + wantErrCode := database.ErrTxNotWritable + err := tx.Commit() + if !checkDbError(tc.t, testName, err, wantErrCode) { + _ = tx.Rollback() + return false + } + } else { + if !testPutValues(tc, bucket1, putValues) { + return false + } + + if rollback { + // Rollback the transaction. + if err := tx.Rollback(); err != nil { + tc.t.Errorf("Rollback: unexpected "+ + "error %v", err) + return false + } + } else { + // The commit should succeed. + if err := tx.Commit(); err != nil { + tc.t.Errorf("Commit: unexpected error "+ + "%v", err) + return false + } + } + } + + return true + } + + // checkValues starts a read-only transaction and checks that all of + // the key/value pairs specified in the expectedValues parameter match + // what's in the database. + checkValues := func(expectedValues []keyPair) bool { + tx, err := tc.db.Begin(false) + if err != nil { + tc.t.Errorf("Begin: unexpected error %v", err) + return false + } + defer rollbackOnPanic(tc.t, tx) + + metadataBucket := tx.Metadata() + if metadataBucket == nil { + tc.t.Errorf("Metadata: unexpected nil bucket") + _ = tx.Rollback() + return false + } + + bucket1 := metadataBucket.Bucket(bucket1Name) + if bucket1 == nil { + tc.t.Errorf("Bucket1: unexpected nil bucket") + return false + } + + if !testGetValues(tc, bucket1, expectedValues) { + _ = tx.Rollback() + return false + } + + // Rollback the read-only transaction. + if err := tx.Rollback(); err != nil { + tc.t.Errorf("Commit: unexpected error %v", err) + return false + } + + return true + } + + // deleteValues starts a read-write transaction and deletes the keys + // in the passed key/value pairs. + deleteValues := func(values []keyPair) bool { + tx, err := tc.db.Begin(true) + if err != nil { + + } + defer rollbackOnPanic(tc.t, tx) + + metadataBucket := tx.Metadata() + if metadataBucket == nil { + tc.t.Errorf("Metadata: unexpected nil bucket") + _ = tx.Rollback() + return false + } + + bucket1 := metadataBucket.Bucket(bucket1Name) + if bucket1 == nil { + tc.t.Errorf("Bucket1: unexpected nil bucket") + return false + } + + // Delete the keys and ensure they were deleted. + if !testDeleteValues(tc, bucket1, values) { + _ = tx.Rollback() + return false + } + if !testGetValues(tc, bucket1, rollbackValues(values)) { + _ = tx.Rollback() + return false + } + + // Commit the changes and ensure it was successful. + if err := tx.Commit(); err != nil { + tc.t.Errorf("Commit: unexpected error %v", err) + return false + } + + return true + } + + // keyValues holds the keys and values to use when putting values into a + // bucket. + var keyValues = []keyPair{ + {[]byte("umtxkey1"), []byte("foo1")}, + {[]byte("umtxkey2"), []byte("foo2")}, + {[]byte("umtxkey3"), []byte("foo3")}, + {[]byte("umtxkey4"), nil}, + } + + // Ensure that attempting populating the values using a read-only + // transaction fails as expected. + if !populateValues(false, true, keyValues) { + return false + } + if !checkValues(rollbackValues(keyValues)) { + return false + } + + // Ensure that attempting populating the values using a read-write + // transaction and then rolling it back yields the expected values. + if !populateValues(true, true, keyValues) { + return false + } + if !checkValues(rollbackValues(keyValues)) { + return false + } + + // Ensure that attempting populating the values using a read-write + // transaction and then committing it stores the expected values. + if !populateValues(true, false, keyValues) { + return false + } + if !checkValues(toGetValues(keyValues)) { + return false + } + + // Clean up the keys. + if !deleteValues(keyValues) { + return false + } + + return true +} + +// testManagedTxPanics ensures calling Rollback of Commit inside a managed +// transaction panics. +func testManagedTxPanics(tc *testContext) bool { + testPanic := func(fn func()) (paniced bool) { + // Setup a defer to catch the expected panic and update the + // return variable. + defer func() { + if err := recover(); err != nil { + paniced = true + } + }() + + fn() + return false + } + + // Ensure calling Commit on a managed read-only transaction panics. + paniced := testPanic(func() { + tc.db.View(func(tx database.Tx) error { + tx.Commit() + return nil + }) + }) + if !paniced { + tc.t.Error("Commit called inside View did not panic") + return false + } + + // Ensure calling Rollback on a managed read-only transaction panics. + paniced = testPanic(func() { + tc.db.View(func(tx database.Tx) error { + tx.Rollback() + return nil + }) + }) + if !paniced { + tc.t.Error("Rollback called inside View did not panic") + return false + } + + // Ensure calling Commit on a managed read-write transaction panics. + paniced = testPanic(func() { + tc.db.Update(func(tx database.Tx) error { + tx.Commit() + return nil + }) + }) + if !paniced { + tc.t.Error("Commit called inside Update did not panic") + return false + } + + // Ensure calling Rollback on a managed read-write transaction panics. + paniced = testPanic(func() { + tc.db.Update(func(tx database.Tx) error { + tx.Rollback() + return nil + }) + }) + if !paniced { + tc.t.Error("Rollback called inside Update did not panic") + return false + } + + return true +} + +// testMetadataTxInterface tests all facets of the managed read/write and +// manual transaction metadata interfaces as well as the bucket interfaces under +// them. +func testMetadataTxInterface(tc *testContext) bool { + if !testManagedTxPanics(tc) { + return false + } + + bucket1Name := []byte("bucket1") + err := tc.db.Update(func(tx database.Tx) error { + _, err := tx.Metadata().CreateBucket(bucket1Name) + return err + }) + if err != nil { + tc.t.Errorf("Update: unexpected error creating bucket: %v", err) + return false + } + + if !testMetadataManualTxInterface(tc) { + return false + } + + // keyValues holds the keys and values to use when putting values + // into a bucket. + keyValues := []keyPair{ + {[]byte("mtxkey1"), []byte("foo1")}, + {[]byte("mtxkey2"), []byte("foo2")}, + {[]byte("mtxkey3"), []byte("foo3")}, + {[]byte("mtxkey4"), nil}, + } + + // Test the bucket interface via a managed read-only transaction. + err = tc.db.View(func(tx database.Tx) error { + metadataBucket := tx.Metadata() + if metadataBucket == nil { + return fmt.Errorf("Metadata: unexpected nil bucket") + } + + bucket1 := metadataBucket.Bucket(bucket1Name) + if bucket1 == nil { + return fmt.Errorf("Bucket1: unexpected nil bucket") + } + + tc.isWritable = false + if !testBucketInterface(tc, bucket1) { + return errSubTestFail + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("%v", err) + } + return false + } + + // Ensure errors returned from the user-supplied View function are + // returned. + viewError := fmt.Errorf("example view error") + err = tc.db.View(func(tx database.Tx) error { + return viewError + }) + if err != viewError { + tc.t.Errorf("View: inner function error not returned - got "+ + "%v, want %v", err, viewError) + return false + } + + // Test the bucket interface via a managed read-write transaction. + // Also, put a series of values and force a rollback so the following + // code can ensure the values were not stored. + forceRollbackError := fmt.Errorf("force rollback") + err = tc.db.Update(func(tx database.Tx) error { + metadataBucket := tx.Metadata() + if metadataBucket == nil { + return fmt.Errorf("Metadata: unexpected nil bucket") + } + + bucket1 := metadataBucket.Bucket(bucket1Name) + if bucket1 == nil { + return fmt.Errorf("Bucket1: unexpected nil bucket") + } + + tc.isWritable = true + if !testBucketInterface(tc, bucket1) { + return errSubTestFail + } + + if !testPutValues(tc, bucket1, keyValues) { + return errSubTestFail + } + + // Return an error to force a rollback. + return forceRollbackError + }) + if err != forceRollbackError { + if err == errSubTestFail { + return false + } + + tc.t.Errorf("Update: inner function error not returned - got "+ + "%v, want %v", err, forceRollbackError) + return false + } + + // Ensure the values that should not have been stored due to the forced + // rollback above were not actually stored. + err = tc.db.View(func(tx database.Tx) error { + metadataBucket := tx.Metadata() + if metadataBucket == nil { + return fmt.Errorf("Metadata: unexpected nil bucket") + } + + if !testGetValues(tc, metadataBucket, rollbackValues(keyValues)) { + return errSubTestFail + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("%v", err) + } + return false + } + + // Store a series of values via a managed read-write transaction. + err = tc.db.Update(func(tx database.Tx) error { + metadataBucket := tx.Metadata() + if metadataBucket == nil { + return fmt.Errorf("Metadata: unexpected nil bucket") + } + + bucket1 := metadataBucket.Bucket(bucket1Name) + if bucket1 == nil { + return fmt.Errorf("Bucket1: unexpected nil bucket") + } + + if !testPutValues(tc, bucket1, keyValues) { + return errSubTestFail + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("%v", err) + } + return false + } + + // Ensure the values stored above were committed as expected. + err = tc.db.View(func(tx database.Tx) error { + metadataBucket := tx.Metadata() + if metadataBucket == nil { + return fmt.Errorf("Metadata: unexpected nil bucket") + } + + bucket1 := metadataBucket.Bucket(bucket1Name) + if bucket1 == nil { + return fmt.Errorf("Bucket1: unexpected nil bucket") + } + + if !testGetValues(tc, bucket1, toGetValues(keyValues)) { + return errSubTestFail + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("%v", err) + } + return false + } + + // Clean up the values stored above in a managed read-write transaction. + err = tc.db.Update(func(tx database.Tx) error { + metadataBucket := tx.Metadata() + if metadataBucket == nil { + return fmt.Errorf("Metadata: unexpected nil bucket") + } + + bucket1 := metadataBucket.Bucket(bucket1Name) + if bucket1 == nil { + return fmt.Errorf("Bucket1: unexpected nil bucket") + } + + if !testDeleteValues(tc, bucket1, keyValues) { + return errSubTestFail + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("%v", err) + } + return false + } + + return true +} + +// testFetchBlockIOMissing ensures that all of the block retrieval API functions +// work as expected when requesting blocks that don't exist. +func testFetchBlockIOMissing(tc *testContext, tx database.Tx) bool { + wantErrCode := database.ErrBlockNotFound + + // --------------------- + // Non-bulk Block IO API + // --------------------- + + // Test the individual block APIs one block at a time to ensure they + // return the expected error. Also, build the data needed to test the + // bulk APIs below while looping. + allBlockHashes := make([]wire.ShaHash, len(tc.blocks)) + allBlockRegions := make([]database.BlockRegion, len(tc.blocks)) + for i, block := range tc.blocks { + blockHash := block.Sha() + allBlockHashes[i] = *blockHash + + txLocs, err := block.TxLoc() + if err != nil { + tc.t.Errorf("block.TxLoc(%d): unexpected error: %v", i, + err) + return false + } + + // Ensure FetchBlock returns expected error. + testName := fmt.Sprintf("FetchBlock #%d on missing block", i) + _, err = tx.FetchBlock(blockHash) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure FetchBlockHeader returns expected error. + testName = fmt.Sprintf("FetchBlockHeader #%d on missing block", + i) + _, err = tx.FetchBlockHeader(blockHash) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure the first transaction fetched as a block region from + // the database returns the expected error. + region := database.BlockRegion{ + Hash: blockHash, + Offset: uint32(txLocs[0].TxStart), + Len: uint32(txLocs[0].TxLen), + } + allBlockRegions[i] = region + _, err = tx.FetchBlockRegion(®ion) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure HasBlock returns false. + hasBlock, err := tx.HasBlock(blockHash) + if err != nil { + tc.t.Errorf("HasBlock #%d: unexpected err: %v", i, err) + return false + } + if hasBlock { + tc.t.Errorf("HasBlock #%d: should not have block", i) + return false + } + } + + // ----------------- + // Bulk Block IO API + // ----------------- + + // Ensure FetchBlocks returns expected error. + testName := "FetchBlocks on missing blocks" + _, err := tx.FetchBlocks(allBlockHashes) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure FetchBlockHeaders returns expected error. + testName = "FetchBlockHeaders on missing blocks" + _, err = tx.FetchBlockHeaders(allBlockHashes) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure FetchBlockRegions returns expected error. + testName = "FetchBlockRegions on missing blocks" + _, err = tx.FetchBlockRegions(allBlockRegions) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure HasBlocks returns false for all blocks. + hasBlocks, err := tx.HasBlocks(allBlockHashes) + if err != nil { + tc.t.Errorf("HasBlocks: unexpected err: %v", err) + } + for i, hasBlock := range hasBlocks { + if hasBlock { + tc.t.Errorf("HasBlocks #%d: should not have block", i) + return false + } + } + + return true +} + +// testFetchBlockIO ensures all of the block retrieval API functions work as +// expected for the provide set of blocks. The blocks must already be stored in +// the database, or at least stored into the the passed transaction. It also +// tests several error conditions such as ensuring the expected errors are +// returned when fetching blocks, headers, and regions that don't exist. +func testFetchBlockIO(tc *testContext, tx database.Tx) bool { + // --------------------- + // Non-bulk Block IO API + // --------------------- + + // Test the individual block APIs one block at a time. Also, build the + // data needed to test the bulk APIs below while looping. + allBlockHashes := make([]wire.ShaHash, len(tc.blocks)) + allBlockBytes := make([][]byte, len(tc.blocks)) + allBlockTxLocs := make([][]wire.TxLoc, len(tc.blocks)) + allBlockRegions := make([]database.BlockRegion, len(tc.blocks)) + for i, block := range tc.blocks { + blockHash := block.Sha() + allBlockHashes[i] = *blockHash + + blockBytes, err := block.Bytes() + if err != nil { + tc.t.Errorf("block.Bytes(%d): unexpected error: %v", i, + err) + return false + } + allBlockBytes[i] = blockBytes + + txLocs, err := block.TxLoc() + if err != nil { + tc.t.Errorf("block.TxLoc(%d): unexpected error: %v", i, + err) + return false + } + allBlockTxLocs[i] = txLocs + + // Ensure the block data fetched from the database matches the + // expected bytes. + gotBlockBytes, err := tx.FetchBlock(blockHash) + if err != nil { + tc.t.Errorf("FetchBlock(%s): unexpected error: %v", + blockHash, err) + return false + } + if !bytes.Equal(gotBlockBytes, blockBytes) { + tc.t.Errorf("FetchBlock(%s): bytes mismatch: got %x, "+ + "want %x", blockHash, gotBlockBytes, blockBytes) + return false + } + + // Ensure the block header fetched from the database matches the + // expected bytes. + wantHeaderBytes := blockBytes[0:wire.MaxBlockHeaderPayload] + gotHeaderBytes, err := tx.FetchBlockHeader(blockHash) + if err != nil { + tc.t.Errorf("FetchBlockHeader(%s): unexpected error: %v", + blockHash, err) + return false + } + if !bytes.Equal(gotHeaderBytes, wantHeaderBytes) { + tc.t.Errorf("FetchBlockHeader(%s): bytes mismatch: "+ + "got %x, want %x", blockHash, gotHeaderBytes, + wantHeaderBytes) + return false + } + + // Ensure the first transaction fetched as a block region from + // the database matches the expected bytes. + region := database.BlockRegion{ + Hash: blockHash, + Offset: uint32(txLocs[0].TxStart), + Len: uint32(txLocs[0].TxLen), + } + allBlockRegions[i] = region + endRegionOffset := region.Offset + region.Len + wantRegionBytes := blockBytes[region.Offset:endRegionOffset] + gotRegionBytes, err := tx.FetchBlockRegion(®ion) + if err != nil { + tc.t.Errorf("FetchBlockRegion(%s): unexpected error: %v", + blockHash, err) + return false + } + if !bytes.Equal(gotRegionBytes, wantRegionBytes) { + tc.t.Errorf("FetchBlockRegion(%s): bytes mismatch: "+ + "got %x, want %x", blockHash, gotRegionBytes, + wantRegionBytes) + return false + } + + // Ensure the block header fetched from the database matches the + // expected bytes. + hasBlock, err := tx.HasBlock(blockHash) + if err != nil { + tc.t.Errorf("HasBlock(%s): unexpected error: %v", + blockHash, err) + return false + } + if !hasBlock { + tc.t.Errorf("HasBlock(%s): database claims it doesn't "+ + "have the block when it should", blockHash) + return false + } + + // ----------------------- + // Invalid blocks/regions. + // ----------------------- + + // Ensure fetching a block that doesn't exist returns the + // expected error. + badBlockHash := &wire.ShaHash{} + testName := fmt.Sprintf("FetchBlock(%s) invalid block", + badBlockHash) + wantErrCode := database.ErrBlockNotFound + _, err = tx.FetchBlock(badBlockHash) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure fetching a block header that doesn't exist returns + // the expected error. + testName = fmt.Sprintf("FetchBlockHeader(%s) invalid block", + badBlockHash) + _, err = tx.FetchBlockHeader(badBlockHash) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure fetching a block region in a block that doesn't exist + // return the expected error. + testName = fmt.Sprintf("FetchBlockRegion(%s) invalid hash", + badBlockHash) + wantErrCode = database.ErrBlockNotFound + region.Hash = badBlockHash + region.Offset = ^uint32(0) + _, err = tx.FetchBlockRegion(®ion) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure fetching a block region that is out of bounds returns + // the expected error. + testName = fmt.Sprintf("FetchBlockRegion(%s) invalid region", + blockHash) + wantErrCode = database.ErrBlockRegionInvalid + region.Hash = blockHash + region.Offset = ^uint32(0) + _, err = tx.FetchBlockRegion(®ion) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + } + + // ----------------- + // Bulk Block IO API + // ----------------- + + // Ensure the bulk block data fetched from the database matches the + // expected bytes. + blockData, err := tx.FetchBlocks(allBlockHashes) + if err != nil { + tc.t.Errorf("FetchBlocks: unexpected error: %v", err) + return false + } + if len(blockData) != len(allBlockBytes) { + tc.t.Errorf("FetchBlocks: unexpected number of results - got "+ + "%d, want %d", len(blockData), len(allBlockBytes)) + return false + } + for i := 0; i < len(blockData); i++ { + blockHash := allBlockHashes[i] + wantBlockBytes := allBlockBytes[i] + gotBlockBytes := blockData[i] + if !bytes.Equal(gotBlockBytes, wantBlockBytes) { + tc.t.Errorf("FetchBlocks(%s): bytes mismatch: got %x, "+ + "want %x", blockHash, gotBlockBytes, + wantBlockBytes) + return false + } + } + + // Ensure the bulk block headers fetched from the database match the + // expected bytes. + blockHeaderData, err := tx.FetchBlockHeaders(allBlockHashes) + if err != nil { + tc.t.Errorf("FetchBlockHeaders: unexpected error: %v", err) + return false + } + if len(blockHeaderData) != len(allBlockBytes) { + tc.t.Errorf("FetchBlockHeaders: unexpected number of results "+ + "- got %d, want %d", len(blockHeaderData), + len(allBlockBytes)) + return false + } + for i := 0; i < len(blockHeaderData); i++ { + blockHash := allBlockHashes[i] + wantHeaderBytes := allBlockBytes[i][0:wire.MaxBlockHeaderPayload] + gotHeaderBytes := blockHeaderData[i] + if !bytes.Equal(gotHeaderBytes, wantHeaderBytes) { + tc.t.Errorf("FetchBlockHeaders(%s): bytes mismatch: "+ + "got %x, want %x", blockHash, gotHeaderBytes, + wantHeaderBytes) + return false + } + } + + // Ensure the first transaction of every block fetched in bulk block + // regions from the database matches the expected bytes. + allRegionBytes, err := tx.FetchBlockRegions(allBlockRegions) + if err != nil { + tc.t.Errorf("FetchBlockRegions: unexpected error: %v", err) + return false + + } + if len(allRegionBytes) != len(allBlockRegions) { + tc.t.Errorf("FetchBlockRegions: unexpected number of results "+ + "- got %d, want %d", len(allRegionBytes), + len(allBlockRegions)) + return false + } + for i, gotRegionBytes := range allRegionBytes { + region := &allBlockRegions[i] + endRegionOffset := region.Offset + region.Len + wantRegionBytes := blockData[i][region.Offset:endRegionOffset] + if !bytes.Equal(gotRegionBytes, wantRegionBytes) { + tc.t.Errorf("FetchBlockRegions(%d): bytes mismatch: "+ + "got %x, want %x", i, gotRegionBytes, + wantRegionBytes) + return false + } + } + + // Ensure the bulk determination of whether a set of block hashes are in + // the database returns true for all loaded blocks. + hasBlocks, err := tx.HasBlocks(allBlockHashes) + if err != nil { + tc.t.Errorf("HasBlocks: unexpected error: %v", err) + return false + } + for i, hasBlock := range hasBlocks { + if !hasBlock { + tc.t.Errorf("HasBlocks(%d): should have block", i) + return false + } + } + + // ----------------------- + // Invalid blocks/regions. + // ----------------------- + + // Ensure fetching blocks for which one doesn't exist returns the + // expected error. + testName := "FetchBlocks invalid hash" + badBlockHashes := make([]wire.ShaHash, len(allBlockHashes)+1) + copy(badBlockHashes, allBlockHashes) + badBlockHashes[len(badBlockHashes)-1] = wire.ShaHash{} + wantErrCode := database.ErrBlockNotFound + _, err = tx.FetchBlocks(badBlockHashes) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure fetching block headers for which one doesn't exist returns the + // expected error. + testName = "FetchBlockHeaders invalid hash" + _, err = tx.FetchBlockHeaders(badBlockHashes) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure fetching block regions for which one of blocks doesn't exist + // returns expected error. + testName = "FetchBlockRegions invalid hash" + badBlockRegions := make([]database.BlockRegion, len(allBlockRegions)+1) + copy(badBlockRegions, allBlockRegions) + badBlockRegions[len(badBlockRegions)-1].Hash = &wire.ShaHash{} + wantErrCode = database.ErrBlockNotFound + _, err = tx.FetchBlockRegions(badBlockRegions) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure fetching block regions that are out of bounds returns the + // expected error. + testName = "FetchBlockRegions invalid regions" + badBlockRegions = badBlockRegions[:len(badBlockRegions)-1] + for i := range badBlockRegions { + badBlockRegions[i].Offset = ^uint32(0) + } + wantErrCode = database.ErrBlockRegionInvalid + _, err = tx.FetchBlockRegions(badBlockRegions) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + return true +} + +// testBlockIOTxInterface ensures that the block IO interface works as expected +// for both managed read/write and manual transactions. This function leaves +// all of the stored blocks in the database. +func testBlockIOTxInterface(tc *testContext) bool { + // Ensure attempting to store a block with a read-only transaction fails + // with the expected error. + err := tc.db.View(func(tx database.Tx) error { + wantErrCode := database.ErrTxNotWritable + for i, block := range tc.blocks { + testName := fmt.Sprintf("StoreBlock(%d) on ro tx", i) + err := tx.StoreBlock(block) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return errSubTestFail + } + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("%v", err) + } + return false + } + + // Populate the database with loaded blocks and ensure all of the data + // fetching APIs work properly on them within the transaction before a + // commit or rollback. Then, force a rollback so the code below can + // ensure none of the data actually gets stored. + forceRollbackError := fmt.Errorf("force rollback") + err = tc.db.Update(func(tx database.Tx) error { + // Store all blocks in the same transaction. + for i, block := range tc.blocks { + err := tx.StoreBlock(block) + if err != nil { + tc.t.Errorf("StoreBlock #%d: unexpected error: "+ + "%v", i, err) + return errSubTestFail + } + } + + // Ensure attempting to store the same block again, before the + // transaction has been committed, returns the expected error. + wantErrCode := database.ErrBlockExists + for i, block := range tc.blocks { + testName := fmt.Sprintf("duplicate block entry #%d "+ + "(before commit)", i) + err := tx.StoreBlock(block) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return errSubTestFail + } + } + + // Ensure that all data fetches from the stored blocks before + // the transaction has been committed work as expected. + if !testFetchBlockIO(tc, tx) { + return errSubTestFail + } + + return forceRollbackError + }) + if err != forceRollbackError { + if err == errSubTestFail { + return false + } + + tc.t.Errorf("Update: inner function error not returned - got "+ + "%v, want %v", err, forceRollbackError) + return false + } + + // Ensure rollback was successful + err = tc.db.View(func(tx database.Tx) error { + if !testFetchBlockIOMissing(tc, tx) { + return errSubTestFail + } + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("%v", err) + } + return false + } + + // Populate the database with loaded blocks and ensure all of the data + // fetching APIs work properly. + err = tc.db.Update(func(tx database.Tx) error { + // Store a bunch of blocks in the same transaction. + for i, block := range tc.blocks { + err := tx.StoreBlock(block) + if err != nil { + tc.t.Errorf("StoreBlock #%d: unexpected error: "+ + "%v", i, err) + return errSubTestFail + } + } + + // Ensure attempting to store the same block again while in the + // same transaction, but before it has been committed, returns + // the expected error. + for i, block := range tc.blocks { + testName := fmt.Sprintf("duplicate block entry #%d "+ + "(before commit)", i) + wantErrCode := database.ErrBlockExists + err := tx.StoreBlock(block) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return errSubTestFail + } + } + + // Ensure that all data fetches from the stored blocks before + // the transaction has been committed work as expected. + if !testFetchBlockIO(tc, tx) { + return errSubTestFail + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("%v", err) + } + return false + } + + // Ensure all data fetch tests work as expected using a managed + // read-only transaction after the data was successfully committed + // above. + err = tc.db.View(func(tx database.Tx) error { + if !testFetchBlockIO(tc, tx) { + return errSubTestFail + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("%v", err) + } + return false + } + + // Ensure all data fetch tests work as expected using a managed + // read-write transaction after the data was successfully committed + // above. + err = tc.db.Update(func(tx database.Tx) error { + if !testFetchBlockIO(tc, tx) { + return errSubTestFail + } + + // Ensure attempting to store existing blocks again returns the + // expected error. Note that this is different from the + // previous version since this is a new transaction after the + // blocks have been committed. + wantErrCode := database.ErrBlockExists + for i, block := range tc.blocks { + testName := fmt.Sprintf("duplicate block entry #%d "+ + "(before commit)", i) + err := tx.StoreBlock(block) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return errSubTestFail + } + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("%v", err) + } + return false + } + + return true +} + +// testClosedTxInterface ensures that both the metadata and block IO API +// functions behave as expected when attempted against a closed transaction. +func testClosedTxInterface(tc *testContext, tx database.Tx) bool { + wantErrCode := database.ErrTxClosed + bucket := tx.Metadata() + cursor := tx.Metadata().Cursor() + bucketName := []byte("closedtxbucket") + keyName := []byte("closedtxkey") + + // ------------ + // Metadata API + // ------------ + + // Ensure that attempting to get an existing bucket returns nil when the + // transaction is closed. + if b := bucket.Bucket(bucketName); b != nil { + tc.t.Errorf("Bucket: did not return nil on closed tx") + return false + } + + // Ensure CreateBucket returns expected error. + testName := "CreateBucket on closed tx" + _, err := bucket.CreateBucket(bucketName) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure CreateBucketIfNotExists returns expected error. + testName = "CreateBucketIfNotExists on closed tx" + _, err = bucket.CreateBucketIfNotExists(bucketName) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure Delete returns expected error. + testName = "Delete on closed tx" + err = bucket.Delete(keyName) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure DeleteBucket returns expected error. + testName = "DeleteBucket on closed tx" + err = bucket.DeleteBucket(bucketName) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure ForEach returns expected error. + testName = "ForEach on closed tx" + err = bucket.ForEach(nil) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure ForEachBucket returns expected error. + testName = "ForEachBucket on closed tx" + err = bucket.ForEachBucket(nil) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure Get returns expected error. + testName = "Get on closed tx" + if k := bucket.Get(keyName); k != nil { + tc.t.Errorf("Get: did not return nil on closed tx") + return false + } + + // Ensure Put returns expected error. + testName = "Put on closed tx" + err = bucket.Put(keyName, []byte("test")) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // ------------------- + // Metadata Cursor API + // ------------------- + + // Ensure attempting to get a bucket from a cursor on a closed tx gives + // back nil. + if b := cursor.Bucket(); b != nil { + tc.t.Error("Cursor.Bucket: returned non-nil on closed tx") + return false + } + + // Ensure Cursor.Delete returns expected error. + testName = "Cursor.Delete on closed tx" + err = cursor.Delete() + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure Cursor.First on a closed tx returns false and nil key/value. + if cursor.First() { + tc.t.Error("Cursor.First: claims ok on closed tx") + return false + } + if cursor.Key() != nil || cursor.Value() != nil { + tc.t.Error("Cursor.First: key and/or value are not nil on " + + "closed tx") + return false + } + + // Ensure Cursor.Last on a closed tx returns false and nil key/value. + if cursor.Last() { + tc.t.Error("Cursor.Last: claims ok on closed tx") + return false + } + if cursor.Key() != nil || cursor.Value() != nil { + tc.t.Error("Cursor.Last: key and/or value are not nil on " + + "closed tx") + return false + } + + // Ensure Cursor.Next on a closed tx returns false and nil key/value. + if cursor.Next() { + tc.t.Error("Cursor.Next: claims ok on closed tx") + return false + } + if cursor.Key() != nil || cursor.Value() != nil { + tc.t.Error("Cursor.Next: key and/or value are not nil on " + + "closed tx") + return false + } + + // Ensure Cursor.Prev on a closed tx returns false and nil key/value. + if cursor.Prev() { + tc.t.Error("Cursor.Prev: claims ok on closed tx") + return false + } + if cursor.Key() != nil || cursor.Value() != nil { + tc.t.Error("Cursor.Prev: key and/or value are not nil on " + + "closed tx") + return false + } + + // Ensure Cursor.Seek on a closed tx returns false and nil key/value. + if cursor.Seek([]byte{}) { + tc.t.Error("Cursor.Seek: claims ok on closed tx") + return false + } + if cursor.Key() != nil || cursor.Value() != nil { + tc.t.Error("Cursor.Seek: key and/or value are not nil on " + + "closed tx") + return false + } + + // --------------------- + // Non-bulk Block IO API + // --------------------- + + // Test the individual block APIs one block at a time to ensure they + // return the expected error. Also, build the data needed to test the + // bulk APIs below while looping. + allBlockHashes := make([]wire.ShaHash, len(tc.blocks)) + allBlockRegions := make([]database.BlockRegion, len(tc.blocks)) + for i, block := range tc.blocks { + blockHash := block.Sha() + allBlockHashes[i] = *blockHash + + txLocs, err := block.TxLoc() + if err != nil { + tc.t.Errorf("block.TxLoc(%d): unexpected error: %v", i, + err) + return false + } + + // Ensure StoreBlock returns expected error. + testName = "StoreBlock on closed tx" + err = tx.StoreBlock(block) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure FetchBlock returns expected error. + testName = fmt.Sprintf("FetchBlock #%d on closed tx", i) + _, err = tx.FetchBlock(blockHash) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure FetchBlockHeader returns expected error. + testName = fmt.Sprintf("FetchBlockHeader #%d on closed tx", i) + _, err = tx.FetchBlockHeader(blockHash) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure the first transaction fetched as a block region from + // the database returns the expected error. + region := database.BlockRegion{ + Hash: blockHash, + Offset: uint32(txLocs[0].TxStart), + Len: uint32(txLocs[0].TxLen), + } + allBlockRegions[i] = region + _, err = tx.FetchBlockRegion(®ion) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure HasBlock returns expected error. + testName = fmt.Sprintf("HasBlock #%d on closed tx", i) + _, err = tx.HasBlock(blockHash) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + } + + // ----------------- + // Bulk Block IO API + // ----------------- + + // Ensure FetchBlocks returns expected error. + testName = "FetchBlocks on closed tx" + _, err = tx.FetchBlocks(allBlockHashes) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure FetchBlockHeaders returns expected error. + testName = "FetchBlockHeaders on closed tx" + _, err = tx.FetchBlockHeaders(allBlockHashes) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure FetchBlockRegions returns expected error. + testName = "FetchBlockRegions on closed tx" + _, err = tx.FetchBlockRegions(allBlockRegions) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // Ensure HasBlocks returns expected error. + testName = "HasBlocks on closed tx" + _, err = tx.HasBlocks(allBlockHashes) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return false + } + + // --------------- + // Commit/Rollback + // --------------- + + // Ensure that attempting to rollback or commit a transaction that is + // already closed returns the expected error. + err = tx.Rollback() + if !checkDbError(tc.t, "closed tx rollback", err, wantErrCode) { + return false + } + err = tx.Commit() + if !checkDbError(tc.t, "closed tx commit", err, wantErrCode) { + return false + } + + return true +} + +// testTxClosed ensures that both the metadata and block IO API functions behave +// as expected when attempted against both read-only and read-write +// transactions. +func testTxClosed(tc *testContext) bool { + bucketName := []byte("closedtxbucket") + keyName := []byte("closedtxkey") + + // Start a transaction, create a bucket and key used for testing, and + // immediately perform a commit on it so it is closed. + tx, err := tc.db.Begin(true) + if err != nil { + tc.t.Errorf("Begin(true): unexpected error: %v", err) + return false + } + defer rollbackOnPanic(tc.t, tx) + if _, err := tx.Metadata().CreateBucket(bucketName); err != nil { + tc.t.Errorf("CreateBucket: unexpected error: %v", err) + return false + } + if err := tx.Metadata().Put(keyName, []byte("test")); err != nil { + tc.t.Errorf("Put: unexpected error: %v", err) + return false + } + if err := tx.Commit(); err != nil { + tc.t.Errorf("Commit: unexpected error: %v", err) + return false + } + + // Ensure invoking all of the functions on the closed read-write + // transaction behave as expected. + if !testClosedTxInterface(tc, tx) { + return false + } + + // Repeat the tests with a rolled-back read-only transaction. + tx, err = tc.db.Begin(false) + if err != nil { + tc.t.Errorf("Begin(false): unexpected error: %v", err) + return false + } + defer rollbackOnPanic(tc.t, tx) + if err := tx.Rollback(); err != nil { + tc.t.Errorf("Rollback: unexpected error: %v", err) + return false + } + + // Ensure invoking all of the functions on the closed read-only + // transaction behave as expected. + return testClosedTxInterface(tc, tx) +} + +// testConcurrecy ensure the database properly supports concurrent readers and +// only a single writer. It also ensures views act as snapshots at the time +// they are acquired. +func testConcurrecy(tc *testContext) bool { + // sleepTime is how long each of the concurrent readers should sleep to + // aid in detection of whether or not the data is actually being read + // concurrently. It starts with a sane lower bound. + var sleepTime = time.Millisecond * 250 + + // Determine about how long it takes for a single block read. When it's + // longer than the default minimum sleep time, adjust the sleep time to + // help prevent durations that are too short which would cause erroneous + // test failures on slower systems. + startTime := time.Now() + err := tc.db.View(func(tx database.Tx) error { + _, err := tx.FetchBlock(tc.blocks[0].Sha()) + if err != nil { + return err + } + return nil + }) + if err != nil { + tc.t.Errorf("Unexpected error in view: %v", err) + return false + } + elapsed := time.Now().Sub(startTime) + if sleepTime < elapsed { + sleepTime = elapsed + } + tc.t.Logf("Time to load block 0: %v, using sleep time: %v", elapsed, + sleepTime) + + // reader takes a block number to load and channel to return the result + // of the operation on. It is used below to launch multiple concurrent + // readers. + numReaders := len(tc.blocks) + resultChan := make(chan bool, numReaders) + reader := func(blockNum int) { + err := tc.db.View(func(tx database.Tx) error { + time.Sleep(sleepTime) + _, err := tx.FetchBlock(tc.blocks[blockNum].Sha()) + if err != nil { + return err + } + return nil + }) + if err != nil { + tc.t.Errorf("Unexpected error in concurrent view: %v", + err) + resultChan <- false + } + resultChan <- true + } + + // Start up several concurrent readers for the same block and wait for + // the results. + startTime = time.Now() + for i := 0; i < numReaders; i++ { + go reader(0) + } + for i := 0; i < numReaders; i++ { + if result := <-resultChan; !result { + return false + } + } + elapsed = time.Now().Sub(startTime) + tc.t.Logf("%d concurrent reads of same block elapsed: %v", numReaders, + elapsed) + + // Consider it a failure if it took longer than half the time it would + // take with no concurrency. + if elapsed > sleepTime*time.Duration(numReaders/2) { + tc.t.Errorf("Concurrent views for same block did not appear to "+ + "run simultaneously: elapsed %v", elapsed) + return false + } + + // Start up several concurrent readers for different blocks and wait for + // the results. + startTime = time.Now() + for i := 0; i < numReaders; i++ { + go reader(i) + } + for i := 0; i < numReaders; i++ { + if result := <-resultChan; !result { + return false + } + } + elapsed = time.Now().Sub(startTime) + tc.t.Logf("%d concurrent reads of different blocks elapsed: %v", + numReaders, elapsed) + + // Consider it a failure if it took longer than half the time it would + // take with no concurrency. + if elapsed > sleepTime*time.Duration(numReaders/2) { + tc.t.Errorf("Concurrent views for different blocks did not "+ + "appear to run simultaneously: elapsed %v", elapsed) + return false + } + + // Start up a few readers and wait for them to acquire views. Each + // reader waits for a signal from the writer to be finished to ensure + // that the data written by the writer is not seen by the view since it + // was started before the data was set. + concurrentKey := []byte("notthere") + concurrentVal := []byte("someval") + started := make(chan struct{}) + writeComplete := make(chan struct{}) + reader = func(blockNum int) { + err := tc.db.View(func(tx database.Tx) error { + started <- struct{}{} + + // Wait for the writer to complete. + <-writeComplete + + // Since this reader was created before the write took + // place, the data it added should not be visible. + val := tx.Metadata().Get(concurrentKey) + if val != nil { + return fmt.Errorf("%s should not be visible", + concurrentKey) + } + return nil + }) + if err != nil { + tc.t.Errorf("Unexpected error in concurrent view: %v", + err) + resultChan <- false + } + resultChan <- true + } + for i := 0; i < numReaders; i++ { + go reader(0) + } + for i := 0; i < numReaders; i++ { + <-started + } + + // All readers are started and waiting for completion of the writer. + // Set some data the readers are expecting to not find and signal the + // readers the write is done by closing the writeComplete channel. + err = tc.db.Update(func(tx database.Tx) error { + err := tx.Metadata().Put(concurrentKey, concurrentVal) + if err != nil { + return err + } + return nil + }) + if err != nil { + tc.t.Errorf("Unexpected error in update: %v", err) + return false + } + close(writeComplete) + + // Wait for reader results. + for i := 0; i < numReaders; i++ { + if result := <-resultChan; !result { + return false + } + } + + // Start a few writers and ensure the total time is at least the + // writeSleepTime * numWriters. This ensures only one write transaction + // can be active at a time. + writeSleepTime := time.Millisecond * 250 + writer := func() { + err := tc.db.Update(func(tx database.Tx) error { + time.Sleep(writeSleepTime) + return nil + }) + if err != nil { + tc.t.Errorf("Unexpected error in concurrent view: %v", + err) + resultChan <- false + } + resultChan <- true + } + numWriters := 3 + startTime = time.Now() + for i := 0; i < numWriters; i++ { + go writer() + } + for i := 0; i < numWriters; i++ { + if result := <-resultChan; !result { + return false + } + } + elapsed = time.Now().Sub(startTime) + tc.t.Logf("%d concurrent writers elapsed using sleep time %v: %v", + numWriters, writeSleepTime, elapsed) + + // The total time must have been at least the sum of all sleeps if the + // writes blocked properly. + if elapsed < writeSleepTime*time.Duration(numWriters) { + tc.t.Errorf("Concurrent writes appeared to run simultaneously: "+ + "elapsed %v", elapsed) + return false + } + + return true +} + +// testConcurrentClose ensures that closing the database with open transactions +// blocks until the transactions are finished. +// +// The database will be closed upon returning from this function. +func testConcurrentClose(tc *testContext) bool { + // Start up a few readers and wait for them to acquire views. Each + // reader waits for a signal to complete to ensure the transactions stay + // open until they are explicitly signalled to be closed. + var activeReaders int32 + numReaders := 3 + started := make(chan struct{}) + finishReaders := make(chan struct{}) + resultChan := make(chan bool, numReaders+1) + reader := func() { + err := tc.db.View(func(tx database.Tx) error { + atomic.AddInt32(&activeReaders, 1) + started <- struct{}{} + <-finishReaders + atomic.AddInt32(&activeReaders, -1) + return nil + }) + if err != nil { + tc.t.Errorf("Unexpected error in concurrent view: %v", + err) + resultChan <- false + } + resultChan <- true + } + for i := 0; i < numReaders; i++ { + go reader() + } + for i := 0; i < numReaders; i++ { + <-started + } + + // Close the database in a separate goroutine. This should block until + // the transactions are finished. Once the close has taken place, the + // dbClosed channel is closed to signal the main goroutine below. + dbClosed := make(chan struct{}) + go func() { + started <- struct{}{} + err := tc.db.Close() + if err != nil { + tc.t.Errorf("Unexpected error in concurrent view: %v", + err) + resultChan <- false + } + close(dbClosed) + resultChan <- true + }() + <-started + + // Wait a short period and then signal the reader transactions to + // finish. When the db closed channel is received, ensure there are no + // active readers open. + time.AfterFunc(time.Millisecond*250, func() { close(finishReaders) }) + <-dbClosed + if nr := atomic.LoadInt32(&activeReaders); nr != 0 { + tc.t.Errorf("Close did not appear to block with active "+ + "readers: %d active", nr) + return false + } + + // Wait for all results. + for i := 0; i < numReaders+1; i++ { + if result := <-resultChan; !result { + return false + } + } + + return true +} + +// testInterface tests performs tests for the various interfaces of the database +// package which require state in the database for the given database type. +func testInterface(t *testing.T, db database.DB) { + // Create a test context to pass around. + context := testContext{t: t, db: db} + + // Load the test blocks and store in the test context for use throughout + // the tests. + blocks, err := loadBlocks(t, blockDataFile, blockDataNet) + if err != nil { + t.Errorf("loadBlocks: Unexpected error: %v", err) + return + } + context.blocks = blocks + + // Test the transaction metadata interface including managed and manual + // transactions as well as buckets. + if !testMetadataTxInterface(&context) { + return + } + + // Test the transaction block IO interface using managed and manual + // transactions. This function leaves all of the stored blocks in the + // database since they're used later. + if !testBlockIOTxInterface(&context) { + return + } + + // Test all of the transaction interface functions against a closed + // transaction work as expected. + if !testTxClosed(&context) { + return + } + + // Test the database properly supports concurrency. + if !testConcurrecy(&context) { + return + } + + // Test that closing the database with open transactions blocks until + // the transactions are finished. + // + // The database will be closed upon returning from this function, so it + // must be the last thing called. + testConcurrentClose(&context) +} diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/ldbtreapiter.go b/vendor/github.com/btcsuite/btcd/database/ffldb/ldbtreapiter.go new file mode 100644 index 0000000000000000000000000000000000000000..b3d6b6bd5879ec1a87500b8bdb6dada1801fbc93 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/ldbtreapiter.go @@ -0,0 +1,58 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package ffldb + +import ( + "github.com/btcsuite/btcd/database/internal/treap" + "github.com/btcsuite/goleveldb/leveldb/iterator" + "github.com/btcsuite/goleveldb/leveldb/util" +) + +// ldbTreapIter wraps a treap iterator to provide the additional functionality +// needed to satisfy the leveldb iterator.Iterator interface. +type ldbTreapIter struct { + *treap.Iterator + tx *transaction + released bool +} + +// Enforce ldbTreapIter implements the leveldb iterator.Iterator interface. +var _ iterator.Iterator = (*ldbTreapIter)(nil) + +// Error is only provided to satisfy the iterator interface as there are no +// errors for this memory-only structure. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *ldbTreapIter) Error() error { + return nil +} + +// SetReleaser is only provided to satisfy the iterator interface as there is no +// need to override it. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *ldbTreapIter) SetReleaser(releaser util.Releaser) { +} + +// Release releases the iterator by removing the underlying treap iterator from +// the list of active iterators against the pending keys treap. +// +// This is part of the leveldb iterator.Iterator interface implementation. +func (iter *ldbTreapIter) Release() { + if !iter.released { + iter.tx.removeActiveIter(iter.Iterator) + iter.released = true + } +} + +// newLdbTreapIter creates a new treap iterator for the given slice against the +// pending keys for the passed transaction and returns it wrapped in an +// ldbTreapIter so it can be used as a leveldb iterator. It also adds the new +// iterator to the list of active iterators for the transaction. +func newLdbTreapIter(tx *transaction, slice *util.Range) *ldbTreapIter { + iter := tx.pendingKeys.Iterator(slice.Start, slice.Limit) + tx.addActiveIter(iter) + return &ldbTreapIter{Iterator: iter, tx: tx} +} diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/mockfile_test.go b/vendor/github.com/btcsuite/btcd/database/ffldb/mockfile_test.go new file mode 100644 index 0000000000000000000000000000000000000000..15fbd6e5a76d96fb859cc4c17a366cdb768ba5f8 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/mockfile_test.go @@ -0,0 +1,163 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// This file is part of the ffldb package rather than the ffldb_test package as +// it is part of the whitebox testing. + +package ffldb + +import ( + "errors" + "io" + "sync" +) + +// Errors used for the mock file. +var ( + // errMockFileClosed is used to indicate a mock file is closed. + errMockFileClosed = errors.New("file closed") + + // errInvalidOffset is used to indicate an offset that is out of range + // for the file was provided. + errInvalidOffset = errors.New("invalid offset") + + // errSyncFail is used to indicate simulated sync failure. + errSyncFail = errors.New("simulated sync failure") +) + +// mockFile implements the filer interface and used in order to force failures +// the database code related to reading and writing from the flat block files. +// A maxSize of -1 is unlimited. +type mockFile struct { + sync.RWMutex + maxSize int64 + data []byte + forceSyncErr bool + closed bool +} + +// Close closes the mock file without releasing any data associated with it. +// This allows it to be "reopened" without losing the data. +// +// This is part of the filer implementation. +func (f *mockFile) Close() error { + f.Lock() + defer f.Unlock() + + if f.closed { + return errMockFileClosed + } + f.closed = true + return nil +} + +// ReadAt reads len(b) bytes from the mock file starting at byte offset off. It +// returns the number of bytes read and the error, if any. ReadAt always +// returns a non-nil error when n < len(b). At end of file, that error is +// io.EOF. +// +// This is part of the filer implementation. +func (f *mockFile) ReadAt(b []byte, off int64) (int, error) { + f.RLock() + defer f.RUnlock() + + if f.closed { + return 0, errMockFileClosed + } + maxSize := int64(len(f.data)) + if f.maxSize > -1 && maxSize > f.maxSize { + maxSize = f.maxSize + } + if off < 0 || off > maxSize { + return 0, errInvalidOffset + } + + // Limit to the max size field, if set. + numToRead := int64(len(b)) + endOffset := off + numToRead + if endOffset > maxSize { + numToRead = maxSize - off + } + + copy(b, f.data[off:off+numToRead]) + if numToRead < int64(len(b)) { + return int(numToRead), io.EOF + } + return int(numToRead), nil +} + +// Truncate changes the size of the mock file. +// +// This is part of the filer implementation. +func (f *mockFile) Truncate(size int64) error { + f.Lock() + defer f.Unlock() + + if f.closed { + return errMockFileClosed + } + maxSize := int64(len(f.data)) + if f.maxSize > -1 && maxSize > f.maxSize { + maxSize = f.maxSize + } + if size > maxSize { + return errInvalidOffset + } + + f.data = f.data[:size] + return nil +} + +// Write writes len(b) bytes to the mock file. It returns the number of bytes +// written and an error, if any. Write returns a non-nil error any time +// n != len(b). +// +// This is part of the filer implementation. +func (f *mockFile) WriteAt(b []byte, off int64) (int, error) { + f.Lock() + defer f.Unlock() + + if f.closed { + return 0, errMockFileClosed + } + maxSize := f.maxSize + if maxSize < 0 { + maxSize = 100 * 1024 // 100KiB + } + if off < 0 || off > maxSize { + return 0, errInvalidOffset + } + + // Limit to the max size field, if set, and grow the slice if needed. + numToWrite := int64(len(b)) + if off+numToWrite > maxSize { + numToWrite = maxSize - off + } + if off+numToWrite > int64(len(f.data)) { + newData := make([]byte, off+numToWrite) + copy(newData, f.data) + f.data = newData + } + + copy(f.data[off:], b[:numToWrite]) + if numToWrite < int64(len(b)) { + return int(numToWrite), io.EOF + } + return int(numToWrite), nil +} + +// Sync doesn't do anything for mock files. However, it will return an error if +// the mock file's forceSyncErr flag is set. +// +// This is part of the filer implementation. +func (f *mockFile) Sync() error { + if f.forceSyncErr { + return errSyncFail + } + + return nil +} + +// Ensure the mockFile type implements the filer interface. +var _ filer = (*mockFile)(nil) diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/reconcile.go b/vendor/github.com/btcsuite/btcd/database/ffldb/reconcile.go new file mode 100644 index 0000000000000000000000000000000000000000..1f75d1c10a7e6d1c5138625ede088dc0f7f34859 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/reconcile.go @@ -0,0 +1,117 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package ffldb + +import ( + "fmt" + "hash/crc32" + + "github.com/btcsuite/btcd/database" +) + +// The serialized write cursor location format is: +// +// [0:4] Block file (4 bytes) +// [4:8] File offset (4 bytes) +// [8:12] Castagnoli CRC-32 checksum (4 bytes) + +// serializeWriteRow serialize the current block file and offset where new +// will be written into a format suitable for storage into the metadata. +func serializeWriteRow(curBlockFileNum, curFileOffset uint32) []byte { + var serializedRow [12]byte + byteOrder.PutUint32(serializedRow[0:4], curBlockFileNum) + byteOrder.PutUint32(serializedRow[4:8], curFileOffset) + checksum := crc32.Checksum(serializedRow[:8], castagnoli) + byteOrder.PutUint32(serializedRow[8:12], checksum) + return serializedRow[:] +} + +// deserializeWriteRow deserializes the write cursor location stored in the +// metadata. Returns ErrCorruption if the checksum of the entry doesn't match. +func deserializeWriteRow(writeRow []byte) (uint32, uint32, error) { + // Ensure the checksum matches. The checksum is at the end. + gotChecksum := crc32.Checksum(writeRow[:8], castagnoli) + wantChecksumBytes := writeRow[8:12] + wantChecksum := byteOrder.Uint32(wantChecksumBytes) + if gotChecksum != wantChecksum { + str := fmt.Sprintf("metadata for write cursor does not match "+ + "the expected checksum - got %d, want %d", gotChecksum, + wantChecksum) + return 0, 0, makeDbErr(database.ErrCorruption, str, nil) + } + + fileNum := byteOrder.Uint32(writeRow[0:4]) + fileOffset := byteOrder.Uint32(writeRow[4:8]) + return fileNum, fileOffset, nil +} + +// reconcileDB reconciles the metadata with the flat block files on disk. It +// will also initialize the underlying database if the create flag is set. +func reconcileDB(pdb *db, create bool) (database.DB, error) { + // Perform initial internal bucket and value creation during database + // creation. + if create { + if err := initDB(pdb.cache.ldb); err != nil { + return nil, err + } + } + + // Load the current write cursor position from the metadata. + var curFileNum, curOffset uint32 + err := pdb.View(func(tx database.Tx) error { + writeRow := tx.Metadata().Get(writeLocKeyName) + if writeRow == nil { + str := "write cursor does not exist" + return makeDbErr(database.ErrCorruption, str, nil) + } + + var err error + curFileNum, curOffset, err = deserializeWriteRow(writeRow) + return err + }) + if err != nil { + return nil, err + } + + // When the write cursor position found by scanning the block files on + // disk is AFTER the position the metadata believes to be true, truncate + // the files on disk to match the metadata. This can be a fairly common + // occurrence in unclean shutdown scenarios while the block files are in + // the middle of being written. Since the metadata isn't updated until + // after the block data is written, this is effectively just a rollback + // to the known good point before the unclean shutdown. + wc := pdb.store.writeCursor + if wc.curFileNum > curFileNum || (wc.curFileNum == curFileNum && + wc.curOffset > curOffset) { + + log.Info("Detected unclean shutdown - Repairing...") + log.Debugf("Metadata claims file %d, offset %d. Block data is "+ + "at file %d, offset %d", curFileNum, curOffset, + wc.curFileNum, wc.curOffset) + pdb.store.handleRollback(curFileNum, curOffset) + log.Infof("Database sync complete") + } + + // When the write cursor position found by scanning the block files on + // disk is BEFORE the position the metadata believes to be true, return + // a corruption error. Since sync is called after each block is written + // and before the metadata is updated, this should only happen in the + // case of missing, deleted, or truncated block files, which generally + // is not an easily recoverable scenario. In the future, it might be + // possible to rescan and rebuild the metadata from the block files, + // however, that would need to happen with coordination from a higher + // layer since it could invalidate other metadata. + if wc.curFileNum < curFileNum || (wc.curFileNum == curFileNum && + wc.curOffset < curOffset) { + + str := fmt.Sprintf("metadata claims file %d, offset %d, but "+ + "block data is at file %d, offset %d", curFileNum, + curOffset, wc.curFileNum, wc.curOffset) + _ = log.Warnf("***Database corruption detected***: %v", str) + return nil, makeDbErr(database.ErrCorruption, str, nil) + } + + return pdb, nil +} diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/whitebox_test.go b/vendor/github.com/btcsuite/btcd/database/ffldb/whitebox_test.go new file mode 100644 index 0000000000000000000000000000000000000000..af74ee62fb8956434be9a5a3761a2dcc0c3ace26 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/ffldb/whitebox_test.go @@ -0,0 +1,706 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// This file is part of the ffldb package rather than the ffldb_test package as +// it provides whitebox testing. + +package ffldb + +import ( + "compress/bzip2" + "encoding/binary" + "fmt" + "hash/crc32" + "io" + "os" + "path/filepath" + "testing" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/goleveldb/leveldb" + ldberrors "github.com/btcsuite/goleveldb/leveldb/errors" +) + +var ( + // blockDataNet is the expected network in the test block data. + blockDataNet = wire.MainNet + + // blockDataFile is the path to a file containing the first 256 blocks + // of the block chain. + blockDataFile = filepath.Join("..", "testdata", "blocks1-256.bz2") + + // errSubTestFail is used to signal that a sub test returned false. + errSubTestFail = fmt.Errorf("sub test failure") +) + +// loadBlocks loads the blocks contained in the testdata directory and returns +// a slice of them. +func loadBlocks(t *testing.T, dataFile string, network wire.BitcoinNet) ([]*btcutil.Block, error) { + // Open the file that contains the blocks for reading. + fi, err := os.Open(dataFile) + if err != nil { + t.Errorf("failed to open file %v, err %v", dataFile, err) + return nil, err + } + defer func() { + if err := fi.Close(); err != nil { + t.Errorf("failed to close file %v %v", dataFile, + err) + } + }() + dr := bzip2.NewReader(fi) + + // Set the first block as the genesis block. + blocks := make([]*btcutil.Block, 0, 256) + genesis := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) + blocks = append(blocks, genesis) + + // Load the remaining blocks. + for height := 1; ; height++ { + var net uint32 + err := binary.Read(dr, binary.LittleEndian, &net) + if err == io.EOF { + // Hit end of file at the expected offset. No error. + break + } + if err != nil { + t.Errorf("Failed to load network type for block %d: %v", + height, err) + return nil, err + } + if net != uint32(network) { + t.Errorf("Block doesn't match network: %v expects %v", + net, network) + return nil, err + } + + var blockLen uint32 + err = binary.Read(dr, binary.LittleEndian, &blockLen) + if err != nil { + t.Errorf("Failed to load block size for block %d: %v", + height, err) + return nil, err + } + + // Read the block. + blockBytes := make([]byte, blockLen) + _, err = io.ReadFull(dr, blockBytes) + if err != nil { + t.Errorf("Failed to load block %d: %v", height, err) + return nil, err + } + + // Deserialize and store the block. + block, err := btcutil.NewBlockFromBytes(blockBytes) + if err != nil { + t.Errorf("Failed to parse block %v: %v", height, err) + return nil, err + } + blocks = append(blocks, block) + } + + return blocks, nil +} + +// checkDbError ensures the passed error is a database.Error with an error code +// that matches the passed error code. +func checkDbError(t *testing.T, testName string, gotErr error, wantErrCode database.ErrorCode) bool { + dbErr, ok := gotErr.(database.Error) + if !ok { + t.Errorf("%s: unexpected error type - got %T, want %T", + testName, gotErr, database.Error{}) + return false + } + if dbErr.ErrorCode != wantErrCode { + t.Errorf("%s: unexpected error code - got %s (%s), want %s", + testName, dbErr.ErrorCode, dbErr.Description, + wantErrCode) + return false + } + + return true +} + +// testContext is used to store context information about a running test which +// is passed into helper functions. +type testContext struct { + t *testing.T + db database.DB + files map[uint32]*lockableFile + maxFileSizes map[uint32]int64 + blocks []*btcutil.Block +} + +// TestConvertErr ensures the leveldb error to database error conversion works +// as expected. +func TestConvertErr(t *testing.T) { + t.Parallel() + + tests := []struct { + err error + wantErrCode database.ErrorCode + }{ + {&ldberrors.ErrCorrupted{}, database.ErrCorruption}, + {leveldb.ErrClosed, database.ErrDbNotOpen}, + {leveldb.ErrSnapshotReleased, database.ErrTxClosed}, + {leveldb.ErrIterReleased, database.ErrTxClosed}, + } + + for i, test := range tests { + gotErr := convertErr("test", test.err) + if gotErr.ErrorCode != test.wantErrCode { + t.Errorf("convertErr #%d unexpected error - got %v, "+ + "want %v", i, gotErr.ErrorCode, test.wantErrCode) + continue + } + } +} + +// TestCornerCases ensures several corner cases which can happen when opening +// a database and/or block files work as expected. +func TestCornerCases(t *testing.T) { + t.Parallel() + + // Create a file at the datapase path to force the open below to fail. + dbPath := filepath.Join(os.TempDir(), "ffldb-errors") + _ = os.RemoveAll(dbPath) + fi, err := os.Create(dbPath) + if err != nil { + t.Errorf("os.Create: unexpected error: %v", err) + return + } + fi.Close() + + // Ensure creating a new database fails when a file exists where a + // directory is needed. + testName := "openDB: fail due to file at target location" + wantErrCode := database.ErrDriverSpecific + idb, err := openDB(dbPath, blockDataNet, true) + if !checkDbError(t, testName, err, wantErrCode) { + if err == nil { + idb.Close() + } + _ = os.RemoveAll(dbPath) + return + } + + // Remove the file and create the database to run tests against. It + // should be successful this time. + _ = os.RemoveAll(dbPath) + idb, err = openDB(dbPath, blockDataNet, true) + if err != nil { + t.Errorf("openDB: unexpected error: %v", err) + return + } + defer os.RemoveAll(dbPath) + defer idb.Close() + + // Ensure attempting to write to a file that can't be created returns + // the expected error. + testName = "writeBlock: open file failure" + filePath := blockFilePath(dbPath, 0) + if err := os.Mkdir(filePath, 0755); err != nil { + t.Errorf("os.Mkdir: unexpected error: %v", err) + return + } + store := idb.(*db).store + _, err = store.writeBlock([]byte{0x00}) + if !checkDbError(t, testName, err, database.ErrDriverSpecific) { + return + } + _ = os.RemoveAll(filePath) + + // Close the underlying leveldb database out from under the database. + ldb := idb.(*db).cache.ldb + ldb.Close() + + // Ensure initilization errors in the underlying database work as + // expected. + testName = "initDB: reinitialization" + wantErrCode = database.ErrDbNotOpen + err = initDB(ldb) + if !checkDbError(t, testName, err, wantErrCode) { + return + } + + // Ensure the View handles errors in the underlying leveldb database + // properly. + testName = "View: underlying leveldb error" + wantErrCode = database.ErrDbNotOpen + err = idb.View(func(tx database.Tx) error { + return nil + }) + if !checkDbError(t, testName, err, wantErrCode) { + return + } + + // Ensure the Update handles errors in the underlying leveldb database + // properly. + testName = "Update: underlying leveldb error" + err = idb.Update(func(tx database.Tx) error { + return nil + }) + if !checkDbError(t, testName, err, wantErrCode) { + return + } +} + +// resetDatabase removes everything from the opened database associated with the +// test context including all metadata and the mock files. +func resetDatabase(tc *testContext) bool { + // Reset the metadata. + err := tc.db.Update(func(tx database.Tx) error { + // Remove all the keys using a cursor while also generating a + // list of buckets. It's not safe to remove keys during ForEach + // iteration nor is it safe to remove buckets during cursor + // iteration, so this dual approach is needed. + var bucketNames [][]byte + cursor := tx.Metadata().Cursor() + for ok := cursor.First(); ok; ok = cursor.Next() { + if cursor.Value() != nil { + if err := cursor.Delete(); err != nil { + return err + } + } else { + bucketNames = append(bucketNames, cursor.Key()) + } + } + + // Remove the buckets. + for _, k := range bucketNames { + if err := tx.Metadata().DeleteBucket(k); err != nil { + return err + } + } + + _, err := tx.Metadata().CreateBucket(blockIdxBucketName) + return err + }) + if err != nil { + tc.t.Errorf("Update: unexpected error: %v", err) + return false + } + + // Reset the mock files. + store := tc.db.(*db).store + wc := store.writeCursor + wc.curFile.Lock() + if wc.curFile.file != nil { + wc.curFile.file.Close() + wc.curFile.file = nil + } + wc.curFile.Unlock() + wc.Lock() + wc.curFileNum = 0 + wc.curOffset = 0 + wc.Unlock() + tc.files = make(map[uint32]*lockableFile) + tc.maxFileSizes = make(map[uint32]int64) + return true +} + +// testWriteFailures tests various failures paths when writing to the block +// files. +func testWriteFailures(tc *testContext) bool { + if !resetDatabase(tc) { + return false + } + + // Ensure file sync errors during flush return the expected error. + store := tc.db.(*db).store + testName := "flush: file sync failure" + store.writeCursor.Lock() + oldFile := store.writeCursor.curFile + store.writeCursor.curFile = &lockableFile{ + file: &mockFile{forceSyncErr: true, maxSize: -1}, + } + store.writeCursor.Unlock() + err := tc.db.(*db).cache.flush() + if !checkDbError(tc.t, testName, err, database.ErrDriverSpecific) { + return false + } + store.writeCursor.Lock() + store.writeCursor.curFile = oldFile + store.writeCursor.Unlock() + + // Force errors in the various error paths when writing data by using + // mock files with a limited max size. + block0Bytes, _ := tc.blocks[0].Bytes() + tests := []struct { + fileNum uint32 + maxSize int64 + }{ + // Force an error when writing the network bytes. + {fileNum: 0, maxSize: 2}, + + // Force an error when writing the block size. + {fileNum: 0, maxSize: 6}, + + // Force an error when writing the block. + {fileNum: 0, maxSize: 17}, + + // Force an error when writing the checksum. + {fileNum: 0, maxSize: int64(len(block0Bytes)) + 10}, + + // Force an error after writing enough blocks for force multiple + // files. + {fileNum: 15, maxSize: 1}, + } + + for i, test := range tests { + if !resetDatabase(tc) { + return false + } + + // Ensure storing the specified number of blocks using a mock + // file that fails the write fails when the transaction is + // committed, not when the block is stored. + tc.maxFileSizes = map[uint32]int64{test.fileNum: test.maxSize} + err := tc.db.Update(func(tx database.Tx) error { + for i, block := range tc.blocks { + err := tx.StoreBlock(block) + if err != nil { + tc.t.Errorf("StoreBlock (%d): unexpected "+ + "error: %v", i, err) + return errSubTestFail + } + } + + return nil + }) + testName := fmt.Sprintf("Force update commit failure - test "+ + "%d, fileNum %d, maxsize %d", i, test.fileNum, + test.maxSize) + if !checkDbError(tc.t, testName, err, database.ErrDriverSpecific) { + tc.t.Errorf("%v", err) + return false + } + + // Ensure the commit rollback removed all extra files and data. + if len(tc.files) != 1 { + tc.t.Errorf("Update rollback: new not removed - want "+ + "1 file, got %d", len(tc.files)) + return false + } + if _, ok := tc.files[0]; !ok { + tc.t.Error("Update rollback: file 0 does not exist") + return false + } + file := tc.files[0].file.(*mockFile) + if len(file.data) != 0 { + tc.t.Errorf("Update rollback: file did not truncate - "+ + "want len 0, got len %d", len(file.data)) + return false + } + } + + return true +} + +// testBlockFileErrors ensures the database returns expected errors with various +// file-related issues such as closed and missing files. +func testBlockFileErrors(tc *testContext) bool { + if !resetDatabase(tc) { + return false + } + + // Ensure errors in blockFile and openFile when requesting invalid file + // numbers. + store := tc.db.(*db).store + testName := "blockFile invalid file open" + _, err := store.blockFile(^uint32(0)) + if !checkDbError(tc.t, testName, err, database.ErrDriverSpecific) { + return false + } + testName = "openFile invalid file open" + _, err = store.openFile(^uint32(0)) + if !checkDbError(tc.t, testName, err, database.ErrDriverSpecific) { + return false + } + + // Insert the first block into the mock file. + err = tc.db.Update(func(tx database.Tx) error { + err := tx.StoreBlock(tc.blocks[0]) + if err != nil { + tc.t.Errorf("StoreBlock: unexpected error: %v", err) + return errSubTestFail + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("Update: unexpected error: %v", err) + } + return false + } + + // Ensure errors in readBlock and readBlockRegion when requesting a file + // number that doesn't exist. + block0Hash := tc.blocks[0].Sha() + testName = "readBlock invalid file number" + invalidLoc := blockLocation{ + blockFileNum: ^uint32(0), + blockLen: 80, + } + _, err = store.readBlock(block0Hash, invalidLoc) + if !checkDbError(tc.t, testName, err, database.ErrDriverSpecific) { + return false + } + testName = "readBlockRegion invalid file number" + _, err = store.readBlockRegion(invalidLoc, 0, 80) + if !checkDbError(tc.t, testName, err, database.ErrDriverSpecific) { + return false + } + + // Close the block file out from under the database. + store.writeCursor.curFile.Lock() + store.writeCursor.curFile.file.Close() + store.writeCursor.curFile.Unlock() + + // Ensure failures in FetchBlock and FetchBlockRegion(s) since the + // underlying file they need to read from has been closed. + err = tc.db.View(func(tx database.Tx) error { + testName = "FetchBlock closed file" + wantErrCode := database.ErrDriverSpecific + _, err := tx.FetchBlock(block0Hash) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return errSubTestFail + } + + testName = "FetchBlockRegion closed file" + regions := []database.BlockRegion{ + { + Hash: block0Hash, + Len: 80, + Offset: 0, + }, + } + _, err = tx.FetchBlockRegion(®ions[0]) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return errSubTestFail + } + + testName = "FetchBlockRegions closed file" + _, err = tx.FetchBlockRegions(regions) + if !checkDbError(tc.t, testName, err, wantErrCode) { + return errSubTestFail + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("View: unexpected error: %v", err) + } + return false + } + + return true +} + +// testCorruption ensures the database returns expected errors under various +// corruption scenarios. +func testCorruption(tc *testContext) bool { + if !resetDatabase(tc) { + return false + } + + // Insert the first block into the mock file. + err := tc.db.Update(func(tx database.Tx) error { + err := tx.StoreBlock(tc.blocks[0]) + if err != nil { + tc.t.Errorf("StoreBlock: unexpected error: %v", err) + return errSubTestFail + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("Update: unexpected error: %v", err) + } + return false + } + + // Ensure corruption is detected by intentionally modifying the bytes + // stored to the mock file and reading the block. + block0Bytes, _ := tc.blocks[0].Bytes() + block0Hash := tc.blocks[0].Sha() + tests := []struct { + offset uint32 + fixChecksum bool + wantErrCode database.ErrorCode + }{ + // One of the network bytes. The checksum needs to be fixed so + // the invalid network is detected. + {2, true, database.ErrDriverSpecific}, + + // The same network byte, but this time don't fix the checksum + // to ensure the corruption is detected. + {2, false, database.ErrCorruption}, + + // One of the block length bytes. + {6, false, database.ErrCorruption}, + + // Random header byte. + {17, false, database.ErrCorruption}, + + // Random transaction byte. + {90, false, database.ErrCorruption}, + + // Random checksum byte. + {uint32(len(block0Bytes)) + 10, false, database.ErrCorruption}, + } + err = tc.db.View(func(tx database.Tx) error { + data := tc.files[0].file.(*mockFile).data + for i, test := range tests { + // Corrupt the byte at the offset by a single bit. + data[test.offset] ^= 0x10 + + // Fix the checksum if requested to force other errors. + fileLen := len(data) + var oldChecksumBytes [4]byte + copy(oldChecksumBytes[:], data[fileLen-4:]) + if test.fixChecksum { + toSum := data[:fileLen-4] + cksum := crc32.Checksum(toSum, castagnoli) + binary.BigEndian.PutUint32(data[fileLen-4:], cksum) + } + + testName := fmt.Sprintf("FetchBlock (test #%d): "+ + "corruption", i) + _, err := tx.FetchBlock(block0Hash) + if !checkDbError(tc.t, testName, err, test.wantErrCode) { + return errSubTestFail + } + + // Reset the corrupted data back to the original. + data[test.offset] ^= 0x10 + if test.fixChecksum { + copy(data[fileLen-4:], oldChecksumBytes[:]) + } + } + + return nil + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("View: unexpected error: %v", err) + } + return false + } + + return true +} + +// TestFailureScenarios ensures several failure scenarios such as database +// corruption, block file write failures, and rollback failures are handled +// correctly. +func TestFailureScenarios(t *testing.T) { + // Create a new database to run tests against. + dbPath := filepath.Join(os.TempDir(), "ffldb-failurescenarios") + _ = os.RemoveAll(dbPath) + idb, err := database.Create(dbType, dbPath, blockDataNet) + if err != nil { + t.Errorf("Failed to create test database (%s) %v", dbType, err) + return + } + defer os.RemoveAll(dbPath) + defer idb.Close() + + // Create a test context to pass around. + tc := &testContext{ + t: t, + db: idb, + files: make(map[uint32]*lockableFile), + maxFileSizes: make(map[uint32]int64), + } + + // Change the maximum file size to a small value to force multiple flat + // files with the test data set and replace the file-related functions + // to make use of mock files in memory. This allows injection of + // various file-related errors. + store := idb.(*db).store + store.maxBlockFileSize = 1024 // 1KiB + store.openWriteFileFunc = func(fileNum uint32) (filer, error) { + if file, ok := tc.files[fileNum]; ok { + // "Reopen" the file. + file.Lock() + mock := file.file.(*mockFile) + mock.Lock() + mock.closed = false + mock.Unlock() + file.Unlock() + return mock, nil + } + + // Limit the max size of the mock file as specified in the test + // context. + maxSize := int64(-1) + if maxFileSize, ok := tc.maxFileSizes[fileNum]; ok { + maxSize = int64(maxFileSize) + } + file := &mockFile{maxSize: int64(maxSize)} + tc.files[fileNum] = &lockableFile{file: file} + return file, nil + } + store.openFileFunc = func(fileNum uint32) (*lockableFile, error) { + // Force error when trying to open max file num. + if fileNum == ^uint32(0) { + return nil, makeDbErr(database.ErrDriverSpecific, + "test", nil) + } + if file, ok := tc.files[fileNum]; ok { + // "Reopen" the file. + file.Lock() + mock := file.file.(*mockFile) + mock.Lock() + mock.closed = false + mock.Unlock() + file.Unlock() + return file, nil + } + file := &lockableFile{file: &mockFile{}} + tc.files[fileNum] = file + return file, nil + } + store.deleteFileFunc = func(fileNum uint32) error { + if file, ok := tc.files[fileNum]; ok { + file.Lock() + file.file.Close() + file.Unlock() + delete(tc.files, fileNum) + return nil + } + + str := fmt.Sprintf("file %d does not exist", fileNum) + return makeDbErr(database.ErrDriverSpecific, str, nil) + } + + // Load the test blocks and save in the test context for use throughout + // the tests. + blocks, err := loadBlocks(t, blockDataFile, blockDataNet) + if err != nil { + t.Errorf("loadBlocks: Unexpected error: %v", err) + return + } + tc.blocks = blocks + + // Test various failures paths when writing to the block files. + if !testWriteFailures(tc) { + return + } + + // Test various file-related issues such as closed and missing files. + if !testBlockFileErrors(tc) { + return + } + + // Test various corruption scenarios. + testCorruption(tc) +} diff --git a/vendor/github.com/btcsuite/btcd/database/interface.go b/vendor/github.com/btcsuite/btcd/database/interface.go new file mode 100644 index 0000000000000000000000000000000000000000..545096b8dd2ec5867ef4a3a965f4da51f036c31a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/interface.go @@ -0,0 +1,466 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// Parts of this interface were inspired heavily by the excellent boltdb project +// at https://github.com/boltdb/bolt by Ben B. Johnson. + +package database + +import ( + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// Cursor represents a cursor over key/value pairs and nested buckets of a +// bucket. +// +// Note that open cursors are not tracked on bucket changes and any +// modifications to the bucket, with the exception of Cursor.Delete, invalidates +// the cursor. After invalidation, the cursor must be repositioned, or the keys +// and values returned may be unpredictable. +type Cursor interface { + // Bucket returns the bucket the cursor was created for. + Bucket() Bucket + + // Delete removes the current key/value pair the cursor is at without + // invalidating the cursor. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrIncompatibleValue if attempted when the cursor points to a + // nested bucket + // - ErrTxNotWritable if attempted against a read-only transaction + // - ErrTxClosed if the transaction has already been closed + Delete() error + + // First positions the cursor at the first key/value pair and returns + // whether or not the pair exists. + First() bool + + // Last positions the cursor at the last key/value pair and returns + // whether or not the pair exists. + Last() bool + + // Next moves the cursor one key/value pair forward and returns whether + // or not the pair exists. + Next() bool + + // Prev moves the cursor one key/value pair backward and returns whether + // or not the pair exists. + Prev() bool + + // Seek positions the cursor at the first key/value pair that is greater + // than or equal to the passed seek key. Returns whether or not the + // pair exists. + Seek(seek []byte) bool + + // Key returns the current key the cursor is pointing to. + Key() []byte + + // Value returns the current value the cursor is pointing to. This will + // be nil for nested buckets. + Value() []byte +} + +// Bucket represents a collection of key/value pairs. +type Bucket interface { + // Bucket retrieves a nested bucket with the given key. Returns nil if + // the bucket does not exist. + Bucket(key []byte) Bucket + + // CreateBucket creates and returns a new nested bucket with the given + // key. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrBucketExists if the bucket already exists + // - ErrBucketNameRequired if the key is empty + // - ErrIncompatibleValue if the key is otherwise invalid for the + // particular implementation + // - ErrTxNotWritable if attempted against a read-only transaction + // - ErrTxClosed if the transaction has already been closed + CreateBucket(key []byte) (Bucket, error) + + // CreateBucketIfNotExists creates and returns a new nested bucket with + // the given key if it does not already exist. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrBucketNameRequired if the key is empty + // - ErrIncompatibleValue if the key is otherwise invalid for the + // particular implementation + // - ErrTxNotWritable if attempted against a read-only transaction + // - ErrTxClosed if the transaction has already been closed + CreateBucketIfNotExists(key []byte) (Bucket, error) + + // DeleteBucket removes a nested bucket with the given key. This also + // includes removing all nested buckets and keys under the bucket being + // deleted. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrBucketNotFound if the specified bucket does not exist + // - ErrTxNotWritable if attempted against a read-only transaction + // - ErrTxClosed if the transaction has already been closed + DeleteBucket(key []byte) error + + // ForEach invokes the passed function with every key/value pair in the + // bucket. This does not include nested buckets or the key/value pairs + // within those nested buckets. + // + // WARNING: It is not safe to mutate data while iterating with this + // method. Doing so may cause the underlying cursor to be invalidated + // and return unexpected keys and/or values. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrTxClosed if the transaction has already been closed + // + // NOTE: The slices returned by this function are only valid during a + // transaction. Attempting to access them after a transaction has ended + // results in undefined behavior. Additionally, the slices must NOT + // be modified by the caller. These constraints prevent additional data + // copies and allows support for memory-mapped database implementations. + ForEach(func(k, v []byte) error) error + + // ForEachBucket invokes the passed function with the key of every + // nested bucket in the current bucket. This does not include any + // nested buckets within those nested buckets. + // + // WARNING: It is not safe to mutate data while iterating with this + // method. Doing so may cause the underlying cursor to be invalidated + // and return unexpected keys and/or values. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrTxClosed if the transaction has already been closed + // + // NOTE: The keys returned by this function are only valid during a + // transaction. Attempting to access them after a transaction has ended + // results in undefined behavior. This constraint prevents additional + // data copies and allows support for memory-mapped database + // implementations. + ForEachBucket(func(k []byte) error) error + + // Cursor returns a new cursor, allowing for iteration over the bucket's + // key/value pairs and nested buckets in forward or backward order. + // + // You must seek to a position using the First, Last, or Seek functions + // before calling the Next, Prev, Key, or Value functions. Failure to + // do so will result in the same return values as an exhausted cursor, + // which is false for the Prev and Next functions and nil for Key and + // Value functions. + Cursor() Cursor + + // Writable returns whether or not the bucket is writable. + Writable() bool + + // Put saves the specified key/value pair to the bucket. Keys that do + // not already exist are added and keys that already exist are + // overwritten. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrKeyRequired if the key is empty + // - ErrIncompatibleValue if the key is the same as an existing bucket + // - ErrTxNotWritable if attempted against a read-only transaction + // - ErrTxClosed if the transaction has already been closed + // + // NOTE: The slices passed to this function must NOT be modified by the + // caller. This constraint prevents the requirement for additional data + // copies and allows support for memory-mapped database implementations. + Put(key, value []byte) error + + // Get returns the value for the given key. Returns nil if the key does + // not exist in this bucket. An empty slice is returned for keys that + // exist but have no value assigned. + // + // NOTE: The value returned by this function is only valid during a + // transaction. Attempting to access it after a transaction has ended + // results in undefined behavior. Additionally, the value must NOT + // be modified by the caller. These constraints prevent additional data + // copies and allows support for memory-mapped database implementations. + Get(key []byte) []byte + + // Delete removes the specified key from the bucket. Deleting a key + // that does not exist does not return an error. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrKeyRequired if the key is empty + // - ErrIncompatibleValue if the key is the same as an existing bucket + // - ErrTxNotWritable if attempted against a read-only transaction + // - ErrTxClosed if the transaction has already been closed + Delete(key []byte) error +} + +// BlockRegion specifies a particular region of a block identified by the +// specified hash, given an offset and length. +type BlockRegion struct { + Hash *wire.ShaHash + Offset uint32 + Len uint32 +} + +// Tx represents a database transaction. It can either by read-only or +// read-write. The transaction provides a metadata bucket against which all +// read and writes occur. +// +// As would be expected with a transaction, no changes will be saved to the +// database until it has been committed. The transaction will only provide a +// view of the database at the time it was created. Transactions should not be +// long running operations. +type Tx interface { + // Metadata returns the top-most bucket for all metadata storage. + Metadata() Bucket + + // StoreBlock stores the provided block into the database. There are no + // checks to ensure the block connects to a previous block, contains + // double spends, or any additional functionality such as transaction + // indexing. It simply stores the block in the database. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrBlockExists when the block hash already exists + // - ErrTxNotWritable if attempted against a read-only transaction + // - ErrTxClosed if the transaction has already been closed + // + // Other errors are possible depending on the implementation. + StoreBlock(block *btcutil.Block) error + + // HasBlock returns whether or not a block with the given hash exists + // in the database. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrTxClosed if the transaction has already been closed + // + // Other errors are possible depending on the implementation. + HasBlock(hash *wire.ShaHash) (bool, error) + + // HasBlocks returns whether or not the blocks with the provided hashes + // exist in the database. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrTxClosed if the transaction has already been closed + // + // Other errors are possible depending on the implementation. + HasBlocks(hashes []wire.ShaHash) ([]bool, error) + + // FetchBlockHeader returns the raw serialized bytes for the block + // header identified by the given hash. The raw bytes are in the format + // returned by Serialize on a wire.BlockHeader. + // + // It is highly recommended to use this function (or FetchBlockHeaders) + // to obtain block headers over the FetchBlockRegion(s) functions since + // it provides the backend drivers the freedom to perform very specific + // optimizations which can result in significant speed advantages when + // working with headers. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrBlockNotFound if the requested block hash does not exist + // - ErrTxClosed if the transaction has already been closed + // - ErrCorruption if the database has somehow become corrupted + // + // NOTE: The data returned by this function is only valid during a + // database transaction. Attempting to access it after a transaction + // has ended results in undefined behavior. This constraint prevents + // additional data copies and allows support for memory-mapped database + // implementations. + FetchBlockHeader(hash *wire.ShaHash) ([]byte, error) + + // FetchBlockHeaders returns the raw serialized bytes for the block + // headers identified by the given hashes. The raw bytes are in the + // format returned by Serialize on a wire.BlockHeader. + // + // It is highly recommended to use this function (or FetchBlockHeader) + // to obtain block headers over the FetchBlockRegion(s) functions since + // it provides the backend drivers the freedom to perform very specific + // optimizations which can result in significant speed advantages when + // working with headers. + // + // Furthermore, depending on the specific implementation, this function + // can be more efficient for bulk loading multiple block headers than + // loading them one-by-one with FetchBlockHeader. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrBlockNotFound if any of the request block hashes do not exist + // - ErrTxClosed if the transaction has already been closed + // - ErrCorruption if the database has somehow become corrupted + // + // NOTE: The data returned by this function is only valid during a + // database transaction. Attempting to access it after a transaction + // has ended results in undefined behavior. This constraint prevents + // additional data copies and allows support for memory-mapped database + // implementations. + FetchBlockHeaders(hashes []wire.ShaHash) ([][]byte, error) + + // FetchBlock returns the raw serialized bytes for the block identified + // by the given hash. The raw bytes are in the format returned by + // Serialize on a wire.MsgBlock. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrBlockNotFound if the requested block hash does not exist + // - ErrTxClosed if the transaction has already been closed + // - ErrCorruption if the database has somehow become corrupted + // + // NOTE: The data returned by this function is only valid during a + // database transaction. Attempting to access it after a transaction + // has ended results in undefined behavior. This constraint prevents + // additional data copies and allows support for memory-mapped database + // implementations. + FetchBlock(hash *wire.ShaHash) ([]byte, error) + + // FetchBlocks returns the raw serialized bytes for the blocks + // identified by the given hashes. The raw bytes are in the format + // returned by Serialize on a wire.MsgBlock. + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrBlockNotFound if the any of the requested block hashes do not + // exist + // - ErrTxClosed if the transaction has already been closed + // - ErrCorruption if the database has somehow become corrupted + // + // NOTE: The data returned by this function is only valid during a + // database transaction. Attempting to access it after a transaction + // has ended results in undefined behavior. This constraint prevents + // additional data copies and allows support for memory-mapped database + // implementations. + FetchBlocks(hashes []wire.ShaHash) ([][]byte, error) + + // FetchBlockRegion returns the raw serialized bytes for the given + // block region. + // + // For example, it is possible to directly extract Bitcoin transactions + // and/or scripts from a block with this function. Depending on the + // backend implementation, this can provide significant savings by + // avoiding the need to load entire blocks. + // + // The raw bytes are in the format returned by Serialize on a + // wire.MsgBlock and the Offset field in the provided BlockRegion is + // zero-based and relative to the start of the block (byte 0). + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrBlockNotFound if the requested block hash does not exist + // - ErrBlockRegionInvalid if the region exceeds the bounds of the + // associated block + // - ErrTxClosed if the transaction has already been closed + // - ErrCorruption if the database has somehow become corrupted + // + // NOTE: The data returned by this function is only valid during a + // database transaction. Attempting to access it after a transaction + // has ended results in undefined behavior. This constraint prevents + // additional data copies and allows support for memory-mapped database + // implementations. + FetchBlockRegion(region *BlockRegion) ([]byte, error) + + // FetchBlockRegions returns the raw serialized bytes for the given + // block regions. + // + // For example, it is possible to directly extract Bitcoin transactions + // and/or scripts from various blocks with this function. Depending on + // the backend implementation, this can provide significant savings by + // avoiding the need to load entire blocks. + // + // The raw bytes are in the format returned by Serialize on a + // wire.MsgBlock and the Offset fields in the provided BlockRegions are + // zero-based and relative to the start of the block (byte 0). + // + // The interface contract guarantees at least the following errors will + // be returned (other implementation-specific errors are possible): + // - ErrBlockNotFound if any of the requested block hashed do not + // exist + // - ErrBlockRegionInvalid if one or more region exceed the bounds of + // the associated block + // - ErrTxClosed if the transaction has already been closed + // - ErrCorruption if the database has somehow become corrupted + // + // NOTE: The data returned by this function is only valid during a + // database transaction. Attempting to access it after a transaction + // has ended results in undefined behavior. This constraint prevents + // additional data copies and allows support for memory-mapped database + // implementations. + FetchBlockRegions(regions []BlockRegion) ([][]byte, error) + + // ****************************************************************** + // Methods related to both atomic metadata storage and block storage. + // ****************************************************************** + + // Commit commits all changes that have been made to the metadata or + // block storage. Depending on the backend implementation this could be + // to a cache that is periodically synced to persistent storage or + // directly to persistent storage. In any case, all transactions which + // are started after the commit finishes will include all changes made + // by this transaction. Calling this function on a managed transaction + // will result in a panic. + Commit() error + + // Rollback undoes all changes that have been made to the metadata or + // block storage. Calling this function on a managed transaction will + // result in a panic. + Rollback() error +} + +// DB provides a generic interface that is used to store bitcoin blocks and +// related metadata. This interface is intended to be agnostic to the actual +// mechanism used for backend data storage. The RegisterDriver function can be +// used to add a new backend data storage method. +// +// This interface is divided into two distinct categories of functionality. +// +// The first category is atomic metadata storage with bucket support. This is +// accomplished through the use of database transactions. +// +// The second category is generic block storage. This functionality is +// intentionally separate because the mechanism used for block storage may or +// may not be the same mechanism used for metadata storage. For example, it is +// often more efficient to store the block data as flat files while the metadata +// is kept in a database. However, this interface aims to be generic enough to +// support blocks in the database too, if needed by a particular backend. +type DB interface { + // Type returns the database driver type the current database instance + // was created with. + Type() string + + // Begin starts a transaction which is either read-only or read-write + // depending on the specified flag. Multiple read-only transactions + // can be started simultaneously while only a single read-write + // transaction can be started at a time. The call will block when + // starting a read-write transaction when one is already open. + // + // NOTE: The transaction must be closed by calling Rollback or Commit on + // it when it is no longer needed. Failure to do so can result in + // unclaimed memory and/or inablity to close the database due to locks + // depending on the specific database implementation. + Begin(writable bool) (Tx, error) + + // View invokes the passed function in the context of a managed + // read-only transaction. Any errors returned from the user-supplied + // function are returned from this function. + // + // Calling Rollback or Commit on the transaction passed to the + // user-supplied function will result in a panic. + View(fn func(tx Tx) error) error + + // Update invokes the passed function in the context of a managed + // read-write transaction. Any errors returned from the user-supplied + // function will cause the transaction to be rolled back and are + // returned from this function. Otherwise, the transaction is committed + // when the user-supplied function returns a nil error. + // + // Calling Rollback or Commit on the transaction passed to the + // user-supplied function will result in a panic. + Update(fn func(tx Tx) error) error + + // Close cleanly shuts down the database and syncs all data. It will + // block until all database transactions have been finalized (rolled + // back or committed). + Close() error +} diff --git a/vendor/github.com/btcsuite/btcd/database/internal/treap/README.md b/vendor/github.com/btcsuite/btcd/database/internal/treap/README.md new file mode 100644 index 0000000000000000000000000000000000000000..57136e3cffd1b5c4824fd617a55e8d602084b5e7 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/internal/treap/README.md @@ -0,0 +1,51 @@ +treap +===== + +[] +(https://travis-ci.org/btcsuite/btcd) + +Package treap implements a treap data structure that is used to hold ordered +key/value pairs using a combination of binary search tree and heap semantics. +It is a self-organizing and randomized data structure that doesn't require +complex operations to to maintain balance. Search, insert, and delete +operations are all O(log n). Both mutable and immutable variants are provided. + +The mutable variant is typically faster since it is able to simply update the +treap when modifications are made. However, a mutable treap is not safe for +concurrent access without careful use of locking by the caller and care must be +taken when iterating since it can change out from under the iterator. + +The immutable variant works by creating a new version of the treap for all +mutations by replacing modified nodes with new nodes that have updated values +while sharing all unmodified nodes with the previous version. This is extremely +useful in concurrent applications since the caller only has to atomically +replace the treap pointer with the newly returned version after performing any +mutations. All readers can simply use their existing pointer as a snapshot +since the treap it points to is immutable. This effectively provides O(1) +snapshot capability with efficient memory usage characteristics since the old +nodes only remain allocated until there are no longer any references to them. + +Package treap is licensed under the copyfree ISC license. + +## Usage + +This package is only used internally in the database code and as such is not +available for use outside of it. + +## Documentation + +[] +(http://godoc.org/github.com/btcsuite/btcd/database/internal/treap) + +Full `go doc` style documentation for the project can be viewed online without +installing this package by using the GoDoc site here: +http://godoc.org/github.com/btcsuite/btcd/database/internal/treap + +You can also view the documentation locally once the package is installed with +the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to +http://localhost:6060/pkg/github.com/btcsuite/btcd/database/internal/treap + +## License + +Package treap is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/vendor/github.com/btcsuite/btcd/database/internal/treap/common.go b/vendor/github.com/btcsuite/btcd/database/internal/treap/common.go new file mode 100644 index 0000000000000000000000000000000000000000..090a7bd5ab0624b0dfe0adad1ff6c280afab1da9 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/internal/treap/common.go @@ -0,0 +1,136 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package treap + +import ( + "math/rand" + "time" +) + +const ( + // staticDepth is the size of the static array to use for keeping track + // of the parent stack during treap iteration. Since a treap has a very + // high probability that the tree height is logarithmic, it is + // exceedingly unlikely that the parent stack will ever exceed this size + // even for extremely large numbers of items. + staticDepth = 128 + + // nodeFieldsSize is the size the fields of each node takes excluding + // the contents of the key and value. It assumes 64-bit pointers so + // technically it is smaller on 32-bit platforms, but overestimating the + // size in that case is acceptable since it avoids the need to import + // unsafe. It consists of 24-bytes for each key and value + 8 bytes for + // each of the priority, left, and right fields (24*2 + 8*3). + nodeFieldsSize = 72 +) + +var ( + // emptySlice is used for keys that have no value associated with them + // so callers can distinguish between a key that does not exist and one + // that has no value associated with it. + emptySlice = make([]byte, 0) +) + +// treapNode represents a node in the treap. +type treapNode struct { + key []byte + value []byte + priority int + left *treapNode + right *treapNode +} + +// nodeSize returns the number of bytes the specified node occupies including +// the struct fields and the contents of the key and value. +func nodeSize(node *treapNode) uint64 { + return nodeFieldsSize + uint64(len(node.key)+len(node.value)) +} + +// newTreapNode returns a new node from the given key, value, and priority. The +// node is not initially linked to any others. +func newTreapNode(key, value []byte, priority int) *treapNode { + return &treapNode{key: key, value: value, priority: priority} +} + +// parentStack represents a stack of parent treap nodes that are used during +// iteration. It consists of a static array for holding the parents and a +// dynamic overflow slice. It is extremely unlikely the overflow will ever be +// hit during normal operation, however, since a treap's height is +// probabilistic, the overflow case needs to be handled properly. This approach +// is used because it is much more efficient for the majority case than +// dynamically allocating heap space every time the treap is iterated. +type parentStack struct { + index int + items [staticDepth]*treapNode + overflow []*treapNode +} + +// Len returns the current number of items in the stack. +func (s *parentStack) Len() int { + return s.index +} + +// At returns the item n number of items from the top of the stack, where 0 is +// the topmost item, without removing it. It returns nil if n exceeds the +// number of items on the stack. +func (s *parentStack) At(n int) *treapNode { + index := s.index - n - 1 + if index < 0 { + return nil + } + + if index < staticDepth { + return s.items[index] + } + + return s.overflow[index-staticDepth] +} + +// Pop removes the top item from the stack. It returns nil if the stack is +// empty. +func (s *parentStack) Pop() *treapNode { + if s.index == 0 { + return nil + } + + s.index-- + if s.index < staticDepth { + node := s.items[s.index] + s.items[s.index] = nil + return node + } + + node := s.overflow[s.index-staticDepth] + s.overflow[s.index-staticDepth] = nil + return node +} + +// Push pushes the passed item onto the top of the stack. +func (s *parentStack) Push(node *treapNode) { + if s.index < staticDepth { + s.items[s.index] = node + s.index++ + return + } + + // This approach is used over append because reslicing the slice to pop + // the item causes the compiler to make unneeded allocations. Also, + // since the max number of items is related to the tree depth which + // requires expontentially more items to increase, only increase the cap + // one item at a time. This is more intelligent than the generic append + // expansion algorithm which often doubles the cap. + index := s.index - staticDepth + if index+1 > cap(s.overflow) { + overflow := make([]*treapNode, index+1) + copy(overflow, s.overflow) + s.overflow = overflow + } + s.overflow[index] = node + s.index++ +} + +func init() { + rand.Seed(time.Now().UnixNano()) +} diff --git a/vendor/github.com/btcsuite/btcd/database/internal/treap/common_test.go b/vendor/github.com/btcsuite/btcd/database/internal/treap/common_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c43e678de7aaf578c2046b13cc04ab44d836ff21 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/internal/treap/common_test.go @@ -0,0 +1,121 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package treap + +import ( + "encoding/binary" + "encoding/hex" + "math/rand" + "reflect" + "testing" +) + +// fromHex converts the passed hex string into a byte slice and will panic if +// there is an error. This is only provided for the hard-coded constants so +// errors in the source code can be detected. It will only (and must only) be +// called for initialization purposes. +func fromHex(s string) []byte { + r, err := hex.DecodeString(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + return r +} + +// serializeUint32 returns the big-endian encoding of the passed uint32. +func serializeUint32(ui uint32) []byte { + var ret [4]byte + binary.BigEndian.PutUint32(ret[:], ui) + return ret[:] +} + +// TestParentStack ensures the treapParentStack functionality works as intended. +func TestParentStack(t *testing.T) { + t.Parallel() + + tests := []struct { + numNodes int + }{ + {numNodes: 1}, + {numNodes: staticDepth}, + {numNodes: staticDepth + 1}, // Test dynamic code paths + } + +testLoop: + for i, test := range tests { + nodes := make([]*treapNode, 0, test.numNodes) + for j := 0; j < test.numNodes; j++ { + var key [4]byte + binary.BigEndian.PutUint32(key[:], uint32(j)) + node := newTreapNode(key[:], key[:], 0) + nodes = append(nodes, node) + } + + // Push all of the nodes onto the parent stack while testing + // various stack properties. + stack := &parentStack{} + for j, node := range nodes { + stack.Push(node) + + // Ensure the stack length is the expected value. + if stack.Len() != j+1 { + t.Errorf("Len #%d (%d): unexpected stack "+ + "length - got %d, want %d", i, j, + stack.Len(), j+1) + continue testLoop + } + + // Ensure the node at each index is the expected one. + for k := 0; k <= j; k++ { + atNode := stack.At(j - k) + if !reflect.DeepEqual(atNode, nodes[k]) { + t.Errorf("At #%d (%d): mismatched node "+ + "- got %v, want %v", i, j-k, + atNode, nodes[k]) + continue testLoop + } + } + } + + // Ensure each popped node is the expected one. + for j := 0; j < len(nodes); j++ { + node := stack.Pop() + expected := nodes[len(nodes)-j-1] + if !reflect.DeepEqual(node, expected) { + t.Errorf("At #%d (%d): mismatched node - "+ + "got %v, want %v", i, j, node, expected) + continue testLoop + } + } + + // Ensure the stack is now empty. + if stack.Len() != 0 { + t.Errorf("Len #%d: stack is not empty - got %d", i, + stack.Len()) + continue testLoop + } + + // Ensure attempting to retrieve a node at an index beyond the + // stack's length returns nil. + if node := stack.At(2); node != nil { + t.Errorf("At #%d: did not give back nil - got %v", i, + node) + continue testLoop + } + + // Ensure attempting to pop a node from an empty stack returns + // nil. + if node := stack.Pop(); node != nil { + t.Errorf("Pop #%d: did not give back nil - got %v", i, + node) + continue testLoop + } + } +} + +func init() { + // Force the same pseudo random numbers for each test run. + rand.Seed(0) +} diff --git a/vendor/github.com/btcsuite/btcd/database/internal/treap/doc.go b/vendor/github.com/btcsuite/btcd/database/internal/treap/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..4f46e057c12ebe11cf37bd59b67faed54c50388b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/internal/treap/doc.go @@ -0,0 +1,27 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package treap implements a treap data structure that is used to hold ordered +key/value pairs using a combination of binary search tree and heap semantics. +It is a self-organizing and randomized data structure that doesn't require +complex operations to to maintain balance. Search, insert, and delete +operations are all O(log n). Both mutable and immutable variants are provided. + +The mutable variant is typically faster since it is able to simply update the +treap when modifications are made. However, a mutable treap is not safe for +concurrent access without careful use of locking by the caller and care must be +taken when iterating since it can change out from under the iterator. + +The immutable variant works by creating a new version of the treap for all +mutations by replacing modified nodes with new nodes that have updated values +while sharing all unmodified nodes with the previous version. This is extremely +useful in concurrent applications since the caller only has to atomically +replace the treap pointer with the newly returned version after performing any +mutations. All readers can simply use their existing pointer as a snapshot +since the treap it points to is immutable. This effectively provides O(1) +snapshot capability with efficient memory usage characteristics since the old +nodes only remain allocated until there are no longer any references to them. +*/ +package treap diff --git a/vendor/github.com/btcsuite/btcd/database/internal/treap/immutable.go b/vendor/github.com/btcsuite/btcd/database/internal/treap/immutable.go new file mode 100644 index 0000000000000000000000000000000000000000..a6e13ff4a50471101bd3a0e44adca63f1d0c1589 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/internal/treap/immutable.go @@ -0,0 +1,360 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package treap + +import ( + "bytes" + "math/rand" +) + +// cloneTreapNode returns a shallow copy of the passed node. +func cloneTreapNode(node *treapNode) *treapNode { + return &treapNode{ + key: node.key, + value: node.value, + priority: node.priority, + left: node.left, + right: node.right, + } +} + +// Immutable represents a treap data structure which is used to hold ordered +// key/value pairs using a combination of binary search tree and heap semantics. +// It is a self-organizing and randomized data structure that doesn't require +// complex operations to maintain balance. Search, insert, and delete +// operations are all O(log n). In addition, it provides O(1) snapshots for +// multi-version concurrency control (MVCC). +// +// All operations which result in modifying the treap return a new version of +// the treap with only the modified nodes updated. All unmodified nodes are +// shared with the previous version. This is extremely useful in concurrent +// applications since the caller only has to atomically replace the treap +// pointer with the newly returned version after performing any mutations. All +// readers can simply use their existing pointer as a snapshot since the treap +// it points to is immutable. This effectively provides O(1) snapshot +// capability with efficient memory usage characteristics since the old nodes +// only remain allocated until there are no longer any references to them. +type Immutable struct { + root *treapNode + count int + + // totalSize is the best estimate of the total size of of all data in + // the treap including the keys, values, and node sizes. + totalSize uint64 +} + +// newImmutable returns a new immutable treap given the passed parameters. +func newImmutable(root *treapNode, count int, totalSize uint64) *Immutable { + return &Immutable{root: root, count: count, totalSize: totalSize} +} + +// Len returns the number of items stored in the treap. +func (t *Immutable) Len() int { + return t.count +} + +// Size returns a best estimate of the total number of bytes the treap is +// consuming including all of the fields used to represent the nodes as well as +// the size of the keys and values. Shared values are not detected, so the +// returned size assumes each value is pointing to different memory. +func (t *Immutable) Size() uint64 { + return t.totalSize +} + +// get returns the treap node that contains the passed key. It will return nil +// when the key does not exist. +func (t *Immutable) get(key []byte) *treapNode { + for node := t.root; node != nil; { + // Traverse left or right depending on the result of the + // comparison. + compareResult := bytes.Compare(key, node.key) + if compareResult < 0 { + node = node.left + continue + } + if compareResult > 0 { + node = node.right + continue + } + + // The key exists. + return node + } + + // A nil node was reached which means the key does not exist. + return nil +} + +// Has returns whether or not the passed key exists. +func (t *Immutable) Has(key []byte) bool { + if node := t.get(key); node != nil { + return true + } + return false +} + +// Get returns the value for the passed key. The function will return nil when +// the key does not exist. +func (t *Immutable) Get(key []byte) []byte { + if node := t.get(key); node != nil { + return node.value + } + return nil +} + +// Put inserts the passed key/value pair. +func (t *Immutable) Put(key, value []byte) *Immutable { + // Use an empty byte slice for the value when none was provided. This + // ultimately allows key existence to be determined from the value since + // an empty byte slice is distinguishable from nil. + if value == nil { + value = emptySlice + } + + // The node is the root of the tree if there isn't already one. + if t.root == nil { + root := newTreapNode(key, value, rand.Int()) + return newImmutable(root, 1, nodeSize(root)) + } + + // Find the binary tree insertion point and construct a replaced list of + // parents while doing so. This is done because this is an immutable + // data structure so regardless of where in the treap the new key/value + // pair ends up, all ancestors up to and including the root need to be + // replaced. + // + // When the key matches an entry already in the treap, replace the node + // with a new one that has the new value set and return. + var parents parentStack + var compareResult int + for node := t.root; node != nil; { + // Clone the node and link its parent to it if needed. + nodeCopy := cloneTreapNode(node) + if oldParent := parents.At(0); oldParent != nil { + if oldParent.left == node { + oldParent.left = nodeCopy + } else { + oldParent.right = nodeCopy + } + } + parents.Push(nodeCopy) + + // Traverse left or right depending on the result of comparing + // the keys. + compareResult = bytes.Compare(key, node.key) + if compareResult < 0 { + node = node.left + continue + } + if compareResult > 0 { + node = node.right + continue + } + + // The key already exists, so update its value. + nodeCopy.value = value + + // Return new immutable treap with the replaced node and + // ancestors up to and including the root of the tree. + newRoot := parents.At(parents.Len() - 1) + newTotalSize := t.totalSize - uint64(len(node.value)) + + uint64(len(value)) + return newImmutable(newRoot, t.count, newTotalSize) + } + + // Link the new node into the binary tree in the correct position. + node := newTreapNode(key, value, rand.Int()) + parent := parents.At(0) + if compareResult < 0 { + parent.left = node + } else { + parent.right = node + } + + // Perform any rotations needed to maintain the min-heap and replace + // the ancestors up to and including the tree root. + newRoot := parents.At(parents.Len() - 1) + for parents.Len() > 0 { + // There is nothing left to do when the node's priority is + // greater than or equal to its parent's priority. + parent = parents.Pop() + if node.priority >= parent.priority { + break + } + + // Perform a right rotation if the node is on the left side or + // a left rotation if the node is on the right side. + if parent.left == node { + node.right, parent.left = parent, node.right + } else { + node.left, parent.right = parent, node.left + } + + // Either set the new root of the tree when there is no + // grandparent or relink the grandparent to the node based on + // which side the old parent the node is replacing was on. + grandparent := parents.At(0) + if grandparent == nil { + newRoot = node + } else if grandparent.left == parent { + grandparent.left = node + } else { + grandparent.right = node + } + } + + return newImmutable(newRoot, t.count+1, t.totalSize+nodeSize(node)) +} + +// Delete removes the passed key from the treap and returns the resulting treap +// if it exists. The original immutable treap is returned if the key does not +// exist. +func (t *Immutable) Delete(key []byte) *Immutable { + // Find the node for the key while constructing a list of parents while + // doing so. + var parents parentStack + var delNode *treapNode + for node := t.root; node != nil; { + parents.Push(node) + + // Traverse left or right depending on the result of the + // comparison. + compareResult := bytes.Compare(key, node.key) + if compareResult < 0 { + node = node.left + continue + } + if compareResult > 0 { + node = node.right + continue + } + + // The key exists. + delNode = node + break + } + + // There is nothing to do if the key does not exist. + if delNode == nil { + return t + } + + // When the only node in the tree is the root node and it is the one + // being deleted, there is nothing else to do besides removing it. + parent := parents.At(1) + if parent == nil && delNode.left == nil && delNode.right == nil { + return newImmutable(nil, 0, 0) + } + + // Construct a replaced list of parents and the node to delete itself. + // This is done because this is an immutable data structure and + // therefore all ancestors of the node that will be deleted, up to and + // including the root, need to be replaced. + var newParents parentStack + for i := parents.Len(); i > 0; i-- { + node := parents.At(i - 1) + nodeCopy := cloneTreapNode(node) + if oldParent := newParents.At(0); oldParent != nil { + if oldParent.left == node { + oldParent.left = nodeCopy + } else { + oldParent.right = nodeCopy + } + } + newParents.Push(nodeCopy) + } + delNode = newParents.Pop() + parent = newParents.At(0) + + // Perform rotations to move the node to delete to a leaf position while + // maintaining the min-heap while replacing the modified children. + var child *treapNode + newRoot := newParents.At(newParents.Len() - 1) + for delNode.left != nil || delNode.right != nil { + // Choose the child with the higher priority. + var isLeft bool + if delNode.left == nil { + child = delNode.right + } else if delNode.right == nil { + child = delNode.left + isLeft = true + } else if delNode.left.priority >= delNode.right.priority { + child = delNode.left + isLeft = true + } else { + child = delNode.right + } + + // Rotate left or right depending on which side the child node + // is on. This has the effect of moving the node to delete + // towards the bottom of the tree while maintaining the + // min-heap. + child = cloneTreapNode(child) + if isLeft { + child.right, delNode.left = delNode, child.right + } else { + child.left, delNode.right = delNode, child.left + } + + // Either set the new root of the tree when there is no + // grandparent or relink the grandparent to the node based on + // which side the old parent the node is replacing was on. + // + // Since the node to be deleted was just moved down a level, the + // new grandparent is now the current parent and the new parent + // is the current child. + if parent == nil { + newRoot = child + } else if parent.left == delNode { + parent.left = child + } else { + parent.right = child + } + + // The parent for the node to delete is now what was previously + // its child. + parent = child + } + + // Delete the node, which is now a leaf node, by disconnecting it from + // its parent. + if parent.right == delNode { + parent.right = nil + } else { + parent.left = nil + } + + return newImmutable(newRoot, t.count-1, t.totalSize-nodeSize(delNode)) +} + +// ForEach invokes the passed function with every key/value pair in the treap +// in ascending order. +func (t *Immutable) ForEach(fn func(k, v []byte) bool) { + // Add the root node and all children to the left of it to the list of + // nodes to traverse and loop until they, and all of their child nodes, + // have been traversed. + var parents parentStack + for node := t.root; node != nil; node = node.left { + parents.Push(node) + } + for parents.Len() > 0 { + node := parents.Pop() + if !fn(node.key, node.value) { + return + } + + // Extend the nodes to traverse by all children to the left of + // the current node's right child. + for node := node.right; node != nil; node = node.left { + parents.Push(node) + } + } +} + +// NewImmutable returns a new empty immutable treap ready for use. See the +// documentation for the Immutable structure for more details. +func NewImmutable() *Immutable { + return &Immutable{} +} diff --git a/vendor/github.com/btcsuite/btcd/database/internal/treap/immutable_test.go b/vendor/github.com/btcsuite/btcd/database/internal/treap/immutable_test.go new file mode 100644 index 0000000000000000000000000000000000000000..176a2c4612931eeabc9bff138070f9d31e0c3a06 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/internal/treap/immutable_test.go @@ -0,0 +1,500 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package treap + +import ( + "bytes" + "crypto/sha256" + "testing" +) + +// TestImmutableEmpty ensures calling functions on an empty immutable treap +// works as expected. +func TestImmutableEmpty(t *testing.T) { + t.Parallel() + + // Ensure the treap length is the expected value. + testTreap := NewImmutable() + if gotLen := testTreap.Len(); gotLen != 0 { + t.Fatalf("Len: unexpected length - got %d, want %d", gotLen, 0) + } + + // Ensure the reported size is 0. + if gotSize := testTreap.Size(); gotSize != 0 { + t.Fatalf("Size: unexpected byte size - got %d, want 0", + gotSize) + } + + // Ensure there are no errors with requesting keys from an empty treap. + key := serializeUint32(0) + if gotVal := testTreap.Has(key); gotVal != false { + t.Fatalf("Has: unexpected result - got %v, want false", gotVal) + } + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get: unexpected result - got %x, want nil", gotVal) + } + + // Ensure there are no panics when deleting keys from an empty treap. + testTreap.Delete(key) + + // Ensure the number of keys iterated by ForEach on an empty treap is + // zero. + var numIterated int + testTreap.ForEach(func(k, v []byte) bool { + numIterated++ + return true + }) + if numIterated != 0 { + t.Fatalf("ForEach: unexpected iterate count - got %d, want 0", + numIterated) + } +} + +// TestImmutableSequential ensures that putting keys into an immutable treap in +// sequential order works as expected. +func TestImmutableSequential(t *testing.T) { + t.Parallel() + + // Insert a bunch of sequential keys while checking several of the treap + // functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewImmutable() + for i := 0; i < numItems; i++ { + key := serializeUint32(uint32(i)) + testTreap = testTreap.Put(key, key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != i+1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i+1) + } + + // Ensure the treap has the key. + if !testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap and ensure it is the expected + // value. + if gotVal := testTreap.Get(key); !bytes.Equal(gotVal, key) { + t.Fatalf("Get #%d: unexpected value - got %x, want %x", + i, gotVal, key) + } + + // Ensure the expected size is reported. + expectedSize += (nodeFieldsSize + 8) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } + + // Ensure the all keys are iterated by ForEach in order. + var numIterated int + testTreap.ForEach(func(k, v []byte) bool { + wantKey := serializeUint32(uint32(numIterated)) + + // Ensure the key is as expected. + if !bytes.Equal(k, wantKey) { + t.Fatalf("ForEach #%d: unexpected key - got %x, want %x", + numIterated, k, wantKey) + } + + // Ensure the value is as expected. + if !bytes.Equal(v, wantKey) { + t.Fatalf("ForEach #%d: unexpected value - got %x, want %x", + numIterated, v, wantKey) + } + + numIterated++ + return true + }) + + // Ensure all items were iterated. + if numIterated != numItems { + t.Fatalf("ForEach: unexpected iterate count - got %d, want %d", + numIterated, numItems) + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + key := serializeUint32(uint32(i)) + testTreap = testTreap.Delete(key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != numItems-i-1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i-1) + } + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %x, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + expectedSize -= (nodeFieldsSize + 8) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } +} + +// TestImmutableReverseSequential ensures that putting keys into an immutable +// treap in reverse sequential order works as expected. +func TestImmutableReverseSequential(t *testing.T) { + t.Parallel() + + // Insert a bunch of sequential keys while checking several of the treap + // functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewImmutable() + for i := 0; i < numItems; i++ { + key := serializeUint32(uint32(numItems - i - 1)) + testTreap = testTreap.Put(key, key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != i+1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i+1) + } + + // Ensure the treap has the key. + if !testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap and ensure it is the expected + // value. + if gotVal := testTreap.Get(key); !bytes.Equal(gotVal, key) { + t.Fatalf("Get #%d: unexpected value - got %x, want %x", + i, gotVal, key) + } + + // Ensure the expected size is reported. + expectedSize += (nodeFieldsSize + 8) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } + + // Ensure the all keys are iterated by ForEach in order. + var numIterated int + testTreap.ForEach(func(k, v []byte) bool { + wantKey := serializeUint32(uint32(numIterated)) + + // Ensure the key is as expected. + if !bytes.Equal(k, wantKey) { + t.Fatalf("ForEach #%d: unexpected key - got %x, want %x", + numIterated, k, wantKey) + } + + // Ensure the value is as expected. + if !bytes.Equal(v, wantKey) { + t.Fatalf("ForEach #%d: unexpected value - got %x, want %x", + numIterated, v, wantKey) + } + + numIterated++ + return true + }) + + // Ensure all items were iterated. + if numIterated != numItems { + t.Fatalf("ForEach: unexpected iterate count - got %d, want %d", + numIterated, numItems) + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + // Intentionally use the reverse order they were inserted here. + key := serializeUint32(uint32(i)) + testTreap = testTreap.Delete(key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != numItems-i-1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i-1) + } + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %x, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + expectedSize -= (nodeFieldsSize + 8) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } +} + +// TestImmutableUnordered ensures that putting keys into an immutable treap in +// no paritcular order works as expected. +func TestImmutableUnordered(t *testing.T) { + t.Parallel() + + // Insert a bunch of out-of-order keys while checking several of the + // treap functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewImmutable() + for i := 0; i < numItems; i++ { + // Hash the serialized int to generate out-of-order keys. + hash := sha256.Sum256(serializeUint32(uint32(i))) + key := hash[:] + testTreap = testTreap.Put(key, key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != i+1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i+1) + } + + // Ensure the treap has the key. + if !testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap and ensure it is the expected + // value. + if gotVal := testTreap.Get(key); !bytes.Equal(gotVal, key) { + t.Fatalf("Get #%d: unexpected value - got %x, want %x", + i, gotVal, key) + } + + // Ensure the expected size is reported. + expectedSize += nodeFieldsSize + uint64(len(key)+len(key)) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + // Hash the serialized int to generate out-of-order keys. + hash := sha256.Sum256(serializeUint32(uint32(i))) + key := hash[:] + testTreap = testTreap.Delete(key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != numItems-i-1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i-1) + } + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %x, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + expectedSize -= (nodeFieldsSize + 64) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } +} + +// TestImmutableDuplicatePut ensures that putting a duplicate key into an +// immutable treap works as expected. +func TestImmutableDuplicatePut(t *testing.T) { + t.Parallel() + + expectedVal := []byte("testval") + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewImmutable() + for i := 0; i < numItems; i++ { + key := serializeUint32(uint32(i)) + testTreap = testTreap.Put(key, key) + expectedSize += nodeFieldsSize + uint64(len(key)+len(key)) + + // Put a duplicate key with the the expected final value. + testTreap = testTreap.Put(key, expectedVal) + + // Ensure the key still exists and is the new value. + if gotVal := testTreap.Has(key); gotVal != true { + t.Fatalf("Has: unexpected result - got %v, want false", + gotVal) + } + if gotVal := testTreap.Get(key); !bytes.Equal(gotVal, expectedVal) { + t.Fatalf("Get: unexpected result - got %x, want %x", + gotVal, expectedVal) + } + + // Ensure the expected size is reported. + expectedSize -= uint64(len(key)) + expectedSize += uint64(len(expectedVal)) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size: unexpected byte size - got %d, want %d", + gotSize, expectedSize) + } + } +} + +// TestImmutableNilValue ensures that putting a nil value into an immutable +// treap results in a key being added with an empty byte slice. +func TestImmutableNilValue(t *testing.T) { + t.Parallel() + + key := serializeUint32(0) + + // Put the key with a nil value. + testTreap := NewImmutable() + testTreap = testTreap.Put(key, nil) + + // Ensure the key exists and is an empty byte slice. + if gotVal := testTreap.Has(key); gotVal != true { + t.Fatalf("Has: unexpected result - got %v, want false", gotVal) + } + if gotVal := testTreap.Get(key); gotVal == nil { + t.Fatalf("Get: unexpected result - got nil, want empty slice") + } + if gotVal := testTreap.Get(key); len(gotVal) != 0 { + t.Fatalf("Get: unexpected result - got %x, want empty slice", + gotVal) + } +} + +// TestImmutableForEachStopIterator ensures that returning false from the ForEach +// callback on an immutable treap stops iteration early. +func TestImmutableForEachStopIterator(t *testing.T) { + t.Parallel() + + // Insert a few keys. + numItems := 10 + testTreap := NewImmutable() + for i := 0; i < numItems; i++ { + key := serializeUint32(uint32(i)) + testTreap = testTreap.Put(key, key) + } + + // Ensure ForEach exits early on false return by caller. + var numIterated int + testTreap.ForEach(func(k, v []byte) bool { + numIterated++ + if numIterated == numItems/2 { + return false + } + return true + }) + if numIterated != numItems/2 { + t.Fatalf("ForEach: unexpected iterate count - got %d, want %d", + numIterated, numItems/2) + } +} + +// TestImmutableSnapshot ensures that immutable treaps are actually immutable by +// keeping a reference to the previous treap, performing a mutation, and then +// ensuring the referenced treap does not have the mutation applied. +func TestImmutableSnapshot(t *testing.T) { + t.Parallel() + + // Insert a bunch of sequential keys while checking several of the treap + // functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewImmutable() + for i := 0; i < numItems; i++ { + treapSnap := testTreap + + key := serializeUint32(uint32(i)) + testTreap = testTreap.Put(key, key) + + // Ensure the length of the treap snapshot is the expected + // value. + if gotLen := treapSnap.Len(); gotLen != i { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i) + } + + // Ensure the treap snapshot does not have the key. + if treapSnap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that doesn't exist in the treap snapshot and + // ensure it is nil. + if gotVal := treapSnap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %x, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + if gotSize := treapSnap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + expectedSize += (nodeFieldsSize + 8) + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + treapSnap := testTreap + + key := serializeUint32(uint32(i)) + testTreap = testTreap.Delete(key) + + // Ensure the length of the treap snapshot is the expected + // value. + if gotLen := treapSnap.Len(); gotLen != numItems-i { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i) + } + + // Ensure the treap snapshot still has the key. + if !treapSnap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap snapshot and ensure it is still + // the expected value. + if gotVal := treapSnap.Get(key); !bytes.Equal(gotVal, key) { + t.Fatalf("Get #%d: unexpected value - got %x, want %x", + i, gotVal, key) + } + + // Ensure the expected size is reported. + if gotSize := treapSnap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + expectedSize -= (nodeFieldsSize + 8) + } +} diff --git a/vendor/github.com/btcsuite/btcd/database/internal/treap/mutable.go b/vendor/github.com/btcsuite/btcd/database/internal/treap/mutable.go new file mode 100644 index 0000000000000000000000000000000000000000..84ebe6715f3252da99edcf66f81c305a8ff2db45 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/internal/treap/mutable.go @@ -0,0 +1,278 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package treap + +import ( + "bytes" + "math/rand" +) + +// Mutable represents a treap data structure which is used to hold ordered +// key/value pairs using a combination of binary search tree and heap semantics. +// It is a self-organizing and randomized data structure that doesn't require +// complex operations to maintain balance. Search, insert, and delete +// operations are all O(log n). +type Mutable struct { + root *treapNode + count int + + // totalSize is the best estimate of the total size of of all data in + // the treap including the keys, values, and node sizes. + totalSize uint64 +} + +// Len returns the number of items stored in the treap. +func (t *Mutable) Len() int { + return t.count +} + +// Size returns a best estimate of the total number of bytes the treap is +// consuming including all of the fields used to represent the nodes as well as +// the size of the keys and values. Shared values are not detected, so the +// returned size assumes each value is pointing to different memory. +func (t *Mutable) Size() uint64 { + return t.totalSize +} + +// get returns the treap node that contains the passed key and its parent. When +// the found node is the root of the tree, the parent will be nil. When the key +// does not exist, both the node and the parent will be nil. +func (t *Mutable) get(key []byte) (*treapNode, *treapNode) { + var parent *treapNode + for node := t.root; node != nil; { + // Traverse left or right depending on the result of the + // comparison. + compareResult := bytes.Compare(key, node.key) + if compareResult < 0 { + parent = node + node = node.left + continue + } + if compareResult > 0 { + parent = node + node = node.right + continue + } + + // The key exists. + return node, parent + } + + // A nil node was reached which means the key does not exist. + return nil, nil +} + +// Has returns whether or not the passed key exists. +func (t *Mutable) Has(key []byte) bool { + if node, _ := t.get(key); node != nil { + return true + } + return false +} + +// Get returns the value for the passed key. The function will return nil when +// the key does not exist. +func (t *Mutable) Get(key []byte) []byte { + if node, _ := t.get(key); node != nil { + return node.value + } + return nil +} + +// relinkGrandparent relinks the node into the treap after it has been rotated +// by changing the passed grandparent's left or right pointer, depending on +// where the old parent was, to point at the passed node. Otherwise, when there +// is no grandparent, it means the node is now the root of the tree, so update +// it accordingly. +func (t *Mutable) relinkGrandparent(node, parent, grandparent *treapNode) { + // The node is now the root of the tree when there is no grandparent. + if grandparent == nil { + t.root = node + return + } + + // Relink the grandparent's left or right pointer based on which side + // the old parent was. + if grandparent.left == parent { + grandparent.left = node + } else { + grandparent.right = node + } +} + +// Put inserts the passed key/value pair. +func (t *Mutable) Put(key, value []byte) { + // Use an empty byte slice for the value when none was provided. This + // ultimately allows key existence to be determined from the value since + // an empty byte slice is distinguishable from nil. + if value == nil { + value = emptySlice + } + + // The node is the root of the tree if there isn't already one. + if t.root == nil { + node := newTreapNode(key, value, rand.Int()) + t.count = 1 + t.totalSize = nodeSize(node) + t.root = node + return + } + + // Find the binary tree insertion point and construct a list of parents + // while doing so. When the key matches an entry already in the treap, + // just update its value and return. + var parents parentStack + var compareResult int + for node := t.root; node != nil; { + parents.Push(node) + compareResult = bytes.Compare(key, node.key) + if compareResult < 0 { + node = node.left + continue + } + if compareResult > 0 { + node = node.right + continue + } + + // The key already exists, so update its value. + t.totalSize -= uint64(len(node.value)) + t.totalSize += uint64(len(value)) + node.value = value + return + } + + // Link the new node into the binary tree in the correct position. + node := newTreapNode(key, value, rand.Int()) + t.count++ + t.totalSize += nodeSize(node) + parent := parents.At(0) + if compareResult < 0 { + parent.left = node + } else { + parent.right = node + } + + // Perform any rotations needed to maintain the min-heap. + for parents.Len() > 0 { + // There is nothing left to do when the node's priority is + // greater than or equal to its parent's priority. + parent = parents.Pop() + if node.priority >= parent.priority { + break + } + + // Perform a right rotation if the node is on the left side or + // a left rotation if the node is on the right side. + if parent.left == node { + node.right, parent.left = parent, node.right + } else { + node.left, parent.right = parent, node.left + } + t.relinkGrandparent(node, parent, parents.At(0)) + } +} + +// Delete removes the passed key if it exists. +func (t *Mutable) Delete(key []byte) { + // Find the node for the key along with its parent. There is nothing to + // do if the key does not exist. + node, parent := t.get(key) + if node == nil { + return + } + + // When the only node in the tree is the root node and it is the one + // being deleted, there is nothing else to do besides removing it. + if parent == nil && node.left == nil && node.right == nil { + t.root = nil + t.count = 0 + t.totalSize = 0 + return + } + + // Perform rotations to move the node to delete to a leaf position while + // maintaining the min-heap. + var isLeft bool + var child *treapNode + for node.left != nil || node.right != nil { + // Choose the child with the higher priority. + if node.left == nil { + child = node.right + isLeft = false + } else if node.right == nil { + child = node.left + isLeft = true + } else if node.left.priority >= node.right.priority { + child = node.left + isLeft = true + } else { + child = node.right + isLeft = false + } + + // Rotate left or right depending on which side the child node + // is on. This has the effect of moving the node to delete + // towards the bottom of the tree while maintaining the + // min-heap. + if isLeft { + child.right, node.left = node, child.right + } else { + child.left, node.right = node, child.left + } + t.relinkGrandparent(child, node, parent) + + // The parent for the node to delete is now what was previously + // its child. + parent = child + } + + // Delete the node, which is now a leaf node, by disconnecting it from + // its parent. + if parent.right == node { + parent.right = nil + } else { + parent.left = nil + } + t.count-- + t.totalSize -= nodeSize(node) +} + +// ForEach invokes the passed function with every key/value pair in the treap +// in ascending order. +func (t *Mutable) ForEach(fn func(k, v []byte) bool) { + // Add the root node and all children to the left of it to the list of + // nodes to traverse and loop until they, and all of their child nodes, + // have been traversed. + var parents parentStack + for node := t.root; node != nil; node = node.left { + parents.Push(node) + } + for parents.Len() > 0 { + node := parents.Pop() + if !fn(node.key, node.value) { + return + } + + // Extend the nodes to traverse by all children to the left of + // the current node's right child. + for node := node.right; node != nil; node = node.left { + parents.Push(node) + } + } +} + +// Reset efficiently removes all items in the treap. +func (t *Mutable) Reset() { + t.count = 0 + t.totalSize = 0 + t.root = nil +} + +// NewMutable returns a new empty mutable treap ready for use. See the +// documentation for the Mutable structure for more details. +func NewMutable() *Mutable { + return &Mutable{} +} diff --git a/vendor/github.com/btcsuite/btcd/database/internal/treap/mutable_test.go b/vendor/github.com/btcsuite/btcd/database/internal/treap/mutable_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c22ced0eb839823ce340d750f1bbfbdb78e463d7 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/internal/treap/mutable_test.go @@ -0,0 +1,468 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package treap + +import ( + "bytes" + "crypto/sha256" + "testing" +) + +// TestMutableEmpty ensures calling functions on an empty mutable treap works as +// expected. +func TestMutableEmpty(t *testing.T) { + t.Parallel() + + // Ensure the treap length is the expected value. + testTreap := NewMutable() + if gotLen := testTreap.Len(); gotLen != 0 { + t.Fatalf("Len: unexpected length - got %d, want %d", gotLen, 0) + } + + // Ensure the reported size is 0. + if gotSize := testTreap.Size(); gotSize != 0 { + t.Fatalf("Size: unexpected byte size - got %d, want 0", + gotSize) + } + + // Ensure there are no errors with requesting keys from an empty treap. + key := serializeUint32(0) + if gotVal := testTreap.Has(key); gotVal != false { + t.Fatalf("Has: unexpected result - got %v, want false", gotVal) + } + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get: unexpected result - got %x, want nil", gotVal) + } + + // Ensure there are no panics when deleting keys from an empty treap. + testTreap.Delete(key) + + // Ensure the number of keys iterated by ForEach on an empty treap is + // zero. + var numIterated int + testTreap.ForEach(func(k, v []byte) bool { + numIterated++ + return true + }) + if numIterated != 0 { + t.Fatalf("ForEach: unexpected iterate count - got %d, want 0", + numIterated) + } +} + +// TestMutableReset ensures that resetting an existing mutable treap works as +// expected. +func TestMutableReset(t *testing.T) { + t.Parallel() + + // Insert a few keys. + numItems := 10 + testTreap := NewMutable() + for i := 0; i < numItems; i++ { + key := serializeUint32(uint32(i)) + testTreap.Put(key, key) + } + + // Reset it. + testTreap.Reset() + + // Ensure the treap length is now 0. + if gotLen := testTreap.Len(); gotLen != 0 { + t.Fatalf("Len: unexpected length - got %d, want %d", gotLen, 0) + } + + // Ensure the reported size is now 0. + if gotSize := testTreap.Size(); gotSize != 0 { + t.Fatalf("Size: unexpected byte size - got %d, want 0", + gotSize) + } + + // Ensure the treap no longer has any of the keys. + for i := 0; i < numItems; i++ { + key := serializeUint32(uint32(i)) + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %x, want nil", + i, gotVal) + } + } + + // Ensure the number of keys iterated by ForEach is zero. + var numIterated int + testTreap.ForEach(func(k, v []byte) bool { + numIterated++ + return true + }) + if numIterated != 0 { + t.Fatalf("ForEach: unexpected iterate count - got %d, want 0", + numIterated) + } +} + +// TestMutableSequential ensures that putting keys into a mutable treap in +// sequential order works as expected. +func TestMutableSequential(t *testing.T) { + t.Parallel() + + // Insert a bunch of sequential keys while checking several of the treap + // functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewMutable() + for i := 0; i < numItems; i++ { + key := serializeUint32(uint32(i)) + testTreap.Put(key, key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != i+1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i+1) + } + + // Ensure the treap has the key. + if !testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap and ensure it is the expected + // value. + if gotVal := testTreap.Get(key); !bytes.Equal(gotVal, key) { + t.Fatalf("Get #%d: unexpected value - got %x, want %x", + i, gotVal, key) + } + + // Ensure the expected size is reported. + expectedSize += (nodeFieldsSize + 8) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } + + // Ensure the all keys are iterated by ForEach in order. + var numIterated int + testTreap.ForEach(func(k, v []byte) bool { + wantKey := serializeUint32(uint32(numIterated)) + + // Ensure the key is as expected. + if !bytes.Equal(k, wantKey) { + t.Fatalf("ForEach #%d: unexpected key - got %x, want %x", + numIterated, k, wantKey) + } + + // Ensure the value is as expected. + if !bytes.Equal(v, wantKey) { + t.Fatalf("ForEach #%d: unexpected value - got %x, want %x", + numIterated, v, wantKey) + } + + numIterated++ + return true + }) + + // Ensure all items were iterated. + if numIterated != numItems { + t.Fatalf("ForEach: unexpected iterate count - got %d, want %d", + numIterated, numItems) + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + key := serializeUint32(uint32(i)) + testTreap.Delete(key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != numItems-i-1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i-1) + } + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %x, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + expectedSize -= (nodeFieldsSize + 8) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } +} + +// TestMutableReverseSequential ensures that putting keys into a mutable treap +// in reverse sequential order works as expected. +func TestMutableReverseSequential(t *testing.T) { + t.Parallel() + + // Insert a bunch of sequential keys while checking several of the treap + // functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewMutable() + for i := 0; i < numItems; i++ { + key := serializeUint32(uint32(numItems - i - 1)) + testTreap.Put(key, key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != i+1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i+1) + } + + // Ensure the treap has the key. + if !testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap and ensure it is the expected + // value. + if gotVal := testTreap.Get(key); !bytes.Equal(gotVal, key) { + t.Fatalf("Get #%d: unexpected value - got %x, want %x", + i, gotVal, key) + } + + // Ensure the expected size is reported. + expectedSize += (nodeFieldsSize + 8) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } + + // Ensure the all keys are iterated by ForEach in order. + var numIterated int + testTreap.ForEach(func(k, v []byte) bool { + wantKey := serializeUint32(uint32(numIterated)) + + // Ensure the key is as expected. + if !bytes.Equal(k, wantKey) { + t.Fatalf("ForEach #%d: unexpected key - got %x, want %x", + numIterated, k, wantKey) + } + + // Ensure the value is as expected. + if !bytes.Equal(v, wantKey) { + t.Fatalf("ForEach #%d: unexpected value - got %x, want %x", + numIterated, v, wantKey) + } + + numIterated++ + return true + }) + + // Ensure all items were iterated. + if numIterated != numItems { + t.Fatalf("ForEach: unexpected iterate count - got %d, want %d", + numIterated, numItems) + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + // Intentionally use the reverse order they were inserted here. + key := serializeUint32(uint32(i)) + testTreap.Delete(key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != numItems-i-1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i-1) + } + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %x, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + expectedSize -= (nodeFieldsSize + 8) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } +} + +// TestMutableUnordered ensures that putting keys into a mutable treap in no +// paritcular order works as expected. +func TestMutableUnordered(t *testing.T) { + t.Parallel() + + // Insert a bunch of out-of-order keys while checking several of the + // treap functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewMutable() + for i := 0; i < numItems; i++ { + // Hash the serialized int to generate out-of-order keys. + hash := sha256.Sum256(serializeUint32(uint32(i))) + key := hash[:] + testTreap.Put(key, key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != i+1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i+1) + } + + // Ensure the treap has the key. + if !testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap and ensure it is the expected + // value. + if gotVal := testTreap.Get(key); !bytes.Equal(gotVal, key) { + t.Fatalf("Get #%d: unexpected value - got %x, want %x", + i, gotVal, key) + } + + // Ensure the expected size is reported. + expectedSize += nodeFieldsSize + uint64(len(key)+len(key)) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + // Hash the serialized int to generate out-of-order keys. + hash := sha256.Sum256(serializeUint32(uint32(i))) + key := hash[:] + testTreap.Delete(key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != numItems-i-1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i-1) + } + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %x, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + expectedSize -= (nodeFieldsSize + 64) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } +} + +// TestMutableDuplicatePut ensures that putting a duplicate key into a mutable +// treap updates the existing value. +func TestMutableDuplicatePut(t *testing.T) { + t.Parallel() + + key := serializeUint32(0) + val := []byte("testval") + + // Put the key twice with the second put being the expected final value. + testTreap := NewMutable() + testTreap.Put(key, key) + testTreap.Put(key, val) + + // Ensure the key still exists and is the new value. + if gotVal := testTreap.Has(key); gotVal != true { + t.Fatalf("Has: unexpected result - got %v, want false", gotVal) + } + if gotVal := testTreap.Get(key); !bytes.Equal(gotVal, val) { + t.Fatalf("Get: unexpected result - got %x, want %x", gotVal, val) + } + + // Ensure the expected size is reported. + expectedSize := uint64(nodeFieldsSize + len(key) + len(val)) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size: unexpected byte size - got %d, want %d", + gotSize, expectedSize) + } +} + +// TestMutableNilValue ensures that putting a nil value into a mutable treap +// results in a key being added with an empty byte slice. +func TestMutableNilValue(t *testing.T) { + t.Parallel() + + key := serializeUint32(0) + + // Put the key with a nil value. + testTreap := NewMutable() + testTreap.Put(key, nil) + + // Ensure the key exists and is an empty byte slice. + if gotVal := testTreap.Has(key); gotVal != true { + t.Fatalf("Has: unexpected result - got %v, want false", gotVal) + } + if gotVal := testTreap.Get(key); gotVal == nil { + t.Fatalf("Get: unexpected result - got nil, want empty slice") + } + if gotVal := testTreap.Get(key); len(gotVal) != 0 { + t.Fatalf("Get: unexpected result - got %x, want empty slice", + gotVal) + } +} + +// TestMutableForEachStopIterator ensures that returning false from the ForEach +// callback of a mutable treap stops iteration early. +func TestMutableForEachStopIterator(t *testing.T) { + t.Parallel() + + // Insert a few keys. + numItems := 10 + testTreap := NewMutable() + for i := 0; i < numItems; i++ { + key := serializeUint32(uint32(i)) + testTreap.Put(key, key) + } + + // Ensure ForEach exits early on false return by caller. + var numIterated int + testTreap.ForEach(func(k, v []byte) bool { + numIterated++ + if numIterated == numItems/2 { + return false + } + return true + }) + if numIterated != numItems/2 { + t.Fatalf("ForEach: unexpected iterate count - got %d, want %d", + numIterated, numItems/2) + } +} diff --git a/vendor/github.com/btcsuite/btcd/database/internal/treap/treapiter.go b/vendor/github.com/btcsuite/btcd/database/internal/treap/treapiter.go new file mode 100644 index 0000000000000000000000000000000000000000..d6981aafd8c73df671a91f083faeb9f506e6620e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/internal/treap/treapiter.go @@ -0,0 +1,354 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package treap + +import "bytes" + +// Iterator represents an iterator for forwards and backwards iteration over +// the contents of a treap (mutable or immutable). +type Iterator struct { + t *Mutable // Mutable treap iterator is associated with or nil + root *treapNode // Root node of treap iterator is associated with + node *treapNode // The node the iterator is positioned at + parents parentStack // The stack of parents needed to iterate + isNew bool // Whether the iterator has been positioned + seekKey []byte // Used to handle dynamic updates for mutable treap + startKey []byte // Used to limit the iterator to a range + limitKey []byte // Used to limit the iterator to a range +} + +// limitIterator clears the current iterator node if it is outside of the range +// specified when the iterator was created. It returns whether the iterator is +// valid. +func (iter *Iterator) limitIterator() bool { + if iter.node == nil { + return false + } + + node := iter.node + if iter.startKey != nil && bytes.Compare(node.key, iter.startKey) < 0 { + iter.node = nil + return false + } + + if iter.limitKey != nil && bytes.Compare(node.key, iter.limitKey) >= 0 { + iter.node = nil + return false + } + + return true +} + +// seek moves the iterator based on the provided key and flags. +// +// When the exact match flag is set, the iterator will either be moved to first +// key in the treap that exactly matches the provided key, or the one +// before/after it depending on the greater flag. +// +// When the exact match flag is NOT set, the iterator will be moved to the first +// key in the treap before/after the provided key depending on the greater flag. +// +// In all cases, the limits specified when the iterator was created are +// respected. +func (iter *Iterator) seek(key []byte, exactMatch bool, greater bool) bool { + iter.node = nil + iter.parents = parentStack{} + var selectedNodeDepth int + for node := iter.root; node != nil; { + iter.parents.Push(node) + + // Traverse left or right depending on the result of the + // comparison. Also, set the iterator to the node depending on + // the flags so the iterator is positioned properly when an + // exact match isn't found. + compareResult := bytes.Compare(key, node.key) + if compareResult < 0 { + if greater { + iter.node = node + selectedNodeDepth = iter.parents.Len() - 1 + } + node = node.left + continue + } + if compareResult > 0 { + if !greater { + iter.node = node + selectedNodeDepth = iter.parents.Len() - 1 + } + node = node.right + continue + } + + // The key is an exact match. Set the iterator and return now + // when the exact match flag is set. + if exactMatch { + iter.node = node + iter.parents.Pop() + return iter.limitIterator() + } + + // The key is an exact match, but the exact match is not set, so + // choose which direction to go based on whether the larger or + // smaller key was requested. + if greater { + node = node.right + } else { + node = node.left + } + } + + // There was either no exact match or there was an exact match but the + // exact match flag was not set. In any case, the parent stack might + // need to be adjusted to only include the parents up to the selected + // node. Also, ensure the selected node's key does not exceed the + // allowed range of the iterator. + for i := iter.parents.Len(); i > selectedNodeDepth; i-- { + iter.parents.Pop() + } + return iter.limitIterator() +} + +// First moves the iterator to the first key/value pair. When there is only a +// single key/value pair both First and Last will point to the same pair. +// Returns false if there are no key/value pairs. +func (iter *Iterator) First() bool { + // Seek the start key if the iterator was created with one. This will + // result in either an exact match, the first greater key, or an + // exhausted iterator if no such key exists. + iter.isNew = false + if iter.startKey != nil { + return iter.seek(iter.startKey, true, true) + } + + // The smallest key is in the left-most node. + iter.parents = parentStack{} + for node := iter.root; node != nil; node = node.left { + if node.left == nil { + iter.node = node + return true + } + iter.parents.Push(node) + } + return false +} + +// Last moves the iterator to the last key/value pair. When there is only a +// single key/value pair both First and Last will point to the same pair. +// Returns false if there are no key/value pairs. +func (iter *Iterator) Last() bool { + // Seek the limit key if the iterator was created with one. This will + // result in the first key smaller than the limit key, or an exhausted + // iterator if no such key exists. + iter.isNew = false + if iter.limitKey != nil { + return iter.seek(iter.limitKey, false, false) + } + + // The highest key is in the right-most node. + iter.parents = parentStack{} + for node := iter.root; node != nil; node = node.right { + if node.right == nil { + iter.node = node + return true + } + iter.parents.Push(node) + } + return false +} + +// Next moves the iterator to the next key/value pair and returns false when the +// iterator is exhausted. When invoked on a newly created iterator it will +// position the iterator at the first item. +func (iter *Iterator) Next() bool { + if iter.isNew { + return iter.First() + } + + if iter.node == nil { + return false + } + + // Reseek the previous key without allowing for an exact match if a + // force seek was requested. This results in the key greater than the + // previous one or an exhausted iterator if there is no such key. + if seekKey := iter.seekKey; seekKey != nil { + iter.seekKey = nil + return iter.seek(seekKey, false, true) + } + + // When there is no right node walk the parents until the parent's right + // node is not equal to the previous child. This will be the next node. + if iter.node.right == nil { + parent := iter.parents.Pop() + for parent != nil && parent.right == iter.node { + iter.node = parent + parent = iter.parents.Pop() + } + iter.node = parent + return iter.limitIterator() + } + + // There is a right node, so the next node is the left-most node down + // the right sub-tree. + iter.parents.Push(iter.node) + iter.node = iter.node.right + for node := iter.node.left; node != nil; node = node.left { + iter.parents.Push(iter.node) + iter.node = node + } + return iter.limitIterator() +} + +// Prev moves the iterator to the previous key/value pair and returns false when +// the iterator is exhausted. When invoked on a newly created iterator it will +// position the iterator at the last item. +func (iter *Iterator) Prev() bool { + if iter.isNew { + return iter.Last() + } + + if iter.node == nil { + return false + } + + // Reseek the previous key without allowing for an exact match if a + // force seek was requested. This results in the key smaller than the + // previous one or an exhausted iterator if there is no such key. + if seekKey := iter.seekKey; seekKey != nil { + iter.seekKey = nil + return iter.seek(seekKey, false, false) + } + + // When there is no left node walk the parents until the parent's left + // node is not equal to the previous child. This will be the previous + // node. + for iter.node.left == nil { + parent := iter.parents.Pop() + for parent != nil && parent.left == iter.node { + iter.node = parent + parent = iter.parents.Pop() + } + iter.node = parent + return iter.limitIterator() + } + + // There is a left node, so the previous node is the right-most node + // down the left sub-tree. + iter.parents.Push(iter.node) + iter.node = iter.node.left + for node := iter.node.right; node != nil; node = node.right { + iter.parents.Push(iter.node) + iter.node = node + } + return iter.limitIterator() +} + +// Seek moves the iterator to the first key/value pair with a key that is +// greater than or equal to the given key and returns true if successful. +func (iter *Iterator) Seek(key []byte) bool { + iter.isNew = false + return iter.seek(key, true, true) +} + +// Key returns the key of the current key/value pair or nil when the iterator +// is exhausted. The caller should not modify the contents of the returned +// slice. +func (iter *Iterator) Key() []byte { + if iter.node == nil { + return nil + } + return iter.node.key +} + +// Value returns the value of the current key/value pair or nil when the +// iterator is exhausted. The caller should not modify the contents of the +// returned slice. +func (iter *Iterator) Value() []byte { + if iter.node == nil { + return nil + } + return iter.node.value +} + +// Valid indicates whether the iterator is positioned at a valid key/value pair. +// It will be considered invalid when the iterator is newly created or exhausted. +func (iter *Iterator) Valid() bool { + return iter.node != nil +} + +// ForceReseek notifies the iterator that the underlying mutable treap has been +// updated, so the next call to Prev or Next needs to reseek in order to allow +// the iterator to continue working properly. +// +// NOTE: Calling this function when the iterator is associated with an immutable +// treap has no effect as you would expect. +func (iter *Iterator) ForceReseek() { + // Nothing to do when the iterator is associated with an immutable + // treap. + if iter.t == nil { + return + } + + // Update the iterator root to the mutable treap root in case it + // changed. + iter.root = iter.t.root + + // Set the seek key to the current node. This will force the Next/Prev + // functions to reseek, and thus properly reconstruct the iterator, on + // their next call. + if iter.node == nil { + iter.seekKey = nil + return + } + iter.seekKey = iter.node.key +} + +// Iterator returns a new iterator for the mutable treap. The newly returned +// iterator is not pointing to a valid item until a call to one of the methods +// to position it is made. +// +// The start key and limit key parameters cause the iterator to be limited to +// a range of keys. The start key is inclusive and the limit key is exclusive. +// Either or both can be nil if the functionality is not desired. +// +// WARNING: The ForceSeek method must be called on the returned iterator if +// the treap is mutated. Failure to do so will cause the iterator to return +// unexpected keys and/or values. +// +// For example: +// iter := t.Iterator(nil, nil) +// for iter.Next() { +// if someCondition { +// t.Delete(iter.Key()) +// iter.ForceReseek() +// } +// } +func (t *Mutable) Iterator(startKey, limitKey []byte) *Iterator { + iter := &Iterator{ + t: t, + root: t.root, + isNew: true, + startKey: startKey, + limitKey: limitKey, + } + return iter +} + +// Iterator returns a new iterator for the immutable treap. The newly returned +// iterator is not pointing to a valid item until a call to one of the methods +// to position it is made. +// +// The start key and limit key parameters cause the iterator to be limited to +// a range of keys. The start key is inclusive and the limit key is exclusive. +// Either or both can be nil if the functionality is not desired. +func (t *Immutable) Iterator(startKey, limitKey []byte) *Iterator { + iter := &Iterator{ + root: t.root, + isNew: true, + startKey: startKey, + limitKey: limitKey, + } + return iter +} diff --git a/vendor/github.com/btcsuite/btcd/database/internal/treap/treapiter_test.go b/vendor/github.com/btcsuite/btcd/database/internal/treap/treapiter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..08b4335ebc924a9361dc3644180add871872d837 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/internal/treap/treapiter_test.go @@ -0,0 +1,719 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package treap + +import ( + "bytes" + "encoding/binary" + "testing" +) + +// TestMutableIterator ensures that the general behavior of mutable treap +// iterators is as expected including tests for first, last, ordered and reverse +// ordered iteration, limiting the range, seeking, and initially unpositioned. +func TestMutableIterator(t *testing.T) { + t.Parallel() + + tests := []struct { + numKeys int + step int + startKey []byte + limitKey []byte + expectedFirst []byte + expectedLast []byte + seekKey []byte + expectedSeek []byte + }{ + // No range limits. Values are the set (0, 1, 2, ..., 49). + // Seek existing value. + { + numKeys: 50, + step: 1, + expectedFirst: serializeUint32(0), + expectedLast: serializeUint32(49), + seekKey: serializeUint32(12), + expectedSeek: serializeUint32(12), + }, + + // Limited to range [24, end]. Values are the set + // (0, 2, 4, ..., 48). Seek value that doesn't exist and is + // greater than largest existing key. + { + numKeys: 50, + step: 2, + startKey: serializeUint32(24), + expectedFirst: serializeUint32(24), + expectedLast: serializeUint32(48), + seekKey: serializeUint32(49), + expectedSeek: nil, + }, + + // Limited to range [start, 25). Values are the set + // (0, 3, 6, ..., 48). Seek value that doesn't exist but is + // before an existing value within the range. + { + numKeys: 50, + step: 3, + limitKey: serializeUint32(25), + expectedFirst: serializeUint32(0), + expectedLast: serializeUint32(24), + seekKey: serializeUint32(17), + expectedSeek: serializeUint32(18), + }, + + // Limited to range [10, 21). Values are the set + // (0, 4, ..., 48). Seek value that exists, but is before the + // minimum allowed range. + { + numKeys: 50, + step: 4, + startKey: serializeUint32(10), + limitKey: serializeUint32(21), + expectedFirst: serializeUint32(12), + expectedLast: serializeUint32(20), + seekKey: serializeUint32(4), + expectedSeek: nil, + }, + + // Limited by prefix {0,0,0}, range [{0,0,0}, {0,0,1}). + // Since it's a bytewise compare, {0,0,0,...} < {0,0,1}. + // Seek existing value within the allowed range. + { + numKeys: 300, + step: 1, + startKey: []byte{0x00, 0x00, 0x00}, + limitKey: []byte{0x00, 0x00, 0x01}, + expectedFirst: serializeUint32(0), + expectedLast: serializeUint32(255), + seekKey: serializeUint32(100), + expectedSeek: serializeUint32(100), + }, + } + +testLoop: + for i, test := range tests { + // Insert a bunch of keys. + testTreap := NewMutable() + for i := 0; i < test.numKeys; i += test.step { + key := serializeUint32(uint32(i)) + testTreap.Put(key, key) + } + + // Create new iterator limited by the test params. + iter := testTreap.Iterator(test.startKey, test.limitKey) + + // Ensure the first item is accurate. + hasFirst := iter.First() + if !hasFirst && test.expectedFirst != nil { + t.Errorf("First #%d: unexpected exhausted iterator", i) + continue + } + gotKey := iter.Key() + if !bytes.Equal(gotKey, test.expectedFirst) { + t.Errorf("First.Key #%d: unexpected key - got %x, "+ + "want %x", i, gotKey, test.expectedFirst) + continue + } + gotVal := iter.Value() + if !bytes.Equal(gotVal, test.expectedFirst) { + t.Errorf("First.Value #%d: unexpected value - got %x, "+ + "want %x", i, gotVal, test.expectedFirst) + continue + } + + // Ensure the iterator gives the expected items in order. + curNum := binary.BigEndian.Uint32(test.expectedFirst) + for iter.Next() { + curNum += uint32(test.step) + + // Ensure key is as expected. + gotKey := iter.Key() + expectedKey := serializeUint32(curNum) + if !bytes.Equal(gotKey, expectedKey) { + t.Errorf("iter.Key #%d (%d): unexpected key - "+ + "got %x, want %x", i, curNum, gotKey, + expectedKey) + continue testLoop + } + + // Ensure value is as expected. + gotVal := iter.Value() + if !bytes.Equal(gotVal, expectedKey) { + t.Errorf("iter.Value #%d (%d): unexpected "+ + "value - got %x, want %x", i, curNum, + gotVal, expectedKey) + continue testLoop + } + } + + // Ensure iterator is exhausted. + if iter.Valid() { + t.Errorf("Valid #%d: iterator should be exhausted", i) + continue + } + + // Ensure the last item is accurate. + hasLast := iter.Last() + if !hasLast && test.expectedLast != nil { + t.Errorf("Last #%d: unexpected exhausted iterator", i) + continue + } + gotKey = iter.Key() + if !bytes.Equal(gotKey, test.expectedLast) { + t.Errorf("Last.Key #%d: unexpected key - got %x, "+ + "want %x", i, gotKey, test.expectedLast) + continue + } + gotVal = iter.Value() + if !bytes.Equal(gotVal, test.expectedLast) { + t.Errorf("Last.Value #%d: unexpected value - got %x, "+ + "want %x", i, gotVal, test.expectedLast) + continue + } + + // Ensure the iterator gives the expected items in reverse + // order. + curNum = binary.BigEndian.Uint32(test.expectedLast) + for iter.Prev() { + curNum -= uint32(test.step) + + // Ensure key is as expected. + gotKey := iter.Key() + expectedKey := serializeUint32(curNum) + if !bytes.Equal(gotKey, expectedKey) { + t.Errorf("iter.Key #%d (%d): unexpected key - "+ + "got %x, want %x", i, curNum, gotKey, + expectedKey) + continue testLoop + } + + // Ensure value is as expected. + gotVal := iter.Value() + if !bytes.Equal(gotVal, expectedKey) { + t.Errorf("iter.Value #%d (%d): unexpected "+ + "value - got %x, want %x", i, curNum, + gotVal, expectedKey) + continue testLoop + } + } + + // Ensure iterator is exhausted. + if iter.Valid() { + t.Errorf("Valid #%d: iterator should be exhausted", i) + continue + } + + // Seek to the provided key. + seekValid := iter.Seek(test.seekKey) + if !seekValid && test.expectedSeek != nil { + t.Errorf("Seek #%d: unexpected exhausted iterator", i) + continue + } + gotKey = iter.Key() + if !bytes.Equal(gotKey, test.expectedSeek) { + t.Errorf("Seek.Key #%d: unexpected key - got %x, "+ + "want %x", i, gotKey, test.expectedSeek) + continue + } + gotVal = iter.Value() + if !bytes.Equal(gotVal, test.expectedSeek) { + t.Errorf("Seek.Value #%d: unexpected value - got %x, "+ + "want %x", i, gotVal, test.expectedSeek) + continue + } + + // Recreate the iterator and ensure calling Next on it before it + // has been positioned gives the first element. + iter = testTreap.Iterator(test.startKey, test.limitKey) + hasNext := iter.Next() + if !hasNext && test.expectedFirst != nil { + t.Errorf("Next #%d: unexpected exhausted iterator", i) + continue + } + gotKey = iter.Key() + if !bytes.Equal(gotKey, test.expectedFirst) { + t.Errorf("Next.Key #%d: unexpected key - got %x, "+ + "want %x", i, gotKey, test.expectedFirst) + continue + } + gotVal = iter.Value() + if !bytes.Equal(gotVal, test.expectedFirst) { + t.Errorf("Next.Value #%d: unexpected value - got %x, "+ + "want %x", i, gotVal, test.expectedFirst) + continue + } + + // Recreate the iterator and ensure calling Prev on it before it + // has been positioned gives the first element. + iter = testTreap.Iterator(test.startKey, test.limitKey) + hasPrev := iter.Prev() + if !hasPrev && test.expectedLast != nil { + t.Errorf("Prev #%d: unexpected exhausted iterator", i) + continue + } + gotKey = iter.Key() + if !bytes.Equal(gotKey, test.expectedLast) { + t.Errorf("Prev.Key #%d: unexpected key - got %x, "+ + "want %x", i, gotKey, test.expectedLast) + continue + } + gotVal = iter.Value() + if !bytes.Equal(gotVal, test.expectedLast) { + t.Errorf("Next.Value #%d: unexpected value - got %x, "+ + "want %x", i, gotVal, test.expectedLast) + continue + } + } +} + +// TestMutableEmptyIterator ensures that the various functions behave as +// expected when a mutable treap is empty. +func TestMutableEmptyIterator(t *testing.T) { + t.Parallel() + + // Create iterator against empty treap. + testTreap := NewMutable() + iter := testTreap.Iterator(nil, nil) + + // Ensure Valid on empty iterator reports it as exhausted. + if iter.Valid() { + t.Fatal("Valid: iterator should be exhausted") + } + + // Ensure First and Last on empty iterator report it as exhausted. + if iter.First() { + t.Fatal("First: iterator should be exhausted") + } + if iter.Last() { + t.Fatal("Last: iterator should be exhausted") + } + + // Ensure Next and Prev on empty iterator report it as exhausted. + if iter.Next() { + t.Fatal("Next: iterator should be exhausted") + } + if iter.Prev() { + t.Fatal("Prev: iterator should be exhausted") + } + + // Ensure Key and Value on empty iterator are nil. + if gotKey := iter.Key(); gotKey != nil { + t.Fatalf("Key: should be nil - got %q", gotKey) + } + if gotVal := iter.Value(); gotVal != nil { + t.Fatalf("Value: should be nil - got %q", gotVal) + } + + // Ensure Next and Prev report exhausted after forcing a reseek on an + // empty iterator. + iter.ForceReseek() + if iter.Next() { + t.Fatal("Next: iterator should be exhausted") + } + iter.ForceReseek() + if iter.Prev() { + t.Fatal("Prev: iterator should be exhausted") + } +} + +// TestIteratorUpdates ensures that issuing a call to ForceReseek on an iterator +// that had the underlying mutable treap updated works as expected. +func TestIteratorUpdates(t *testing.T) { + t.Parallel() + + // Create a new treap with various values inserted in no particular + // order. The resulting keys are the set (2, 4, 7, 11, 18, 25). + testTreap := NewMutable() + testTreap.Put(serializeUint32(7), nil) + testTreap.Put(serializeUint32(2), nil) + testTreap.Put(serializeUint32(18), nil) + testTreap.Put(serializeUint32(11), nil) + testTreap.Put(serializeUint32(25), nil) + testTreap.Put(serializeUint32(4), nil) + + // Create an iterator against the treap with a range that excludes the + // lowest and highest entries. The limited set is then (4, 7, 11, 18) + iter := testTreap.Iterator(serializeUint32(3), serializeUint32(25)) + + // Delete a key from the middle of the range and notify the iterator to + // force a reseek. + testTreap.Delete(serializeUint32(11)) + iter.ForceReseek() + + // Ensure that calling Next on the iterator after the forced reseek + // gives the expected key. The limited set of keys at this point is + // (4, 7, 18) and the iterator has not yet been positioned. + if !iter.Next() { + t.Fatal("ForceReseek.Next: unexpected exhausted iterator") + } + wantKey := serializeUint32(4) + gotKey := iter.Key() + if !bytes.Equal(gotKey, wantKey) { + t.Fatalf("ForceReseek.Key: unexpected key - got %x, want %x", + gotKey, wantKey) + } + + // Delete the key the iterator is currently position at and notify the + // iterator to force a reseek. + testTreap.Delete(serializeUint32(4)) + iter.ForceReseek() + + // Ensure that calling Next on the iterator after the forced reseek + // gives the expected key. The limited set of keys at this point is + // (7, 18) and the iterator is positioned at a deleted entry before 7. + if !iter.Next() { + t.Fatal("ForceReseek.Next: unexpected exhausted iterator") + } + wantKey = serializeUint32(7) + gotKey = iter.Key() + if !bytes.Equal(gotKey, wantKey) { + t.Fatalf("ForceReseek.Key: unexpected key - got %x, want %x", + gotKey, wantKey) + } + + // Add a key before the current key the iterator is position at and + // notify the iterator to force a reseek. + testTreap.Put(serializeUint32(4), nil) + iter.ForceReseek() + + // Ensure that calling Prev on the iterator after the forced reseek + // gives the expected key. The limited set of keys at this point is + // (4, 7, 18) and the iterator is positioned at 7. + if !iter.Prev() { + t.Fatal("ForceReseek.Prev: unexpected exhausted iterator") + } + wantKey = serializeUint32(4) + gotKey = iter.Key() + if !bytes.Equal(gotKey, wantKey) { + t.Fatalf("ForceReseek.Key: unexpected key - got %x, want %x", + gotKey, wantKey) + } + + // Delete the next key the iterator would ordinarily move to then notify + // the iterator to force a reseek. + testTreap.Delete(serializeUint32(7)) + iter.ForceReseek() + + // Ensure that calling Next on the iterator after the forced reseek + // gives the expected key. The limited set of keys at this point is + // (4, 18) and the iterator is positioned at 4. + if !iter.Next() { + t.Fatal("ForceReseek.Next: unexpected exhausted iterator") + } + wantKey = serializeUint32(18) + gotKey = iter.Key() + if !bytes.Equal(gotKey, wantKey) { + t.Fatalf("ForceReseek.Key: unexpected key - got %x, want %x", + gotKey, wantKey) + } +} + +// TestImmutableIterator ensures that the general behavior of immutable treap +// iterators is as expected including tests for first, last, ordered and reverse +// ordered iteration, limiting the range, seeking, and initially unpositioned. +func TestImmutableIterator(t *testing.T) { + t.Parallel() + + tests := []struct { + numKeys int + step int + startKey []byte + limitKey []byte + expectedFirst []byte + expectedLast []byte + seekKey []byte + expectedSeek []byte + }{ + // No range limits. Values are the set (0, 1, 2, ..., 49). + // Seek existing value. + { + numKeys: 50, + step: 1, + expectedFirst: serializeUint32(0), + expectedLast: serializeUint32(49), + seekKey: serializeUint32(12), + expectedSeek: serializeUint32(12), + }, + + // Limited to range [24, end]. Values are the set + // (0, 2, 4, ..., 48). Seek value that doesn't exist and is + // greater than largest existing key. + { + numKeys: 50, + step: 2, + startKey: serializeUint32(24), + expectedFirst: serializeUint32(24), + expectedLast: serializeUint32(48), + seekKey: serializeUint32(49), + expectedSeek: nil, + }, + + // Limited to range [start, 25). Values are the set + // (0, 3, 6, ..., 48). Seek value that doesn't exist but is + // before an existing value within the range. + { + numKeys: 50, + step: 3, + limitKey: serializeUint32(25), + expectedFirst: serializeUint32(0), + expectedLast: serializeUint32(24), + seekKey: serializeUint32(17), + expectedSeek: serializeUint32(18), + }, + + // Limited to range [10, 21). Values are the set + // (0, 4, ..., 48). Seek value that exists, but is before the + // minimum allowed range. + { + numKeys: 50, + step: 4, + startKey: serializeUint32(10), + limitKey: serializeUint32(21), + expectedFirst: serializeUint32(12), + expectedLast: serializeUint32(20), + seekKey: serializeUint32(4), + expectedSeek: nil, + }, + + // Limited by prefix {0,0,0}, range [{0,0,0}, {0,0,1}). + // Since it's a bytewise compare, {0,0,0,...} < {0,0,1}. + // Seek existing value within the allowed range. + { + numKeys: 300, + step: 1, + startKey: []byte{0x00, 0x00, 0x00}, + limitKey: []byte{0x00, 0x00, 0x01}, + expectedFirst: serializeUint32(0), + expectedLast: serializeUint32(255), + seekKey: serializeUint32(100), + expectedSeek: serializeUint32(100), + }, + } + +testLoop: + for i, test := range tests { + // Insert a bunch of keys. + testTreap := NewImmutable() + for i := 0; i < test.numKeys; i += test.step { + key := serializeUint32(uint32(i)) + testTreap = testTreap.Put(key, key) + } + + // Create new iterator limited by the test params. + iter := testTreap.Iterator(test.startKey, test.limitKey) + + // Ensure the first item is accurate. + hasFirst := iter.First() + if !hasFirst && test.expectedFirst != nil { + t.Errorf("First #%d: unexpected exhausted iterator", i) + continue + } + gotKey := iter.Key() + if !bytes.Equal(gotKey, test.expectedFirst) { + t.Errorf("First.Key #%d: unexpected key - got %x, "+ + "want %x", i, gotKey, test.expectedFirst) + continue + } + gotVal := iter.Value() + if !bytes.Equal(gotVal, test.expectedFirst) { + t.Errorf("First.Value #%d: unexpected value - got %x, "+ + "want %x", i, gotVal, test.expectedFirst) + continue + } + + // Ensure the iterator gives the expected items in order. + curNum := binary.BigEndian.Uint32(test.expectedFirst) + for iter.Next() { + curNum += uint32(test.step) + + // Ensure key is as expected. + gotKey := iter.Key() + expectedKey := serializeUint32(curNum) + if !bytes.Equal(gotKey, expectedKey) { + t.Errorf("iter.Key #%d (%d): unexpected key - "+ + "got %x, want %x", i, curNum, gotKey, + expectedKey) + continue testLoop + } + + // Ensure value is as expected. + gotVal := iter.Value() + if !bytes.Equal(gotVal, expectedKey) { + t.Errorf("iter.Value #%d (%d): unexpected "+ + "value - got %x, want %x", i, curNum, + gotVal, expectedKey) + continue testLoop + } + } + + // Ensure iterator is exhausted. + if iter.Valid() { + t.Errorf("Valid #%d: iterator should be exhausted", i) + continue + } + + // Ensure the last item is accurate. + hasLast := iter.Last() + if !hasLast && test.expectedLast != nil { + t.Errorf("Last #%d: unexpected exhausted iterator", i) + continue + } + gotKey = iter.Key() + if !bytes.Equal(gotKey, test.expectedLast) { + t.Errorf("Last.Key #%d: unexpected key - got %x, "+ + "want %x", i, gotKey, test.expectedLast) + continue + } + gotVal = iter.Value() + if !bytes.Equal(gotVal, test.expectedLast) { + t.Errorf("Last.Value #%d: unexpected value - got %x, "+ + "want %x", i, gotVal, test.expectedLast) + continue + } + + // Ensure the iterator gives the expected items in reverse + // order. + curNum = binary.BigEndian.Uint32(test.expectedLast) + for iter.Prev() { + curNum -= uint32(test.step) + + // Ensure key is as expected. + gotKey := iter.Key() + expectedKey := serializeUint32(curNum) + if !bytes.Equal(gotKey, expectedKey) { + t.Errorf("iter.Key #%d (%d): unexpected key - "+ + "got %x, want %x", i, curNum, gotKey, + expectedKey) + continue testLoop + } + + // Ensure value is as expected. + gotVal := iter.Value() + if !bytes.Equal(gotVal, expectedKey) { + t.Errorf("iter.Value #%d (%d): unexpected "+ + "value - got %x, want %x", i, curNum, + gotVal, expectedKey) + continue testLoop + } + } + + // Ensure iterator is exhausted. + if iter.Valid() { + t.Errorf("Valid #%d: iterator should be exhausted", i) + continue + } + + // Seek to the provided key. + seekValid := iter.Seek(test.seekKey) + if !seekValid && test.expectedSeek != nil { + t.Errorf("Seek #%d: unexpected exhausted iterator", i) + continue + } + gotKey = iter.Key() + if !bytes.Equal(gotKey, test.expectedSeek) { + t.Errorf("Seek.Key #%d: unexpected key - got %x, "+ + "want %x", i, gotKey, test.expectedSeek) + continue + } + gotVal = iter.Value() + if !bytes.Equal(gotVal, test.expectedSeek) { + t.Errorf("Seek.Value #%d: unexpected value - got %x, "+ + "want %x", i, gotVal, test.expectedSeek) + continue + } + + // Recreate the iterator and ensure calling Next on it before it + // has been positioned gives the first element. + iter = testTreap.Iterator(test.startKey, test.limitKey) + hasNext := iter.Next() + if !hasNext && test.expectedFirst != nil { + t.Errorf("Next #%d: unexpected exhausted iterator", i) + continue + } + gotKey = iter.Key() + if !bytes.Equal(gotKey, test.expectedFirst) { + t.Errorf("Next.Key #%d: unexpected key - got %x, "+ + "want %x", i, gotKey, test.expectedFirst) + continue + } + gotVal = iter.Value() + if !bytes.Equal(gotVal, test.expectedFirst) { + t.Errorf("Next.Value #%d: unexpected value - got %x, "+ + "want %x", i, gotVal, test.expectedFirst) + continue + } + + // Recreate the iterator and ensure calling Prev on it before it + // has been positioned gives the first element. + iter = testTreap.Iterator(test.startKey, test.limitKey) + hasPrev := iter.Prev() + if !hasPrev && test.expectedLast != nil { + t.Errorf("Prev #%d: unexpected exhausted iterator", i) + continue + } + gotKey = iter.Key() + if !bytes.Equal(gotKey, test.expectedLast) { + t.Errorf("Prev.Key #%d: unexpected key - got %x, "+ + "want %x", i, gotKey, test.expectedLast) + continue + } + gotVal = iter.Value() + if !bytes.Equal(gotVal, test.expectedLast) { + t.Errorf("Next.Value #%d: unexpected value - got %x, "+ + "want %x", i, gotVal, test.expectedLast) + continue + } + } +} + +// TestImmutableEmptyIterator ensures that the various functions behave as +// expected when an immutable treap is empty. +func TestImmutableEmptyIterator(t *testing.T) { + t.Parallel() + + // Create iterator against empty treap. + testTreap := NewImmutable() + iter := testTreap.Iterator(nil, nil) + + // Ensure Valid on empty iterator reports it as exhausted. + if iter.Valid() { + t.Fatal("Valid: iterator should be exhausted") + } + + // Ensure First and Last on empty iterator report it as exhausted. + if iter.First() { + t.Fatal("First: iterator should be exhausted") + } + if iter.Last() { + t.Fatal("Last: iterator should be exhausted") + } + + // Ensure Next and Prev on empty iterator report it as exhausted. + if iter.Next() { + t.Fatal("Next: iterator should be exhausted") + } + if iter.Prev() { + t.Fatal("Prev: iterator should be exhausted") + } + + // Ensure Key and Value on empty iterator are nil. + if gotKey := iter.Key(); gotKey != nil { + t.Fatalf("Key: should be nil - got %q", gotKey) + } + if gotVal := iter.Value(); gotVal != nil { + t.Fatalf("Value: should be nil - got %q", gotVal) + } + + // Ensure calling ForceReseek on an immutable treap iterator does not + // cause any issues since it only applies to mutable treap iterators. + iter.ForceReseek() + if iter.Next() { + t.Fatal("Next: iterator should be exhausted") + } + iter.ForceReseek() + if iter.Prev() { + t.Fatal("Prev: iterator should be exhausted") + } +} diff --git a/vendor/github.com/btcsuite/btcd/database/log.go b/vendor/github.com/btcsuite/btcd/database/log.go new file mode 100644 index 0000000000000000000000000000000000000000..4a7b9a88493d33f2b05125a95df965590afa1094 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/log.go @@ -0,0 +1,65 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package database + +import ( + "errors" + "io" + + "github.com/btcsuite/btclog" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + DisableLog() +} + +// DisableLog disables all library log output. Logging output is disabled +// by default until either UseLogger or SetLogWriter are called. +func DisableLog() { + log = btclog.Disabled +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger + + // Update the logger for the registered drivers. + for _, drv := range drivers { + if drv.UseLogger != nil { + drv.UseLogger(logger) + } + } +} + +// SetLogWriter uses a specified io.Writer to output package logging info. +// This allows a caller to direct package logging output without needing a +// dependency on seelog. If the caller is also using btclog, UseLogger should +// be used instead. +func SetLogWriter(w io.Writer, level string) error { + if w == nil { + return errors.New("nil writer") + } + + lvl, ok := btclog.LogLevelFromString(level) + if !ok { + return errors.New("invalid log level") + } + + l, err := btclog.NewLoggerFromWriter(w, lvl) + if err != nil { + return err + } + + UseLogger(l) + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/database/log_test.go b/vendor/github.com/btcsuite/btcd/database/log_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6b37cf67a8bcfb2b304aa8b48abdf0d4801e76ba --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/database/log_test.go @@ -0,0 +1,67 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package database_test + +import ( + "errors" + "io" + "os" + "testing" + + "github.com/btcsuite/btcd/database" +) + +// TestSetLogWriter ensures the +func TestSetLogWriter(t *testing.T) { + tests := []struct { + name string + w io.Writer + level string + expected error + }{ + { + name: "nil writer", + w: nil, + level: "trace", + expected: errors.New("nil writer"), + }, + { + name: "invalid log level", + w: os.Stdout, + level: "wrong", + expected: errors.New("invalid log level"), + }, + { + name: "use off level", + w: os.Stdout, + level: "off", + expected: errors.New("min level can't be greater than max. Got min: 6, max: 5"), + }, + { + name: "pass", + w: os.Stdout, + level: "debug", + expected: nil, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + err := database.SetLogWriter(test.w, test.level) + if err != nil { + if err.Error() != test.expected.Error() { + t.Errorf("SetLogWriter #%d (%s) wrong result\n"+ + "got: %v\nwant: %v", i, test.name, err, + test.expected) + } + } else { + if test.expected != nil { + t.Errorf("SetLogWriter #%d (%s) wrong result\n"+ + "got: %v\nwant: %v", i, test.name, err, + test.expected) + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/database/testdata/blocks1-256.bz2 b/vendor/github.com/btcsuite/btcd/database/testdata/blocks1-256.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..6b8bda4429200c0566bb13c28c35d6397272e475 Binary files /dev/null and b/vendor/github.com/btcsuite/btcd/database/testdata/blocks1-256.bz2 differ diff --git a/vendor/github.com/btcsuite/btcd/deps.txt b/vendor/github.com/btcsuite/btcd/deps.txt new file mode 100644 index 0000000000000000000000000000000000000000..9284bf98e47de3016047f69da1d55557b032758c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/deps.txt @@ -0,0 +1,220 @@ +Dependency Commits +================== + +btcd 0.3.0 Alpha +---------------- +btcchain 4151416b16a7b76e54eeec5ae4da7a99f91ad2b9 +btcdb e4d3f259912ab65f1ee64635bab536ded54b6ef9 +btcec e748650cc8303352d91afb12270ec0c2d6769064 +btcjson 5444d262b80de5056fce0d6a9194c0ade3a73268 +btcscript a5f81fc5457eec41c1438f12b0d92a27a586039f +btcutil 867149f4705b6227a9edcba3e69cf292c5d1901a +btcwire 5f971e10e626cdb48d2ef0dd834d943408274280 +go-flags b7c08f54e16afa16e5f2194895d25f31fefeb99b +go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78 +goleveldb 88ef664c9e3f806fd3dcd79f2dcb70292f8cf91d +seelog 6b91ad56123bb473755caa213db2bde5422177bf + +btcd 0.3.1 Alpha +---------------- +btcchain b7b9823f6cb9e790157ccdb2938f823b400c8a30 +btcdb 3b56ccaff7748219a695a12570c34f57badd253b +btcec e748650cc8303352d91afb12270ec0c2d6769064 +btcjson 31489c15b3a178562aa4245a83e353414ddeb044 +btcscript 003a41f66f5840674faa94022bc3cbd8892bb32e +btcutil f72ab9cfcef8df68fb81ed820c3bc9f4a378b0f8 +btcwire fa1d343430ecff479bee0652a7a000aed61f3f6f +go-flags 91d04d6c04078a9a1b665530c08b758ec2fbef85 +go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78 +goleveldb 1d0f0aa639f784f38fa998bdf50911ae8ccbcff3 +seelog 6b91ad56123bb473755caa213db2bde5422177bf + +btcd 0.3.2 Alpha +---------------- +btcchain b7b9823f6cb9e790157ccdb2938f823b400c8a30 +btcdb 998682ec8383a40351e772f65555a8079b78d520 +btcec e748650cc8303352d91afb12270ec0c2d6769064 +btcjson cbd3d730a91fa92a851aa4aa97053a0dc628e8c4 +btcscript 40c75b27ac8103234379ef8243a8393019c8c708 +btcutil f72ab9cfcef8df68fb81ed820c3bc9f4a378b0f8 +btcwire fa1d343430ecff479bee0652a7a000aed61f3f6f +go-flags 91d04d6c04078a9a1b665530c08b758ec2fbef85 +go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78 +goleveldb 1d0f0aa639f784f38fa998bdf50911ae8ccbcff3 +seelog 6b91ad56123bb473755caa213db2bde5422177bf + +btcd 0.3.3 Alpha +---------------- +btcchain 2300b357319ccf3c27029ef90da7eb7f9ff792ba +btcdb d2269684720afa0fa79f7b5f4c65e398304274c2 +btcec a97fd5fe2c670030f8d77dc13b9fa8401ef9f349 +btcjson d20f958c92e1444d83215c3cf98d6eef41898dcb +btcscript f4a6449ad3b90d0c830bf2895b83ced8d5fb91e9 +btcutil aa811871654079f5036d3692dcf6c66928d19447 +btcwire dd41f7e91a682b7c1ceed633e12ece6ba7b6bc72 +btcws 497f1770445677372557d70621782d921a5318e3 +go-flags fa177a84d3b73bf7e4b79125b2a963bc134eff77 +go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78 +goleveldb 1d0f0aa639f784f38fa998bdf50911ae8ccbcff3 +seelog 6b91ad56123bb473755caa213db2bde5422177bf + +btcd 0.4.0 Alpha +---------------- +btcchain 55331de532b4a4749e242cc434f86e7cd0255166 +btcdb 472c998c0d761fa29ef2e05ddfa2460e943033a6 +btcec 95b3c063e382af78e4c6876408c31f68efc67840 +btcjson 1f52db626dd6b1df6103c6b11cf6e73b72cbe536 +btclog fa3217f76bac7375db18868dfcbc7c69b8c36552 +btcscript c0c167cc15aa3b721c983fd775cdef7afb42de38 +btcutil 9759e8dc64c227fc99c2a01b5c3e52f6700d58f0 +btcwire e5a09bdfaa139999d8195c10cea07312dbeb1065 +btcws 630d38b1b91215e711868593790ddcd1e50161ec +fastsha256 88f426c18509f838fa498c0e82b0faadd86ecc72 +go-flags a53ab6481be8dd78e060df308a9f577859dfeab5 +go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78 +goleveldb 1d0f0aa639f784f38fa998bdf50911ae8ccbcff3 +seelog 6b91ad56123bb473755caa213db2bde5422177bf +winsvc 2a5f78f6f2059b884aad8f6907fb029afda48c43 + +btcd 0.5.0 Alpha +---------------- +btcchain 28f485a1d1163b378972708cd478f24e64384b3d +btcdb 6578e7345f7dafe775b56f0d2db1e08f5cd0e328 +btcec 58cab817f0863f60fa3c8c14c4b56e115ee549de +btcjson bf90ed21428dac774da33a6b965e46c54cb14d38 +btclog 1cd4812f9be0b0c88dd43510d6ce98adfd083b75 +btcscript 565f11409c4a1fca39814e9f616a3dada8ca78c7 +btcutil 2af3c8263a76bc901c2e2cc7a23f71b91ab97189 +btcwire 6c7f45fdb74e92e8de38775a691545ef888d8998 +btcws 05a0ba18b48a12b8d0d615a1371befdaa6bdc5dc +fastsha256 a3150791c7d7ccb8050fc0d13528b873fd67e8c3 +go-flags a53ab6481be8dd78e060df308a9f577859dfeab5 +go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78 +goleveldb 3ce16077443eab51c7bc8371fef66fddee0b5870 +seelog 6b91ad56123bb473755caa213db2bde5422177bf +winsvc 2a5f78f6f2059b884aad8f6907fb029afda48c43 + +btcd 0.6.0 Alpha +---------------- +btcchain e1f66f6103a8775f5a21bf2c531d0167a8789100 +btcdb 0a86df4a162ddd8311194d44231f69e94abd1d23 +btcec 58cab817f0863f60fa3c8c14c4b56e115ee549de +btcjson 0d1539118b5b6be03b4d2c260177ac978fdb4f3a +btclog 1cd4812f9be0b0c88dd43510d6ce98adfd083b75 +btcscript 971fbf8b28711c26c939833bdf53ab286cde02a4 +btcutil ca515e278dbc106e15a597e8ac5dc39239672f09 +btcwire f6b03bf8a8308837a5663e537be297956279dd67 +btcws 3cba42282e40cbc423ad1302bc44ffd060fc5824 +fastsha256 a3150791c7d7ccb8050fc0d13528b873fd67e8c3 +go-flags a53ab6481be8dd78e060df308a9f577859dfeab5 +go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78 +goleveldb 3ce16077443eab51c7bc8371fef66fddee0b5870 +seelog 6b91ad56123bb473755caa213db2bde5422177bf +winsvc 2a5f78f6f2059b884aad8f6907fb029afda48c43 + +btcd 0.7.0 Alpha +---------------- +btcchain 149d8176b0ff0b1fc848bca46ab8bca2079b7ab8 +btcdb 0a86df4a162ddd8311194d44231f69e94abd1d23 +btcec ff3fac426d4d037505ea8208b79e93c2852451e0 +btcjson 21b974e2715f48e36dbcb759314dfe96cdfb094d +btclog 1cd4812f9be0b0c88dd43510d6ce98adfd083b75 +btcscript 2b0b512a83acb2bdfa9766b7dc44b6f81cb89c02 +btcutil ca515e278dbc106e15a597e8ac5dc39239672f09 +btcwire f6b03bf8a8308837a5663e537be297956279dd67 +btcws 5c666417351a31c54a7569553198d0763a2337e9 +fastsha256 a3150791c7d7ccb8050fc0d13528b873fd67e8c3 +go-flags a53ab6481be8dd78e060df308a9f577859dfeab5 +go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78 +goleveldb 3ce16077443eab51c7bc8371fef66fddee0b5870 +seelog 6b91ad56123bb473755caa213db2bde5422177bf +winsvc 2a5f78f6f2059b884aad8f6907fb029afda48c43 + +btcd 0.8.0 Beta +--------------- +btcchain d4082e4f24fa58fd910b6e2bf23f6ba64e06d1c9 +btcdb ae25e28d7e5e815043e5422d106c7c7f36738dd8 +btcec cea5e44f2d87c7a5a8d38d948995d375d37431a2 +btcjson 82e79619a5fee4c841f1309b9bfad1263e005d84 +btclog 9caaed73fd7b082945434ed8567ff685ebf2ea06 +btcnet 536c1e3c298cd868e83f0158d9f56e5849468692 +btcscript 27e1ad758bb0a88347436011624e2d7ad459f0fb +btcutil bff18e5a9305b1f54f900e30c7090d5a19e149d0 +btcwire a4978ba4c8a272c8a0dd710f5ddc797967113dba +btcws c24be8b7bfbba53b40c10f2d22abdb739c888936 +fastsha256 ce4c212c9d0d73ea7074795f8694f49b82152b18 +go-flags a53ab6481be8dd78e060df308a9f577859dfeab5 +go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78 +goleveldb 3ce16077443eab51c7bc8371fef66fddee0b5870 +seelog 6b91ad56123bb473755caa213db2bde5422177bf +winsvc 2a5f78f6f2059b884aad8f6907fb029afda48c43 + +btcd 0.9.0 Beta +--------------- +btcchain f5f03e8172ab3ebbb9934142ef757833c0d62300 +btcdb 2a688a70b2aa4456fc8f01a03039a96fa4be2626 +btcec 4ca0daacc139d145eab60124a16192265da27acb +btcjson 1caddd4a37d2e85c65e36065bdd47cf946e9e4c1 +btclog 7e65e0a3e1fe7b3b579f00ebdabc7b75159ff116 +btcnet 3bbe8ff0ca63f8ce94982f7624f825f84ac10b0a +btcscript 2803ea17c2a7ec5b806b9085c2a7949cee5355f1 +btcutil 2539ca986029f25880b91460d3f4be4e961b33e9 +btcwire 8733b9c8df9fd1c6a563cc06cecb301610b87fe5 +btcws 85cc323e34e694615c4364ebe97010d7c3197952 +fastsha256 17eaa235e05dfb79432c9594dcafe20bb05690a7 +go-flags a53ab6481be8dd78e060df308a9f577859dfeab5 +go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78 +goleveldb ca108ee68824577f394aa81c72ac2f473ce9d04d +seelog 6b91ad56123bb473755caa213db2bde5422177bf +websocket 08812523f279944afd46e7fb820fa55e31f207f2 +winsvc 2a5f78f6f2059b884aad8f6907fb029afda48c43 + +btcd 0.10.0 Beta +---------------- +btclog baa3c5ace9907202e5659ee1416ee902facde213 +btcutil 499e9e0daa5be5eb34fb6e5efc091d245b38edcf +fastsha256 17eaa235e05dfb79432c9594dcafe20bb05690a7 +go-flags 6c288d648c1cc1befcb90cb5511dcacf64ae8e61 +go-socks a7a73aaa00584d5aee1efdb0891e19b7228d8d69 +goleveldb aa685406548ca8a61d8e546a37129081798445ad +seelog 313961b101eb55f65ae0f03ddd4e322731763b6c +websocket 31079b6807923eb23992c421b114992b95131b55 +winsvc f8fb11f83f7e860e3769a08e6811d1b399a43722 + +btcd 0.11.0 Beta +---------------- +btclog 5005b7240f310ae8f01c7664a3954d280241eb2b +btcutil 1b73e9828d204297fcb433c66de67a7730ce6f54 +fastsha256 302ad4db268b46f9ebda3078f6f7397f96047735 +go-flags 6c288d648c1cc1befcb90cb5511dcacf64ae8e61 +go-socks a7a73aaa00584d5aee1efdb0891e19b7228d8d69 +goleveldb d87ae1f5189b9ece9ed9b661e490a668e9a1d0df +seelog 313961b101eb55f65ae0f03ddd4e322731763b6c +websocket 31079b6807923eb23992c421b114992b95131b55 +winsvc f8fb11f83f7e860e3769a08e6811d1b399a43722 + +btcd 0.11.1 Beta +---------------- +btclog 5005b7240f310ae8f01c7664a3954d280241eb2b +btcutil 1b73e9828d204297fcb433c66de67a7730ce6f54 +fastsha256 302ad4db268b46f9ebda3078f6f7397f96047735 +go-flags 6c288d648c1cc1befcb90cb5511dcacf64ae8e61 +go-socks cfe8b59e565c1a5bd4e2005d77cd9aa8b2e14524 +goleveldb d87ae1f5189b9ece9ed9b661e490a668e9a1d0df +seelog 313961b101eb55f65ae0f03ddd4e322731763b6c +websocket 31079b6807923eb23992c421b114992b95131b55 +winsvc f8fb11f83f7e860e3769a08e6811d1b399a43722 + +btcd 0.12.0 Beta +---------------- +btclog 5005b7240f310ae8f01c7664a3954d280241eb2b +btcutil ff82dacded1c76d101bce55c394c03c0bbff69e8 +fastsha256 302ad4db268b46f9ebda3078f6f7397f96047735 +go-flags 6c288d648c1cc1befcb90cb5511dcacf64ae8e61 +go-socks cfe8b59e565c1a5bd4e2005d77cd9aa8b2e14524 +golangcrypto 53f62d9b43e87a6c56975cf862af7edf33a8d0df +goleveldb d87ae1f5189b9ece9ed9b661e490a668e9a1d0df +seelog 313961b101eb55f65ae0f03ddd4e322731763b6c +snappy-go fff4c573c694b1e2d5c7a9b2b72dbe947b6a4c8e +websocket 31079b6807923eb23992c421b114992b95131b55 +winsvc f8fb11f83f7e860e3769a08e6811d1b399a43722 diff --git a/vendor/github.com/btcsuite/btcd/discovery.go b/vendor/github.com/btcsuite/btcd/discovery.go new file mode 100644 index 0000000000000000000000000000000000000000..44c58f78f135d352eb5629f6ac1959187364749f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/discovery.go @@ -0,0 +1,144 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/binary" + "errors" + "net" +) + +const ( + torSucceeded = 0x00 + torGeneralError = 0x01 + torNotAllowed = 0x02 + torNetUnreachable = 0x03 + torHostUnreachable = 0x04 + torConnectionRefused = 0x05 + torTTLExpired = 0x06 + torCmdNotSupported = 0x07 + torAddrNotSupported = 0x08 +) + +var ( + // ErrTorInvalidAddressResponse indicates an invalid address was + // returned by the Tor DNS resolver. + ErrTorInvalidAddressResponse = errors.New("invalid address response") + + // ErrTorInvalidProxyResponse indicates the Tor proxy returned a + // response in an unexpected format. + ErrTorInvalidProxyResponse = errors.New("invalid proxy response") + + // ErrTorUnrecognizedAuthMethod indicates the authentication method + // provided is not recognized. + ErrTorUnrecognizedAuthMethod = errors.New("invalid proxy authentication method") + + torStatusErrors = map[byte]error{ + torSucceeded: errors.New("tor succeeded"), + torGeneralError: errors.New("tor general error"), + torNotAllowed: errors.New("tor not allowed"), + torNetUnreachable: errors.New("tor network is unreachable"), + torHostUnreachable: errors.New("tor host is unreachable"), + torConnectionRefused: errors.New("tor connection refused"), + torTTLExpired: errors.New("tor TTL expired"), + torCmdNotSupported: errors.New("tor command not supported"), + torAddrNotSupported: errors.New("tor address type not supported"), + } +) + +// torLookupIP uses Tor to resolve DNS via the SOCKS extension they provide for +// resolution over the Tor network. Tor itself doesn't support ipv6 so this +// doesn't either. +func torLookupIP(host, proxy string) ([]net.IP, error) { + conn, err := net.Dial("tcp", proxy) + if err != nil { + return nil, err + } + defer conn.Close() + + buf := []byte{'\x05', '\x01', '\x00'} + _, err = conn.Write(buf) + if err != nil { + return nil, err + } + + buf = make([]byte, 2) + _, err = conn.Read(buf) + if err != nil { + return nil, err + } + if buf[0] != '\x05' { + return nil, ErrTorInvalidProxyResponse + } + if buf[1] != '\x00' { + return nil, ErrTorUnrecognizedAuthMethod + } + + buf = make([]byte, 7+len(host)) + buf[0] = 5 // protocol version + buf[1] = '\xF0' // Tor Resolve + buf[2] = 0 // reserved + buf[3] = 3 // Tor Resolve + buf[4] = byte(len(host)) + copy(buf[5:], host) + buf[5+len(host)] = 0 // Port 0 + + _, err = conn.Write(buf) + if err != nil { + return nil, err + } + + buf = make([]byte, 4) + _, err = conn.Read(buf) + if err != nil { + return nil, err + } + if buf[0] != 5 { + return nil, ErrTorInvalidProxyResponse + } + if buf[1] != 0 { + if int(buf[1]) > len(torStatusErrors) { + err = ErrTorInvalidProxyResponse + } else { + err := torStatusErrors[buf[1]] + if err == nil { + err = ErrTorInvalidProxyResponse + } + } + return nil, err + } + if buf[3] != 1 { + err := torStatusErrors[torGeneralError] + return nil, err + } + + buf = make([]byte, 4) + bytes, err := conn.Read(buf) + if err != nil { + return nil, err + } + if bytes != 4 { + return nil, ErrTorInvalidAddressResponse + } + + r := binary.BigEndian.Uint32(buf) + + addr := make([]net.IP, 1) + addr[0] = net.IPv4(byte(r>>24), byte(r>>16), byte(r>>8), byte(r)) + + return addr, nil +} + +// dnsDiscover looks up the list of peers resolved by DNS for all hosts in +// seeders. If proxy is not "" then it is used as a tor proxy for the +// resolution. +func dnsDiscover(seeder string) ([]net.IP, error) { + peers, err := btcdLookup(seeder) + if err != nil { + return nil, err + } + + return peers, nil +} diff --git a/vendor/github.com/btcsuite/btcd/doc.go b/vendor/github.com/btcsuite/btcd/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..d01ffe7af5a2b0c4817fa41ed28932880579a302 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/doc.go @@ -0,0 +1,116 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +btcd is a full-node bitcoin implementation written in Go. + +The default options are sane for most users. This means btcd will work 'out of +the box' for most users. However, there are also a wide variety of flags that +can be used to control it. + +The following section provides a usage overview which enumerates the flags. An +interesting point to note is that the long form of all of these options +(except -C) can be specified in a configuration file that is automatically +parsed when btcd starts up. By default, the configuration file is located at +~/.btcd/btcd.conf on POSIX-style operating systems and %LOCALAPPDATA%\btcd\btcd.conf +on Windows. The -C (--configfile) flag, as shown below, can be used to override +this location. + +Usage: + btcd [OPTIONS] + +Application Options: + -V, --version Display version information and exit + -C, --configfile= Path to configuration file + -b, --datadir= Directory to store data + --logdir= Directory to log output. + -a, --addpeer= Add a peer to connect with at startup + --connect= Connect only to the specified peers at startup + --nolisten Disable listening for incoming connections -- NOTE: + Listening is automatically disabled if the --connect + or --proxy options are used without also specifying + listen interfaces via --listen + --listen= Add an interface/port to listen for connections + (default all interfaces port: 8333, testnet: 18333) + --maxpeers= Max number of inbound and outbound peers (125) + --nobanning Disable banning of misbehaving peers + --banthreshold= Maximum allowed ban score before disconnecting and + banning misbehaving peers. + --banduration= How long to ban misbehaving peers. Valid time units + are {s, m, h}. Minimum 1 second (24h0m0s) + -u, --rpcuser= Username for RPC connections + -P, --rpcpass= Password for RPC connections + --rpclimituser= Username for limited RPC connections + --rpclimitpass= Password for limited RPC connections + --rpclisten= Add an interface/port to listen for RPC connections + (default port: 8334, testnet: 18334) + --rpccert= File containing the certificate file + --rpckey= File containing the certificate key + --rpcmaxclients= Max number of RPC clients for standard connections + (10) + --rpcmaxwebsockets= Max number of RPC websocket connections (25) + --norpc Disable built-in RPC server -- NOTE: The RPC server + is disabled by default if no rpcuser/rpcpass or + rpclimituser/rpclimitpass is specified + --notls Disable TLS for the RPC server -- NOTE: This is only + allowed if the RPC server is bound to localhost + --nodnsseed Disable DNS seeding for peers + --externalip= Add an ip to the list of local addresses we claim to + listen on to peers + --proxy= Connect via SOCKS5 proxy (eg. 127.0.0.1:9050) + --proxyuser= Username for proxy server + --proxypass= Password for proxy server + --onion= Connect to tor hidden services via SOCKS5 proxy + (eg. 127.0.0.1:9050) + --onionuser= Username for onion proxy server + --onionpass= Password for onion proxy server + --noonion Disable connecting to tor hidden services + --torisolation Enable Tor stream isolation by randomizing user + credentials for each connection. + --testnet Use the test network + --regtest Use the regression test network + --simnet Use the simulation test network + --nocheckpoints Disable built-in checkpoints. Don't do this unless + you know what you're doing. + --dbtype= Database backend to use for the Block Chain (ffldb) + --profile= Enable HTTP profiling on given port -- NOTE port + must be between 1024 and 65536 + --cpuprofile= Write CPU profile to the specified file + -d, --debuglevel= Logging level for all subsystems {trace, debug, + info, warn, error, critical} -- You may also specify + <subsystem>=<level>,<subsystem2>=<level>,... to set + the log level for individual subsystems -- Use show + to list available subsystems (info) + --upnp Use UPnP to map our listening port outside of NAT + --minrelaytxfee= The minimum transaction fee in BTC/kB to be + considered a non-zero fee. + --limitfreerelay= Limit relay of transactions with no transaction fee + to the given amount in thousands of bytes per + minute (15) + --norelaypriority Do not require free or low-fee transactions to have + high priority for relaying + --maxorphantx= Max number of orphan transactions to keep in memory + (1000) + --generate Generate (mine) bitcoins using the CPU + --miningaddr= Add the specified payment address to the list of + addresses to use for generated blocks -- At least + one address is required if the generate option is + set + --blockminsize= Mininum block size in bytes to be used when creating + a block + --blockmaxsize= Maximum block size in bytes to be used when creating + a block (750000) + --blockprioritysize= Size in bytes for high-priority/low-fee transactions + when creating a block (50000) + --getworkkey= DEPRECATED -- Use the --miningaddr option instead + --nopeerbloomfilters Disable bloom filtering support. + --sigcachemaxsize= The maximum number of entries in the signature + verification cache. + --blocksonly Do not accept transactions from remote peers. + +Help Options: + -h, --help Show this help message + +*/ +package main diff --git a/vendor/github.com/btcsuite/btcd/docs/README.md b/vendor/github.com/btcsuite/btcd/docs/README.md new file mode 100644 index 0000000000000000000000000000000000000000..63897867080d52f9e8cce81ddd7bb57d1ce3a4c2 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/docs/README.md @@ -0,0 +1,225 @@ +### Table of Contents +1. [About](#About) +2. [Getting Started](#GettingStarted) + 1. [Installation](#Installation) + 1. [Windows](#WindowsInstallation) + 2. [Linux/BSD/MacOSX/POSIX](#PosixInstallation) + 1. [Gentoo Linux](#GentooInstallation) + 2. [Configuration](#Configuration) + 3. [Controlling and Querying btcd via btcctl](#BtcctlConfig) + 4. [Mining](#Mining) +3. [Help](#Help) + 1. [Startup](#Startup) + 1. [Using bootstrap.dat](#BootstrapDat) + 2. [Network Configuration](#NetworkConfig) + 3. [Wallet](#Wallet) +4. [Contact](#Contact) + 1. [IRC](#ContactIRC) + 2. [Mailing Lists](#MailingLists) +5. [Developer Resources](#DeveloperResources) + 1. [Code Contribution Guidelines](#ContributionGuidelines) + 2. [JSON-RPC Reference](#JSONRPCReference) + 3. [The btcsuite Bitcoin-related Go Packages](#GoPackages) + +<a name="About" /> +### 1. About +btcd is a full node bitcoin implementation written in [Go](http://golang.org), +licensed under the [copyfree](http://www.copyfree.org) ISC License. + +This project is currently under active development and is in a Beta state. It +is extremely stable and has been in production use since October 2013. + +It currently properly downloads, validates, and serves the block chain using the +exact rules (including bugs) for block acceptance as the reference +implementation, [bitcoind](https://github.com/bitcoin/bitcoin). We have taken +great care to avoid btcd causing a fork to the block chain. It passes all of +the '[official](https://github.com/TheBlueMatt/test-scripts/)' block acceptance +tests. + +It also properly relays newly mined blocks, maintains a transaction pool, and +relays individual transactions that have not yet made it into a block. It +ensures all individual transactions admitted to the pool follow the rules +required into the block chain and also includes the vast majority of the more +strict checks which filter transactions based on miner requirements ("standard" +transactions). + +One key difference between btcd and Bitcoin Core is that btcd does *NOT* include +wallet functionality and this was a very intentional design decision. See the +blog entry [here](https://blog.conformal.com/btcd-not-your-moms-bitcoin-daemon) +for more details. This means you can't actually make or receive payments +directly with btcd. That functionality is provided by the +[btcwallet](https://github.com/btcsuite/btcwallet) and +[Paymetheus](https://github.com/btcsuite/Paymetheus) (Windows-only) projects +which are both under active development. + +<a name="GettingStarted" /> +### 2. Getting Started + +<a name="Installation" /> +**2.1 Installation**<br /> + +The first step is to install btcd. See one of the following sections for +details on how to install on the supported operating systems. + +<a name="WindowsInstallation" /> +**2.1.1 Windows Installation**<br /> + +* Install the MSI available at: https://github.com/btcsuite/btcd/releases +* Launch btcd from the Start Menu + +<a name="PosixInstallation" /> +**2.1.2 Linux/BSD/MacOSX/POSIX Installation**<br /> + +* Install Go according to the installation instructions here: http://golang.org/doc/install +* Run the following command to ensure your Go version is at least version 1.2: `$ go version` +* Run the following command to obtain btcd, its dependencies, and install it: `$ go get github.com/btcsuite/btcd/...`<br /> + * To upgrade, run the following command: `$ go get -u github.com/btcsuite/btcd/...` +* Run btcd: `$ btcd` + +<a name="GentooInstallation" /> +**2.1.2.1 Gentoo Linux Installation**<br /> + +* Install Layman and enable the Bitcoin overlay. + * https://gitlab.com/bitcoin/gentoo +* Copy or symlink `/var/lib/layman/bitcoin/Documentation/package.keywords/btcd-live` to `/etc/portage/package.keywords/` +* Install btcd: `$ emerge net-p2p/btcd` + +<a name="Configuration" /> +**2.2 Configuration**<br /> + +btcd has a number of [configuration](http://godoc.org/github.com/btcsuite/btcd) +options, which can be viewed by running: `$ btcd --help`. + +<a name="BtcctlConfig" /> +**2.3 Controlling and Querying btcd via btcctl**<br /> + +btcctl is a command line utility that can be used to both control and query btcd +via [RPC](http://www.wikipedia.org/wiki/Remote_procedure_call). btcd does +**not** enable its RPC server by default; You must configure at minimum both an +RPC username and password or both an RPC limited username and password: + +* btcd.conf configuration file +``` +[Application Options] +rpcuser=myuser +rpcpass=SomeDecentp4ssw0rd +rpclimituser=mylimituser +rpclimitpass=Limitedp4ssw0rd +``` +* btcctl.conf configuration file +``` +[Application Options] +rpcuser=myuser +rpcpass=SomeDecentp4ssw0rd +``` +OR +``` +[Application Options] +rpclimituser=mylimituser +rpclimitpass=Limitedp4ssw0rd +``` +For a list of available options, run: `$ btcctl --help` + +<a name="Mining" /> +**2.4 Mining**<br /> +btcd supports both the `getwork` and `getblocktemplate` RPCs although the +`getwork` RPC is deprecated and will likely be removed in a future release. +The limited user cannot access these RPCs.<br /> + +**1. Add the payment addresses with the `miningaddr` option.**<br /> + +``` +[Application Options] +rpcuser=myuser +rpcpass=SomeDecentp4ssw0rd +miningaddr=12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX +miningaddr=1M83ju3EChKYyysmM2FXtLNftbacagd8FR +``` + +**2. Add btcd's RPC TLS certificate to system Certificate Authority list.**<br /> + +`cgminer` uses [curl](http://curl.haxx.se/) to fetch data from the RPC server. +Since curl validates the certificate by default, we must install the `btcd` RPC +certificate into the default system Certificate Authority list. + +**Ubuntu**<br /> + +1. Copy rpc.cert to /usr/share/ca-certificates: `# cp /home/user/.btcd/rpc.cert /usr/share/ca-certificates/btcd.crt`<br /> +2. Add btcd.crt to /etc/ca-certificates.conf: `# echo btcd.crt >> /etc/ca-certificates.conf`<br /> +3. Update the CA certificate list: `# update-ca-certificates`<br /> + +**3. Set your mining software url to use https.**<br /> + +`$ cgminer -o https://127.0.0.1:8334 -u rpcuser -p rpcpassword` + +<a name="Help" /> +### 3. Help + +<a name="Startup" /> +**3.1 Startup**<br /> + +Typically btcd will run and start downloading the block chain with no extra +configuration necessary, however, there is an optional method to use a +`bootstrap.dat` file that may speed up the initial block chain download process. + +<a name="BootstrapDat" /> +**3.1.1 bootstrap.dat**<br /> +* [Using bootstrap.dat](https://github.com/btcsuite/btcd/tree/master/docs/using_bootstrap_dat.md) + +<a name="NetworkConfig" /> +**3.1.2 Network Configuration**<br /> +* [What Ports Are Used by Default?](https://github.com/btcsuite/btcd/tree/master/docs/default_ports.md) +* [How To Listen on Specific Interfaces](https://github.com/btcsuite/btcd/tree/master/docs/configure_peer_server_listen_interfaces.md) +* [How To Configure RPC Server to Listen on Specific Interfaces](https://github.com/btcsuite/btcd/tree/master/docs/configure_rpc_server_listen_interfaces.md) +* [Configuring btcd with Tor](https://github.com/btcsuite/btcd/tree/master/docs/configuring_tor.md) + +<a name="Wallet" /> +**3.1 Wallet**<br /> + +btcd was intentionally developed without an integrated wallet for security +reasons. Please see [btcwallet](https://github.com/btcsuite/btcwallet) for more +information. + +<a name="Contact" /> +### 4. Contact + +<a name="ContactIRC" /> +**4.1 IRC**<br /> +* [irc.freenode.net](irc://irc.freenode.net), channel #btcd + +<a name="MailingLists" /> +**4.2 Mailing Lists**<br /> +* <a href="mailto:btcd+subscribe@opensource.conformal.com">btcd</a>: discussion + of btcd and its packages. +* <a href="mailto:btcd-commits+subscribe@opensource.conformal.com">btcd-commits</a>: + readonly mail-out of source code changes. + +<a name="DeveloperResources" /> +### 5. Developer Resources + +<a name="ContributionGuidelines" /> +* [Code Contribution Guidelines](https://github.com/btcsuite/btcd/tree/master/docs/code_contribution_guidelines.md) +<a name="JSONRPCReference" /> +* [JSON-RPC Reference](https://github.com/btcsuite/btcd/tree/master/docs/json_rpc_api.md) + * [RPC Examples](https://github.com/btcsuite/btcd/tree/master/docs/json_rpc_api.md#ExampleCode) +<a name="GoPackages" /> +* The btcsuite Bitcoin-related Go Packages: + * [btcrpcclient](https://github.com/btcsuite/btcrpcclient) - Implements a + robust and easy to use Websocket-enabled Bitcoin JSON-RPC client + * [btcjson](https://github.com/btcsuite/btcjson) - Provides an extensive API + for the underlying JSON-RPC command and return values + * [wire](https://github.com/btcsuite/btcd/tree/master/wire) - Implements the + Bitcoin wire protocol + * [peer](https://github.com/btcsuite/btcd/tree/master/peer) - + Provides a common base for creating and managing Bitcoin network peers. + * [blockchain](https://github.com/btcsuite/btcd/tree/master/blockchain) - + Implements Bitcoin block handling and chain selection rules + * [txscript](https://github.com/btcsuite/btcd/tree/master/txscript) - + Implements the Bitcoin transaction scripting language + * [btcec](https://github.com/btcsuite/btcd/tree/master/btcec) - Implements + support for the elliptic curve cryptographic functions needed for the + Bitcoin scripts + * [database](https://github.com/btcsuite/btcd/tree/master/database) - + Provides a database interface for the Bitcoin block chain + * [btcutil](https://github.com/btcsuite/btcutil) - Provides Bitcoin-specific + convenience functions and types diff --git a/vendor/github.com/btcsuite/btcd/docs/code_contribution_guidelines.md b/vendor/github.com/btcsuite/btcd/docs/code_contribution_guidelines.md new file mode 100644 index 0000000000000000000000000000000000000000..737e390f62238abed3aad0d7e637987c7c3f181c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/docs/code_contribution_guidelines.md @@ -0,0 +1,334 @@ +### Table of Contents +1. [Overview](#Overview)<br /> +2. [Minimum Recommended Skillset](#MinSkillset)<br /> +3. [Required Reading](#ReqReading)<br /> +4. [Development Practices](#DevelopmentPractices)<br /> +4.1. [Share Early, Share Often](#ShareEarly)<br /> +4.2. [Testing](#Testing)<br /> +4.3. [Code Documentation and Commenting](#CodeDocumentation)<br /> +4.4. [Model Git Commit Messages](#ModelGitCommitMessages)<br /> +5. [Code Approval Process](#CodeApproval)<br /> +5.1 [Code Review](#CodeReview)<br /> +5.2 [Rework Code (if needed)](#CodeRework)<br /> +5.3 [Acceptance](#CodeAcceptance)<br /> +6. [Contribution Standards](#Standards)<br /> +6.1. [Contribution Checklist](#Checklist)<br /> +6.2. [Licensing of Contributions](#Licensing)<br /> + +<a name="Overview" /> +### 1. Overview + +Developing cryptocurrencies is an exciting endeavor that touches a wide variety +of areas such as wire protocols, peer-to-peer networking, databases, +cryptography, language interpretation (transaction scripts), RPC, and +websockets. They also represent a radical shift to the current fiscal system +and as a result provide an opportunity to help reshape the entire financial +system. There are few projects that offer this level of diversity and impact +all in one code base. + +However, as exciting as it is, one must keep in mind that cryptocurrencies +represent real money and introducing bugs and security vulnerabilities can have +far more dire consequences than in typical projects where having a small bug is +minimal by comparison. In the world of cryptocurrencies, even the smallest bug +in the wrong area can cost people a significant amount of money. For this +reason, the btcd suite has a formalized and rigorous development process which +is outlined on this page. + +We highly encourage code contributions, however it is imperative that you adhere +to the guidelines established on this page. + +<a name="MinSkillset" /> +### 2. Minimum Recommended Skillset + +The following list is a set of core competencies that we recommend you possess +before you really start attempting to contribute code to the project. These are +not hard requirements as we will gladly accept code contributions as long as +they follow the guidelines set forth on this page. That said, if you don't have +the following basic qualifications you will likely find it quite difficult to +contribute. + +- A reasonable understanding of bitcoin at a high level (see the + [Required Reading](#ReqReading) section for the original white paper) +- Experience in some type of C-like language +- An understanding of data structures and their performance implications +- Familiarity with unit testing +- Debugging experience +- Ability to understand not only the area you are making a change in, but also + the code your change relies on, and the code which relies on your changed code + +Building on top of those core competencies, the recommended skill set largely +depends on the specific areas you are looking to contribute to. For example, +if you wish to contribute to the cryptography code, you should have a good +understanding of the various aspects involved with cryptography such as the +security and performance implications. + +<a name="ReqReading" /> +### 3. Required Reading + +- [Effective Go](http://golang.org/doc/effective_go.html) - The entire btcd + suite follows the guidelines in this document. For your code to be accepted, + it must follow the guidelines therein. +- [Original Satoshi Whitepaper](http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&ved=0CCkQFjAA&url=http%3A%2F%2Fbitcoin.org%2Fbitcoin.pdf&ei=os3VUuH8G4SlsASV74GoAg&usg=AFQjCNEipPLigou_1MfB7DQjXCNdlylrBg&sig2=FaHDuT5z36GMWDEnybDJLg&bvm=bv.59378465,d.b2I) - This is the white paper that started it all. Having a solid + foundation to build on will make the code much more comprehensible. + +<a name="DevelopmentPractices" /> +### 4. Development Practices + +Developers are expected to work in their own trees and submit pull requests when +they feel their feature or bug fix is ready for integration into the master +branch. + +<a name="ShareEarly" /> +### 4.1 Share Early, Share Often + +We firmly believe in the share early, share often approach. The basic premise +of the approach is to announce your plans **before** you start work, and once +you have started working, craft your changes into a stream of small and easily +reviewable commits. + +This approach has several benefits: + +- Announcing your plans to work on a feature **before** you begin work avoids + duplicate work +- It permits discussions which can help you achieve your goals in a way that is + consistent with the existing architecture +- It minimizes the chances of you spending time and energy on a change that + might not fit with the consensus of the community or existing architecture and + potentially be rejected as a result +- Incremental development helps ensure you are on the right track with regards + to the rest of the community +- The quicker your changes are merged to master, the less time you will need to + spend rebasing and otherwise trying to keep up with the main code base + +<a name="Testing" /> +### 4.2 Testing + +One of the major design goals of all core btcd packages is to aim for complete +test coverage. This is financial software so bugs and regressions can cost +people real money. For this reason every effort must be taken to ensure the +code is as accurate and bug-free as possible. Thorough testing is a good way to +help achieve that goal. + +Unless a new feature you submit is completely trivial, it will probably be +rejected unless it is also accompanied by adequate test coverage for both +positive and negative conditions. That is to say, the tests must ensure your +code works correctly when it is fed correct data as well as incorrect data +(error paths). + +Go provides an excellent test framework that makes writing test code and +checking coverage statistics straight forward. For more information about the +test coverage tools, see the [golang cover blog post](http://blog.golang.org/cover). + +A quick summary of test practices follows: +- All new code should be accompanied by tests that ensure the code behaves + correctly when given expected values, and, perhaps even more importantly, that + it handles errors gracefully +- When you fix a bug, it should be accompanied by tests which exercise the bug + to both prove it has been resolved and to prevent future regressions + +<a name="CodeDocumentation" /> +### 4.3 Code Documentation and Commenting + +- At a minimum every function must be commented with its intended purpose and + any assumptions that it makes + - Function comments must always begin with the name of the function per + [Effective Go](http://golang.org/doc/effective_go.html) + - Function comments should be complete sentences since they allow a wide + variety of automated presentations such as [godoc.org](https://godoc.org) + - The general rule of thumb is to look at it as if you were completely + unfamiliar with the code and ask yourself, would this give me enough + information to understand what this function does and how I'd probably want + to use it? +- Exported functions should also include detailed information the caller of the + function will likely need to know and/or understand:<br /><br /> +**WRONG** +```Go +// convert a compact uint32 to big.Int +func CompactToBig(compact uint32) *big.Int { +``` +**RIGHT** +```Go +// CompactToBig converts a compact representation of a whole number N to a +// big integer. The representation is similar to IEEE754 floating point +// numbers. +// +// Like IEEE754 floating point, there are three basic components: the sign, +// the exponent, and the mantissa. They are broken out as follows: +// +// * the most significant 8 bits represent the unsigned base 256 exponent +// * bit 23 (the 24th bit) represents the sign bit +// * the least significant 23 bits represent the mantissa +// +// ------------------------------------------------- +// | Exponent | Sign | Mantissa | +// ------------------------------------------------- +// | 8 bits [31-24] | 1 bit [23] | 23 bits [22-00] | +// ------------------------------------------------- +// +// The formula to calculate N is: +// N = (-1^sign) * mantissa * 256^(exponent-3) +// +// This compact form is only used in bitcoin to encode unsigned 256-bit numbers +// which represent difficulty targets, thus there really is not a need for a +// sign bit, but it is implemented here to stay consistent with bitcoind. +func CompactToBig(compact uint32) *big.Int { +``` +- Comments in the body of the code are highly encouraged, but they should + explain the intention of the code as opposed to just calling out the + obvious<br /><br /> +**WRONG** +```Go +// return err if amt is less than 5460 +if amt < 5460 { + return err +} +``` +**RIGHT** +```Go +// Treat transactions with amounts less than the amount which is considered dust +// as non-standard. +if amt < 5460 { + return err +} +``` +**NOTE:** The above should really use a constant as opposed to a magic number, +but it was left as a magic number to show how much of a difference a good +comment can make. + +<a name="ModelGitCommitMessages" /> +### 4.4 Code Documentation and Commenting + +This project prefers to keep a clean commit history with well-formed commit +messages. This section illustrates a model commit message and provides a bit +of background for it. This content was originally created by Tim Pope and made +available on his website, however that website is no longer active, so it is +being provided here. + +Here’s a model Git commit message: + +``` +Short (50 chars or less) summary of changes + +More detailed explanatory text, if necessary. Wrap it to about 72 +characters or so. In some contexts, the first line is treated as the +subject of an email and the rest of the text as the body. The blank +line separating the summary from the body is critical (unless you omit +the body entirely); tools like rebase can get confused if you run the +two together. + +Write your commit message in the present tense: "Fix bug" and not "Fixed +bug." This convention matches up with commit messages generated by +commands like git merge and git revert. + +Further paragraphs come after blank lines. + +- Bullet points are okay, too +- Typically a hyphen or asterisk is used for the bullet, preceded by a + single space, with blank lines in between, but conventions vary here +- Use a hanging indent +``` + +Here are some of the reasons why wrapping your commit messages to 72 columns is +a good thing. + +- git log doesn’t do any special special wrapping of the commit messages. With + the default pager of less -S, this means your paragraphs flow far off the edge + of the screen, making them difficult to read. On an 80 column terminal, if we + subtract 4 columns for the indent on the left and 4 more for symmetry on the + right, we’re left with 72 columns. +- git format-patch --stdout converts a series of commits to a series of emails, + using the messages for the message body. Good email netiquette dictates we + wrap our plain text emails such that there’s room for a few levels of nested + reply indicators without overflow in an 80 column terminal. + +<a name="CodeApproval" /> +### 5. Code Approval Process + +This section describes the code approval process that is used for code +contributions. This is how to get your changes into btcd. + +<a name="CodeReview" /> +### 5.1 Code Review + +All code which is submitted will need to be reviewed before inclusion into the +master branch. This process is performed by the project maintainers and usually +other committers who are interested in the area you are working in as well. + +##### Code Review Timeframe + +The timeframe for a code review will vary greatly depending on factors such as +the number of other pull requests which need to be reviewed, the size and +complexity of the contribution, how well you followed the guidelines presented +on this page, and how easy it is for the reviewers to digest your commits. For +example, if you make one monolithic commit that makes sweeping changes to things +in multiple subsystems, it will obviously take much longer to review. You will +also likely be asked to split the commit into several smaller, and hence more +manageable, commits. + +Keeping the above in mind, most small changes will be reviewed within a few +days, while large or far reaching changes may take weeks. This is a good reason +to stick with the [Share Early, Share Often](#ShareOften) development practice +outlined above. + +##### What is the review looking for? + +The review is mainly ensuring the code follows the [Development Practices](#DevelopmentPractices) +and [Code Contribution Standards](#Standards). However, there are a few other +checks which are generally performed as follows: + +- The code is stable and has no stability or security concerns +- The code is properly using existing APIs and generally fits well into the + overall architecture +- The change is not something which is deemed inappropriate by community + consensus + +<a name="CodeRework" /> +### 5.2 Rework Code (if needed) + +After the code review, the change will be accepted immediately if no issues are +found. If there are any concerns or questions, you will be provided with +feedback along with the next steps needed to get your contribution merged with +master. In certain cases the code reviewer(s) or interested committers may help +you rework the code, but generally you will simply be given feedback for you to +make the necessary changes. + +This process will continue until the code is finally accepted. + +<a name="CodeAcceptance" /> +### 5.3 Acceptance + +Once your code is accepted, it will be integrated with the master branch. +Typically it will be rebased and fast-forward merged to master as we prefer to +keep a clean commit history over a tangled weave of merge commits. However, +regardless of the specific merge method used, the code will be integrated with +the master branch and the pull request will be closed. + +Rejoice as you will now be listed as a [contributor](https://github.com/btcsuite/btcd/graphs/contributors)! + +<a name="Standards" /> +### 6. Contribution Standards + +<a name="Checklist" /> +### 6.1. Contribution Checklist + +- [ ] All changes are Go version 1.3 compliant +- [ ] The code being submitted is commented according to the + [Code Documentation and Commenting](#CodeDocumentation) section +- [ ] For new code: Code is accompanied by tests which exercise both + the positive and negative (error paths) conditions (if applicable) +- [ ] For bug fixes: Code is accompanied by new tests which trigger + the bug being fixed to prevent regressions +- [ ] Any new logging statements use an appropriate subsystem and + logging level +- [ ] Code has been formatted with `go fmt` +- [ ] Running `go test` does not fail any tests +- [ ] Running `go vet` does not report any issues +- [ ] Running [golint](https://github.com/golang/lint) does not + report any **new** issues that did not already exist + +<a name="Licensing" /> +### 6.2. Licensing of Contributions +**** +All contributions must be licensed with the +[ISC license](https://github.com/btcsuite/btcd/blob/master/LICENSE). This is +the same license as all of the code in the btcd suite. diff --git a/vendor/github.com/btcsuite/btcd/docs/configure_peer_server_listen_interfaces.md b/vendor/github.com/btcsuite/btcd/docs/configure_peer_server_listen_interfaces.md new file mode 100644 index 0000000000000000000000000000000000000000..26f5ec72a16c8a4f58df62cc9726201f0f567fa6 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/docs/configure_peer_server_listen_interfaces.md @@ -0,0 +1,35 @@ +btcd allows you to bind to specific interfaces which enables you to setup +configurations with varying levels of complexity. The listen parameter can be +specified on the command line as shown below with the -- prefix or in the +configuration file without the -- prefix (as can all long command line options). +The configuration file takes one entry per line. + +**NOTE:** The listen flag can be specified multiple times to listen on multiple +interfaces as a couple of the examples below illustrate. + +Command Line Examples: + +|Flags|Comment| +|----------|------------| +|--listen=|all interfaces on default port which is changed by `--testnet` and `--regtest` (**default**)| +|--listen=0.0.0.0|all IPv4 interfaces on default port which is changed by `--testnet` and `--regtest`| +|--listen=::|all IPv6 interfaces on default port which is changed by `--testnet` and `--regtest`| +|--listen=:8333|all interfaces on port 8333| +|--listen=0.0.0.0:8333|all IPv4 interfaces on port 8333| +|--listen=[::]:8333|all IPv6 interfaces on port 8333| +|--listen=127.0.0.1:8333|only IPv4 localhost on port 8333| +|--listen=[::1]:8333|only IPv6 localhost on port 8333| +|--listen=:8336|all interfaces on non-standard port 8336| +|--listen=0.0.0.0:8336|all IPv4 interfaces on non-standard port 8336| +|--listen=[::]:8336|all IPv6 interfaces on non-standard port 8336| +|--listen=127.0.0.1:8337 --listen=[::1]:8333|IPv4 localhost on port 8337 and IPv6 localhost on port 8333| +|--listen=:8333 --listen=:8337|all interfaces on ports 8333 and 8337| + +The following config file would configure btcd to only listen on localhost for both IPv4 and IPv6: + +```text +[Application Options] + +listen=127.0.0.1:8333 +listen=[::1]:8333 +``` diff --git a/vendor/github.com/btcsuite/btcd/docs/configure_rpc_server_listen_interfaces.md b/vendor/github.com/btcsuite/btcd/docs/configure_rpc_server_listen_interfaces.md new file mode 100644 index 0000000000000000000000000000000000000000..3115d6a16fb8bd480ac6198622612dea03dcdf71 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/docs/configure_rpc_server_listen_interfaces.md @@ -0,0 +1,47 @@ +btcd allows you to bind the RPC server to specific interfaces which enables you +to setup configurations with varying levels of complexity. The `rpclisten` +parameter can be specified on the command line as shown below with the -- prefix +or in the configuration file without the -- prefix (as can all long command line +options). The configuration file takes one entry per line. + +A few things to note regarding the RPC server: +* The RPC server will **not** be enabled unless the `rpcuser` and `rpcpass` + options are specified. +* When the `rpcuser` and `rpcpass` and/or `rpclimituser` and `rpclimitpass` + options are specified, the RPC server will only listen on localhost IPv4 and + IPv6 interfaces by default. You will need to override the RPC listen + interfaces to include external interfaces if you want to connect from a remote + machine. +* The RPC server has TLS enabled by default, even for localhost. You may use + the `--notls` option to disable it, but only when all listeners are on + localhost interfaces. +* The `--rpclisten` flag can be specified multiple times to listen on multiple + interfaces as a couple of the examples below illustrate. +* The RPC server is disabled by default when using the `--regtest` and + `--simnet` networks. You can override this by specifying listen interfaces. + +Command Line Examples: + +|Flags|Comment| +|----------|------------| +|--rpclisten=|all interfaces on default port which is changed by `--testnet`| +|--rpclisten=0.0.0.0|all IPv4 interfaces on default port which is changed by `--testnet`| +|--rpclisten=::|all IPv6 interfaces on default port which is changed by `--testnet`| +|--rpclisten=:8334|all interfaces on port 8334| +|--rpclisten=0.0.0.0:8334|all IPv4 interfaces on port 8334| +|--rpclisten=[::]:8334|all IPv6 interfaces on port 8334| +|--rpclisten=127.0.0.1:8334|only IPv4 localhost on port 8334| +|--rpclisten=[::1]:8334|only IPv6 localhost on port 8334| +|--rpclisten=:8336|all interfaces on non-standard port 8336| +|--rpclisten=0.0.0.0:8336|all IPv4 interfaces on non-standard port 8336| +|--rpclisten=[::]:8336|all IPv6 interfaces on non-standard port 8336| +|--rpclisten=127.0.0.1:8337 --listen=[::1]:8334|IPv4 localhost on port 8337 and IPv6 localhost on port 8334| +|--rpclisten=:8334 --listen=:8337|all interfaces on ports 8334 and 8337| + +The following config file would configure the btcd RPC server to listen to all interfaces on the default port, including external interfaces, for both IPv4 and IPv6: + +```text +[Application Options] + +rpclisten= +``` diff --git a/vendor/github.com/btcsuite/btcd/docs/configuring_tor.md b/vendor/github.com/btcsuite/btcd/docs/configuring_tor.md new file mode 100644 index 0000000000000000000000000000000000000000..d7afb8721907e656b2dfdf085a0be49528942608 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/docs/configuring_tor.md @@ -0,0 +1,190 @@ +### Table of Contents +1. [Overview](#Overview)<br /> +2. [Client-Only](#Client)<br /> +2.1 [Description](#ClientDescription)<br /> +2.2 [Command Line Example](#ClientCLIExample)<br /> +2.3 [Config File Example](#ClientConfigFileExample)<br /> +3. [Client-Server via Tor Hidden Service](#HiddenService)<br /> +3.1 [Description](#HiddenServiceDescription)<br /> +3.2 [Command Line Example](#HiddenServiceCLIExample)<br /> +3.3 [Config File Example](#HiddenServiceConfigFileExample)<br /> +4. [Bridge Mode (Not Anonymous)](#Bridge)<br /> +4.1 [Description](#BridgeDescription)<br /> +4.2 [Command Line Example](#BridgeCLIExample)<br /> +4.3 [Config File Example](#BridgeConfigFileExample)<br /> +5. [Tor Stream Isolation](#TorStreamIsolation)<br /> +5.1 [Description](#TorStreamIsolationDescription)<br /> +5.2 [Command Line Example](#TorStreamIsolationCLIExample)<br /> +5.3 [Config File Example](#TorStreamIsolationFileExample)<br /> + +<a name="Overview" /> +### 1. Overview + +btcd provides full support for anonymous networking via the +[Tor Project](https://www.torproject.org/), including [client-only](#Client) +and [hidden service](#HiddenService) configurations along with +[stream isolation](#TorStreamIsolation). In addition, btcd supports a hybrid, +[bridge mode](#Bridge) which is not anonymous, but allows it to operate as a +bridge between regular nodes and hidden service nodes without routing the +regular connections through Tor. + +While it is easier to only run as a client, it is more beneficial to the Bitcoin +network to run as both a client and a server so others may connect to you to as +you are connecting to them. We recommend you take the time to setup a Tor +hidden service for this reason. + +<a name="Client" /> +### 2. Client-Only + +<a name="ClientDescription" /> +**2.1 Description**<br /> + +Configuring btcd as a Tor client is straightforward. The first step is +obviously to install Tor and ensure it is working. Once that is done, all that +typically needs to be done is to specify the `--proxy` flag via the btcd command +line or in the btcd configuration file. Typically the Tor proxy address will be +127.0.0.1:9050 (if using standalone Tor) or 127.0.0.1:9150 (if using the Tor +Browser Bundle). If you have Tor configured to require a username and password, +you may specify them with the `--proxyuser` and `--proxypass` flags. + +By default, btcd assumes the proxy specified with `--proxy` is a Tor proxy and +hence will send all traffic, including DNS resolution requests, via the +specified proxy. + +NOTE: Specifying the `--proxy` flag disables listening by default since you will +not be reachable for inbound connections unless you also configure a Tor +[hidden service](#HiddenService). + +<a name="ClientCLIExample" /> +**2.2 Command Line Example**<br /> + +```bash +$ ./btcd --proxy=127.0.0.1:9050 +``` + +<a name="ClientConfigFileExample" /> +**2.3 Config File Example**<br /> + +```text +[Application Options] + +proxy=127.0.0.1:9050 +``` + +<a name="HiddenService" /> +### 3. Client-Server via Tor Hidden Service + +<a name="HiddenServiceDescription" /> +**3.1 Description**<br /> + +The first step is to configure Tor to provide a hidden service. Documentation +for this can be found on the Tor project website +[here](https://www.torproject.org/docs/tor-hidden-service.html.en). However, +there is no need to install a web server locally as the linked instructions +discuss since btcd will act as the server. + +In short, the instructions linked above entail modifying your `torrc` file to +add something similar to the following, restarting Tor, and opening the +`hostname` file in the `HiddenServiceDir` to obtain your hidden service .onion +address. + +```text +HiddenServiceDir /var/tor/btcd +HiddenServicePort 8333 127.0.0.1:8333 +``` + +Once Tor is configured to provide the hidden service and you have obtained your +generated .onion address, configuring btcd as a Tor hidden service requires +three flags: +* `--proxy` to identify the Tor (SOCKS 5) proxy to use for outgoing traffic. + This is typically 127.0.0.1:9050. +* `--listen` to enable listening for inbound connections since `--proxy` + disables listening by default +* `--externalip` to set the .onion address that is advertised to other peers + +<a name="HiddenServiceCLIExample" /> +**3.2 Command Line Example**<br /> + +```bash +$ ./btcd --proxy=127.0.0.1:9050 --listen=127.0.0.1 --externalip=fooanon.onion +``` + +<a name="HiddenServiceConfigFileExample" /> +**3.3 Config File Example**<br /> + +```text +[Application Options] + +proxy=127.0.0.1:9050 +listen=127.0.0.1 +externalip=fooanon.onion +``` + +<a name="Bridge" /> +### 4. Bridge Mode (Not Anonymous) + +<a name="BridgeDescription" /> +**4.1 Description**<br /> + +btcd provides support for operating as a bridge between regular nodes and hidden +service nodes. In particular this means only traffic which is directed to or +from a .onion address is sent through Tor while other traffic is sent normally. +_As a result, this mode is **NOT** anonymous._ + +This mode works by specifying an onion-specific proxy, which is pointed at Tor, +by using the `--onion` flag via the btcd command line or in the btcd +configuration file. If you have Tor configured to require a username and +password, you may specify them with the `--onionuser` and `--onionpass` flags. + +NOTE: This mode will also work in conjunction with a hidden service which means +you could accept inbound connections both via the normal network and to your +hidden service through the Tor network. To enable your hidden service in bridge +mode, you only need to specify your hidden service's .onion address via the +`--externalip` flag since traffic to and from .onion addresses are already +routed via Tor due to the `--onion` flag. + +<a name="BridgeCLIExample" /> +**4.2 Command Line Example**<br /> + +```bash +$ ./btcd --onion=127.0.0.1:9050 --externalip=fooanon.onion +``` + +<a name="BridgeConfigFileExample" /> +**4.3 Config File Example**<br /> + +```text +[Application Options] + +onion=127.0.0.1:9050 +externalip=fooanon.onion +``` + +<a name="TorStreamIsolation" /> +### 5. Tor Stream Isolation + +<a name="TorStreamIsolationDescription" /> +**5.1 Description**<br /> + +Tor stream isolation forces Tor to build a new circuit for each connection +making it harder to correlate connections. + +btcd provides support for Tor stream isolation by using the `--torisolation` +flag. This option requires --proxy or --onionproxy to be set. + +<a name="TorStreamIsolationCLIExample" /> +**5.2 Command Line Example**<br /> + +```bash +$ ./btcd --proxy=127.0.0.1:9050 --torisolation +``` + +<a name="TorStreamIsolationFileExample" /> +**5.3 Config File Example**<br /> + +```text +[Application Options] + +proxy=127.0.0.1:9050 +torisolation=1 +``` diff --git a/vendor/github.com/btcsuite/btcd/docs/default_ports.md b/vendor/github.com/btcsuite/btcd/docs/default_ports.md new file mode 100644 index 0000000000000000000000000000000000000000..14e4eea2a70824ee59d313fb5e86a22f7d81b46b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/docs/default_ports.md @@ -0,0 +1,15 @@ +While btcd is highly configurable when it comes to the network configuration, +the following is intended to be a quick reference for the default ports used so +port forwarding can be configured as required. + +btcd provides a `--upnp` flag which can be used to automatically map the bitcoin +peer-to-peer listening port if your router supports UPnP. If your router does +not support UPnP, or you don't wish to use it, please note that only the bitcoin +peer-to-peer port should be forwarded unless you specifically want to allow RPC +access to your btcd from external sources such as in more advanced network +configurations. + +|Name|Port| +|----|----| +|Default Bitcoin peer-to-peer port|TCP 8333| +|Default RPC port|TCP 8334| diff --git a/vendor/github.com/btcsuite/btcd/docs/json_rpc_api.md b/vendor/github.com/btcsuite/btcd/docs/json_rpc_api.md new file mode 100644 index 0000000000000000000000000000000000000000..28928f0463b582fc03227c0ccbc8602ef4298dba --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/docs/json_rpc_api.md @@ -0,0 +1,1266 @@ +### Table of Contents +1. [Overview](#Overview)<br /> +2. [HTTP POST Versus Websockets](#HttpPostVsWebsockets)<br /> +3. [Authentication](#Authentication)<br /> +3.1. [Overview](#AuthenticationOverview)<br /> +3.2. [HTTP Basic Access Authentication](#HTTPAuth)<br /> +3.3. [JSON-RPC Authenticate Command (Websocket-specific)](#JSONAuth)<br /> +4. [Command-line Utility](#CLIUtil)<br /> +5. [Standard Methods](#Methods)<br /> +5.1. [Method Overview](#MethodOverview)<br /> +5.2. [Method Details](#MethodDetails)<br /> +6. [Extension Methods](#ExtensionMethods)<br /> +6.1. [Method Overview](#ExtMethodOverview)<br /> +6.2. [Method Details](#ExtMethodDetails)<br /> +7. [Websocket Extension Methods (Websocket-specific)](#WSExtMethods)<br /> +7.1. [Method Overview](#WSExtMethodOverview)<br /> +7.2. [Method Details](#WSExtMethodDetails)<br /> +8. [Notifications (Websocket-specific)](#Notifications)<br /> +8.1. [Notification Overview](#NotificationOverview)<br /> +8.2. [Notification Details](#NotificationDetails)<br /> +9. [Example Code](#ExampleCode)<br /> +9.1. [Go](#ExampleGoApp)<br /> +9.2. [node.js](#ExampleNodeJsCode)<br /> + +<a name="Overview" /> +### 1. Overview + +btcd provides a [JSON-RPC](http://json-rpc.org/wiki/specification) API that is +fully compatible with the original bitcoind/bitcoin-qt. There are a few key +differences between btcd and bitcoind as far as how RPCs are serviced: +* Unlike bitcoind that has the wallet and chain intermingled in the same process + which leads to several issues, btcd intentionally splits the wallet and chain + services into independent processes. See the blog post + [here](https://blog.conformal.com/btcd-not-your-moms-bitcoin-daemon/) for + further details on why they were separated. This means that if you are + talking directly to btcd, only chain-related RPCs are available. However both + chain-related and wallet-related RPCs are available via + [btcwallet](https://github.com/btcsuite/btcwallet). +* btcd is secure by default which means that the RPC connection is TLS-enabled + by default +* btcd provides access to the API through both + [HTTP POST](http://en.wikipedia.org/wiki/POST_%28HTTP%29) requests and + [Websockets](http://en.wikipedia.org/wiki/WebSocket) + +Websockets are the preferred transport for btcd RPC and are used by applications +such as [btcwallet](https://github.com/btcsuite/btcwallet) for inter-process +communication with btcd. The websocket connection endpoint for btcd is +`wss://your_ip_or_domain:8334/ws`. + +In addition to the [standard API](#Methods), an [extension API](#WSExtMethods) +has been developed that is exclusive to clients using Websockets. In its current +state, this API attempts to cover features found missing in the standard API +during the development of btcwallet. + +While the [standard API](#Methods) is stable, the +[Websocket extension API](#WSExtMethods) should be considered a work in +progress, incomplete, and susceptible to changes (both additions and removals). + +The original bitcoind/bitcoin-qt JSON-RPC API documentation is available at [https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_Calls_list](https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_Calls_list) + +<a name="HttpPostVsWebsockets" /> +### 2. HTTP POST Versus Websockets + +The btcd RPC server supports both [HTTP POST](http://en.wikipedia.org/wiki/POST_%28HTTP%29) +requests and the preferred [Websockets](http://en.wikipedia.org/wiki/WebSocket). +All of the [standard](#Methods) and [extension](#ExtensionMethods) methods +described in this documentation can be accessed through both. As the name +indicates, the [Websocket-specific extension](#WSExtMethods) methods can only be +accessed when connected via Websockets. + +As mentioned in the [overview](#Overview), the websocket connection endpoint for +btcd is `wss://your_ip_or_domain:8334/ws`. + +The most important differences between the two transports as it pertains to the +JSON-RPC API are: + +| |HTTP POST Requests|Websockets| +|---|------------------|----------| +|Allows multiple requests across a single connection|No|Yes| +|Supports asynchronous notifications|No|Yes| +|Scales well with large numbers of requests|No|Yes| + +<a name="Authentication" /> +### 3. Authentication + +<a name="AuthenticationOverview" /> +**3.1 Authentication Overview**<br /> + +The following authentication details are needed before establishing a connection +to a btcd RPC server: + +* **rpcuser** is the full-access username configured for the btcd RPC server +* **rpcpass** is the full-access password configured for the btcd RPC server +* **rpclimituser** is the limited username configured for the btcd RPC server +* **rpclimitpass** is the limited password configured for the btcd RPC server +* **rpccert** is the PEM-encoded X.509 certificate (public key) that the btcd + server is configured with. It is automatically generated by btcd and placed + in the btcd home directory (which is typically `%LOCALAPPDATA%\Btcd` on + Windows and `~/.btcd` on POSIX-like OSes) + +**NOTE:** As mentioned above, btcd is secure by default which means the RPC +server is not running unless configured with a **rpcuser** and **rpcpass** +and/or a **rpclimituser** and **rpclimitpass**, and uses TLS authentication for +all connections. + +Depending on which connection transaction you are using, you can choose one of +two, mutually exclusive, methods. +- [Use HTTP Authorization Header](#HTTPAuth) - HTTP POST requests and Websockets +- [Use the JSON-RPC "authenticate" command](#JSONAuth) - Websockets only + +<a name="HTTPAuth" /> +**3.2 HTTP Basic Access Authentication**<br /> + +The btcd RPC server uses HTTP [basic access authentication](http://en.wikipedia.org/wiki/Basic_access_authentication) with the **rpcuser** +and **rpcpass** detailed above. If the supplied credentials are invalid, you +will be disconnected immediately upon making the connection. + +<a name="JSONAuth" /> +**3.3 JSON-RPC Authenticate Command (Websocket-specific)**<br /> + +While the HTTP basic access authentication method is the preferred method, the +ability to set HTTP headers from websockets is not always available. In that +case, you will need to use the [authenticate](#authenticate) JSON-RPC method. + +The [authenticate](#authenticate) command must be the first command sent after +connecting to the websocket. Sending any other commands before authenticating, +supplying invalid credentials, or attempting to authenticate again when already +authenticated will cause the websocket to be closed immediately. + + +<a name="CLIUtil" /> +### 4. Command-line Utility + +btcd comes with a separate utility named `btcctl` which can be used to issue +these RPC commands via HTTP POST requests to btcd after configuring it with the +information in the [Authentication](#Authentication) section above. It can also +be used to communicate with any server/daemon/service which provides a JSON-RPC +API compatible with the original bitcoind/bitcoin-qt client. + +<a name="Methods" /> +### 5. Standard Methods + +<a name="MethodOverview" /> +**5.1 Method Overview**<br /> + +The following is an overview of the RPC methods and their current status. Click +the method name for further details such as parameter and return information. + +|#|Method|Safe for limited user?|Description| +|---|------|----------|-----------| +|1|[addnode](#addnode)|N|Attempts to add or remove a persistent peer.| +|2|[createrawtransaction](#createrawtransaction)|Y|Returns a new transaction spending the provided inputs and sending to the provided addresses.| +|3|[decoderawtransaction](#decoderawtransaction)|Y|Returns a JSON object representing the provided serialized, hex-encoded transaction.| +|4|[decodescript](#decodescript)|Y|Returns a JSON object with information about the provided hex-encoded script.| +|5|[getaddednodeinfo](#getaddednodeinfo)|N|Returns information about manually added (persistent) peers.| +|6|[getbestblockhash](#getbestblockhash)|Y|Returns the hash of the of the best (most recent) block in the longest block chain.| +|7|[getblock](#getblock)|Y|Returns information about a block given its hash.| +|8|[getblockcount](#getblockcount)|Y|Returns the number of blocks in the longest block chain.| +|9|[getblockhash](#getblockhash)|Y|Returns hash of the block in best block chain at the given height.| +|10|[getblockheader](#getblockheader)|Y|Returns the block header of the block.| +|11|[getconnectioncount](#getconnectioncount)|N|Returns the number of active connections to other peers.| +|12|[getdifficulty](#getdifficulty)|Y|Returns the proof-of-work difficulty as a multiple of the minimum difficulty.| +|13|[getgenerate](#getgenerate)|N|Return if the server is set to generate coins (mine) or not.| +|14|[gethashespersec](#gethashespersec)|N|Returns a recent hashes per second performance measurement while generating coins (mining).| +|15|[getinfo](#getinfo)|Y|Returns a JSON object containing various state info.| +|16|[getmempoolinfo](#getmempoolinfo)|N|Returns a JSON object containing mempool-related information.| +|17|[getmininginfo](#getmininginfo)|N|Returns a JSON object containing mining-related information.| +|18|[getnettotals](#getnettotals)|Y|Returns a JSON object containing network traffic statistics.| +|19|[getnetworkhashps](#getnetworkhashps)|Y|Returns the estimated network hashes per second for the block heights provided by the parameters.| +|20|[getpeerinfo](#getpeerinfo)|N|Returns information about each connected network peer as an array of json objects.| +|21|[getrawmempool](#getrawmempool)|Y|Returns an array of hashes for all of the transactions currently in the memory pool.| +|22|[getrawtransaction](#getrawtransaction)|Y|Returns information about a transaction given its hash.| +|23|[getwork](#getwork)|N|Returns formatted hash data to work on or checks and submits solved data.<br /><font color="orange">NOTE: Since btcd does not have the wallet integrated to provide payment addresses, btcd must be configured via the `--miningaddr` option to provide which payment addresses to pay created blocks to for this RPC to function.</font>| +|24|[help](#help)|Y|Returns a list of all commands or help for a specified command.| +|25|[ping](#ping)|N|Queues a ping to be sent to each connected peer.| +|26|[sendrawtransaction](#sendrawtransaction)|Y|Submits the serialized, hex-encoded transaction to the local peer and relays it to the network.<br /><font color="orange">btcd does not yet implement the `allowhighfees` parameter, so it has no effect</font>| +|27|[setgenerate](#setgenerate) |N|Set the server to generate coins (mine) or not.<br/>NOTE: Since btcd does not have the wallet integrated to provide payment addresses, btcd must be configured via the `--miningaddr` option to provide which payment addresses to pay created blocks to for this RPC to function.| +|28|[stop](#stop)|N|Shutdown btcd.| +|29|[submitblock](#submitblock)|Y|Attempts to submit a new serialized, hex-encoded block to the network.| +|30|[validateaddress](#validateaddress)|Y|Verifies the given address is valid. NOTE: Since btcd does not have a wallet integrated, btcd will only return whether the address is valid or not.| +|31|[verifychain](#verifychain)|N|Verifies the block chain database.| + +<a name="MethodDetails" /> +**5.2 Method Details**<br /> + +<a name="addnode"/> + +| | | +|---|---| +|Method|addnode| +|Parameters|1. peer (string, required) - ip address and port of the peer to operate on<br />2. command (string, required) - `add` to add a persistent peer, `remove` to remove a persistent peer, or `onetry` to try a single connection to a peer| +|Description|Attempts to add or remove a persistent peer.| +|Returns|Nothing| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="createrawtransaction"/> + +| | | +|---|---| +|Method|createrawtransaction| +|Parameters|1. transaction inputs (JSON array, required) - json array of json objects<br />`[`<br /> `{`<br /> `"txid": "hash", (string, required) the hash of the input transaction`<br /> `"vout": n (numeric, required) the specific output of the input transaction to redeem`<br /> `}, ...`<br />`]`<br />2. addresses and amounts (JSON object, required) - json object with addresses as keys and amounts as values<br />`{`<br /> `"address": n.nnn (numeric, required) the address to send to as the key and the amount in BTC as the value`<br /> `, ...`<br />`}`<br />3. locktime (int64, optional, default=0) - specifies the transaction locktime. If non-zero, the inputs will also have their locktimes activated. | +|Description|Returns a new transaction spending the provided inputs and sending to the provided addresses.<br />The transaction inputs are not signed in the created transaction.<br />The `signrawtransaction` RPC command provided by wallet must be used to sign the resulting transaction.| +|Returns|`"transaction" (string) hex-encoded bytes of the serialized transaction`| +|Example Parameters|1. transaction inputs `[{"txid":"e6da89de7a6b8508ce8f371a3d0535b04b5e108cb1a6e9284602d3bfd357c018","vout":1}]`<br />2. addresses and amounts `{"13cgrTP7wgbZYWrY9BZ22BV6p82QXQT3nY": 0.49213337}`<br />3. locktime `0`| +|Example Return|`010000000118c057d3bfd3024628e9a6b18c105e4bb035053d1a378fce08856b7ade89dae6010000`<br />`0000ffffffff0199efee02000000001976a9141cb013db35ecccc156fdfd81d03a11c51998f99388`<br />`ac00000000`<br /><font color="orange">**Newlines added for display purposes. The actual return does not contain newlines.**</font>| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="decoderawtransaction"/> + +| | | +|---|---| +|Method|decoderawtransaction| +|Parameters|1. data (string, required) - serialized, hex-encoded transaction| +|Description|Returns a JSON object representing the provided serialized, hex-encoded transaction.| +|Returns|`{ (json object)`<br /> `"txid": "hash", (string) the hash of the transaction`<br /> `"version": n, (numeric) the transaction version`<br /> `"locktime": n, (numeric) the transaction lock time`<br /> `"vin": [ (array of json objects) the transaction inputs as json objects`<br /> <font color="orange">For coinbase transactions:</font><br /> `{ (json object)`<br /> `"coinbase": "data", (string) the hex-encoded bytes of the signature script`<br /> `"sequence": n, (numeric) the script sequence number`<br /> `}`<br /> <font color="orange">For non-coinbase transactions:</font><br /> `{ (json object)`<br /> `"txid": "hash", (string) the hash of the origin transaction`<br /> `"vout": n, (numeric) the index of the output being redeemed from the origin transaction`<br /> `"scriptSig": { (json object) the signature script used to redeem the origin transaction`<br /> `"asm": "asm", (string) disassembly of the script`<br /> `"hex": "data", (string) hex-encoded bytes of the script`<br /> `}`<br /> `"sequence": n, (numeric) the script sequence number`<br /> `}, ...`<br /> `]`<br /> `"vout": [ (array of json objects) the transaction outputs as json objects`<br /> `{ (json object)`<br /> `"value": n, (numeric) the value in BTC`<br /> `"n": n, (numeric) the index of this transaction output`<br /> `"scriptPubKey": { (json object) the public key script used to pay coins`<br /> `"asm": "asm", (string) disassembly of the script`<br /> `"hex": "data", (string) hex-encoded bytes of the script`<br /> `"reqSigs": n, (numeric) the number of required signatures`<br /> `"type": "scripttype" (string) the type of the script (e.g. 'pubkeyhash')`<br /> `"addresses": [ (json array of string) the bitcoin addresses associated with this output`<br /> `"bitcoinaddress", (string) the bitcoin address`<br /> `...`<br /> `]`<br /> `}`<br /> `}, ...`<br /> `]`<br />`}`| +|Example Return|`{`<br /> `"txid": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",`<br /> `"version": 1,`<br /> `"locktime": 0,`<br /> `"vin": [`<br /> <font color="orange">For coinbase transactions:</font><br /> `{ (json object)`<br /> `"coinbase": "04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6...",`<br /> `"sequence": 4294967295,`<br /> `}`<br /> <font color="orange">For non-coinbase transactions:</font><br /> `{`<br /> `"txid": "60ac4b057247b3d0b9a8173de56b5e1be8c1d1da970511c626ef53706c66be04",`<br /> `"vout": 0,`<br /> `"scriptSig": {`<br /> `"asm": "3046022100cb42f8df44eca83dd0a727988dcde9384953e830b1f8004d57485e2ede1b9c8f0...",`<br /> `"hex": "493046022100cb42f8df44eca83dd0a727988dcde9384953e830b1f8004d57485e2ede1b9c8...",`<br /> `}`<br /> `"sequence": 4294967295,`<br /> `}`<br /> `]`<br /> `"vout": [`<br /> `{`<br /> `"value": 50,`<br /> `"n": 0,`<br /> `"scriptPubKey": {`<br /> `"asm": "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4ce...",`<br /> `"hex": "4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4...",`<br /> `"reqSigs": 1,`<br /> `"type": "pubkey"`<br /> `"addresses": [`<br /> `"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",`<br /> `]`<br /> `}`<br /> `}`<br /> `]`<br />`}`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="decodescript"/> + +| | | +|---|---| +|Method|decodescript| +|Parameters|1. script (string, required) - hex-encoded script| +|Description|Returns a JSON object with information about the provided hex-encoded script.| +|Returns|`{ (json object)`<br /> `"asm": "asm", (string) disassembly of the script`<br /> `"reqSigs": n, (numeric) the number of required signatures`<br /> `"type": "scripttype", (string) the type of the script (e.g. 'pubkeyhash')`<br /> `"addresses": [ (json array of string) the bitcoin addresses associated with this script`<br /> `"bitcoinaddress", (string) the bitcoin address`<br /> `...`<br /> `]`<br /> `"p2sh": "scripthash", (string) the script hash for use in pay-to-script-hash transactions`<br />`}`| +|Example Return|`{`<br /> `"asm": "OP_DUP OP_HASH160 b0a4d8a91981106e4ed85165a66748b19f7b7ad4 OP_EQUALVERIFY OP_CHECKSIG",`<br /> `"reqSigs": 1,`<br /> `"type": "pubkeyhash",`<br /> `"addresses": [`<br /> `"1H71QVBpzuLTNUh5pewaH3UTLTo2vWgcRJ"`<br /> `]`<br /> `"p2sh": "359b84ff799f48231990ff0298206f54117b08b6"`<br />`}`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getaddednodeinfo"/> + +| | | +|---|---| +|Method|getaddednodeinfo| +|Parameters|1. dns (boolean, required) - specifies whether the returned data is a JSON object including DNS and connection information, or just a list of added peers<br />2. node (string, optional) - only return information about this specific peer instead of all added peers.| +|Description|Returns information about manually added (persistent) peers.| +|Returns (dns=false)|`["ip:port", ...]`| +|Returns (dns=true)|`[ (json array of objects)`<br /> `{ (json object)`<br /> `"addednode": "ip_or_domain", (string) the ip address or domain of the added peer`<br /> `"connected": true or false, (boolean) whether or not the peer is currently connected`<br /> `"addresses": [ (json array or objects) DNS lookup and connection information about the peer`<br /> `{ (json object)`<br /> `"address": "ip", (string) the ip address for this DNS entry`<br /> `"connected": "inbound/outbound/false" (string) the connection 'direction' (if connected)`<br /> `}, ...`<br /> `]`<br /> `}, ...`<br />`]`| +|Example Return (dns=false)|`["192.168.0.10:8333", "mydomain.org:8333"]`| +|Example Return (dns=true)|`[`<br /> `{`<br /> `"addednode": "mydomain.org:8333",`<br /> `"connected": true,`<br /> `"addresses": [`<br /> `{`<br /> `"address": "1.2.3.4",`<br /> `"connected": "outbound"`<br /> `},`<br /> `{`<br /> `"address": "5.6.7.8",`<br /> `"connected": "false"`<br /> `}`<br /> `]`<br /> `}`<br />`]`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getbestblockhash"/> + +| | | +|---|---| +|Method|getbestblockhash| +|Parameters|None| +|Description|Returns the hash of the of the best (most recent) block in the longest block chain.| +|Returns|string| +|Example Return|`0000000000000001f356adc6b29ab42b59f913a396e170f80190dba615bd1e60`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getblock"/> + +| | | +|---|---| +|Method|getblock| +|Parameters|1. block hash (string, required) - the hash of the block<br />2. verbose (boolean, optional, default=true) - specifies the block is returned as a JSON object instead of hex-encoded string<br />3. verbosetx (boolean, optional, default=false) - specifies that each transaction is returned as a JSON object and only applies if the `verbose` flag is true.<font color="orange">**This parameter is a btcd extension**</font>| +|Description|Returns information about a block given its hash.| +|Returns (verbose=false)|`"data" (string) hex-encoded bytes of the serialized block`| +|Returns (verbose=true, verbosetx=false)|`{ (json object)`<br /> `"hash": "blockhash", (string) the hash of the block (same as provided)`<br /> `"confirmations": n, (numeric) the number of confirmations`<br /> `"size": n, (numeric) the size of the block`<br /> `"height": n, (numeric) the height of the block in the block chain`<br /> `"version": n, (numeric) the block version`<br /> `"merkleroot": "hash", (string) root hash of the merkle tree`<br /> `"tx": [ (json array of string) the transaction hashes`<br /> `"transactionhash", (string) hash of the parent transaction`<br /> `...`<br /> `]`<br /> `"time": n, (numeric) the block time in seconds since 1 Jan 1970 GMT`<br /> `"nonce": n, (numeric) the block nonce`<br /> `"bits", n, (numeric) the bits which represent the block difficulty`<br /> `difficulty: n.nn, (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty`<br /> `"previousblockhash": "hash", (string) the hash of the previous block`<br /> `"nextblockhash": "hash", (string) the hash of the next block (only if there is one)`<br />`}`| +|Returns (verbose=true, verbosetx=true)|`{ (json object)`<br /> `"hash": "blockhash", (string) the hash of the block (same as provided)`<br /> `"confirmations": n, (numeric) the number of confirmations`<br /> `"size": n, (numeric) the size of the block`<br /> `"height": n, (numeric) the height of the block in the block chain`<br /> `"version": n, (numeric) the block version`<br /> `"merkleroot": "hash", (string) root hash of the merkle tree`<br /> `"rawtx": [ (array of json objects) the transactions as json objects`<br /> `(see getrawtransaction json object details)`<br /> `]`<br /> `"time": n, (numeric) the block time in seconds since 1 Jan 1970 GMT`<br /> `"nonce": n, (numeric) the block nonce`<br /> `"bits", n, (numeric) the bits which represent the block difficulty`<br /> `difficulty: n.nn, (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty`<br /> `"previousblockhash": "hash", (string) the hash of the previous block`<br /> `"nextblockhash": "hash", (string) the hash of the next block`<br />`}`| +|Example Return (verbose=false)|`"010000000000000000000000000000000000000000000000000000000000000000000000`<br />`3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49`<br />`ffff001d1dac2b7c01010000000100000000000000000000000000000000000000000000`<br />`00000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f`<br />`4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f`<br />`6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104`<br />`678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f`<br />`4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"`<br /><font color="orange">**Newlines added for display purposes. The actual return does not contain newlines.**</font>| +|Example Return (verbose=true, verbosetx=false)|`{`<br /> `"hash": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",`<br /> `"confirmations": 277113,`<br /> `"size": 285,`<br /> `"height": 0,`<br /> `"version": 1,`<br /> `"merkleroot": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",`<br /> `"tx": [`<br /> `"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"`<br /> `],`<br /> `"time": 1231006505,`<br /> `"nonce": 2083236893,`<br /> `"bits": "1d00ffff",`<br /> `"difficulty": 1,`<br /> `"previousblockhash": "0000000000000000000000000000000000000000000000000000000000000000",`<br /> `"nextblockhash": "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"`<br />`}`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getblockcount"/> + +| | | +|---|---| +|Method|getblockcount| +|Parameters|None| +|Description|Returns the number of blocks in the longest block chain.| +|Returns|numeric| +|Example Return|`276820`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getblockhash"/> + +| | | +|---|---| +|Method|getblockhash| +|Parameters|1. block height (numeric, required)| +|Description|Returns hash of the block in best block chain at the given height.| +|Returns|string| +|Example Return|`000000000000000096579458d1c0f1531fcfc58d57b4fce51eb177d8d10e784d`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getblockheader"/> + +| | | +|---|---| +|Method|getblockheader| +|Parameters|1. block hash (string, required) - the hash of the block<br />2. verbose (boolean, optional, default=true) - specifies the block header is returned as a JSON object instead of a hex-encoded string| +|Description|Returns hex-encoded bytes of the serialized block header.| +|Returns (verbose=false)|`"data" (string) hex-encoded bytes of the serialized block`| +|Returns (verbose=true)|`{ (json object)`<br /> `"hash": "blockhash", (string) the hash of the block (same as provided)`<br /> `"confirmations": n, (numeric) the number of confirmations`<br /> `"height": n, (numeric) the height of the block in the block chain`<br /> `"version": n, (numeric) the block version`<br /> `"merkleroot": "hash", (string) root hash of the merkle tree`<br /> `"time": n, (numeric) the block time in seconds since 1 Jan 1970 GMT`<br /> `"nonce": n, (numeric) the block nonce`<br /> `"bits": n, (numeric) the bits which represent the block difficulty`<br /> `"difficulty": n.nn, (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty`<br /> `"previousblockhash": "hash", (string) the hash of the previous block`<br /> `"nextblockhash": "hash", (string) the hash of the next block (only if there is one)`<br />`}`| +|Example Return (verbose=false)|`"0200000035ab154183570282ce9afc0b494c9fc6a3cfea05aa8c1add2ecc564900000000`<br />`38ba3d78e4500a5a7570dbe61960398add4410d278b21cd9708e6d9743f374d544fc0552`<br />`27f1001c29c1ea3b"`<br /><font color="orange">**Newlines added for display purposes. The actual return does not contain newlines.**</font>| +|Example Return (verbose=true)|`{`<br /> `"hash": "00000000009e2958c15ff9290d571bf9459e93b19765c6801ddeccadbb160a1e",`<br /> `"confirmations": 392076,`<br /> `"height": 100000,`<br /> `"version": 2,`<br /> `"merkleroot": "d574f343976d8e70d91cb278d21044dd8a396019e6db70755a0a50e4783dba38",`<br /> `"time": 1376123972,`<br /> `"nonce": 1005240617,`<br /> `"bits": "1c00f127",`<br /> `"difficulty": 271.75767393,`<br /> `"previousblockhash": "000000004956cc2edd1a8caa05eacfa3c69f4c490bfc9ace820257834115ab35",`<br /> `"nextblockhash": "0000000000629d100db387f37d0f37c51118f250fb0946310a8c37316cbc4028"`<br />`}`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getconnectioncount"/> + +| | | +|---|---| +|Method|getconnectioncount| +|Parameters|None| +|Description|Returns the number of active connections to other peers| +|Returns|numeric| +|Example Return|`8`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getdifficulty"/> + +| | | +|---|---| +|Method|getdifficulty| +|Parameters|None| +|Description|Returns the proof-of-work difficulty as a multiple of the minimum difficulty.| +|Returns|numeric| +|Example Return|`1180923195.260000`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getgenerate"/> + +| | | +|---|---| +|Method|getgenerate| +|Parameters|None| +|Description|Return if the server is set to generate coins (mine) or not.| +|Returns|`false` (boolean)| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="gethashespersec"/> + +| | | +|---|---| +|Method|gethashespersec| +|Parameters|None| +|Description|Returns a recent hashes per second performance measurement while generating coins (mining).| +|Returns|`0` (numeric)| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getinfo"/> + +| | | +|---|---| +|Method|getinfo| +|Parameters|None| +|Description|Returns a JSON object containing various state info.| +|Notes|NOTE: Since btcd does NOT contain wallet functionality, wallet-related fields are not returned. See getinfo in btcwallet for a version which includes that information.| +|Returns|`{ (json object)`<br /> `"version": n, (numeric) the version of the server`<br /> `"protocolversion": n, (numeric) the latest supported protocol version`<br /> `"blocks": n, (numeric) the number of blocks processed`<br /> `"timeoffset": n, (numeric) the time offset`<br /> `"connections": n, (numeric) the number of connected peers`<br /> `"proxy": "host:port", (string) the proxy used by the server`<br /> `"difficulty": n.nn, (numeric) the current target difficulty`<br /> `"testnet": true or false, (boolean) whether or not server is using testnet`<br /> `"relayfee": n.nn, (numeric) the minimum relay fee for non-free transactions in BTC/KB`<br />`}`| +|Example Return|`{`<br /> `"version": 70000`<br /> `"protocolversion": 70001, `<br /> `"blocks": 298963,`<br /> `"timeoffset": 0,`<br /> `"connections": 17,`<br /> `"proxy": "",`<br /> `"difficulty": 8000872135.97,`<br /> `"testnet": false,`<br /> `"relayfee": 0.00001,`<br />`}`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getmempoolinfo"/> + +| | | +|---|---| +|Method|getmempoolinfo| +|Parameters|None| +|Description|Returns a JSON object containing mempool-related information.| +|Returns|`{ (json object)`<br /> `"bytes": n, (numeric) size in bytes of the mempool`<br /> `"size": n, (numeric) number of transactions in the mempool`<br />`}`| +Example Return|`{`<br /> `"bytes": 310768,`<br /> `"size": 157,`<br />`}`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getmininginfo"/> + +| | | +|---|---| +|Method|getmininginfo| +|Parameters|None| +|Description|Returns a JSON object containing mining-related information.| +|Returns|`{ (json object)`<br /> `"blocks": n, (numeric) latest best block`<br /> `"currentblocksize": n, (numeric) size of the latest best block`<br /> `"currentblocktx": n, (numeric) number of transactions in the latest best block`<br /> `"difficulty": n.nn, (numeric) current target difficulty`<br /> `"errors": "errors", (string) any current errors`<br /> `"generate": true or false, (boolean) whether or not server is set to generate coins`<br /> `"genproclimit": n, (numeric) number of processors to use for coin generation (-1 when disabled)`<br /> `"hashespersec": n, (numeric) recent hashes per second performance measurement while generating coins`<br /> `"networkhashps": n, (numeric) estimated network hashes per second for the most recent blocks`<br /> `"pooledtx": n, (numeric) number of transactions in the memory pool`<br /> `"testnet": true or false, (boolean) whether or not server is using testnet`<br />`}`| +|Example Return|`{`<br /> `"blocks": 236526,`<br /> `"currentblocksize": 185,`<br /> `"currentblocktx": 1,`<br /> `"difficulty": 256,`<br /> `"errors": "",`<br /> `"generate": false,`<br /> `"genproclimit": -1,`<br /> `"hashespersec": 0,`<br /> `"networkhashps": 33081554756,`<br /> `"pooledtx": 8,`<br /> `"testnet": true,`<br />`}`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getnettotals"/> + +| | | +|---|---| +|Method|getnettotals| +|Parameters|None| +|Description|Returns a JSON object containing network traffic statistics.| +|Returns|`{`<br /> `"totalbytesrecv": n, (numeric) total bytes received`<br /> `"totalbytessent": n, (numeric) total bytes sent`<br /> `"timemillis": n (numeric) number of milliseconds since 1 Jan 1970 GMT`<br />`}`| +|Example Return|`{`<br /> `"totalbytesrecv": 1150990,`<br /> `"totalbytessent": 206739,`<br /> `"timemillis": 1391626433845`<br />`}`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getnetworkhashps"/> + +| | | +|---|---| +|Method|getnetworkhashps| +|Parameters|1. blocks (numeric, optional, default=120) - The number of blocks, or -1 for blocks since last difficulty change<br />2. height (numeric, optional, default=-1) - Perform estimate ending with this height or -1 for current best chain block height| +|Description|Returns the estimated network hashes per second for the block heights provided by the parameters.| +|Returns|numeric| +|Example Return|`6573971939`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getpeerinfo"/> + +| | | +|---|---| +|Method|getpeerinfo| +|Parameters|None| +|Description|Returns data about each connected network peer as an array of json objects.| +|Returns|`[`<br /> `{`<br /> `"addr": "host:port", (string) the ip address and port of the peer`<br /> `"services": "00000001", (string) the services supported by the peer`<br /> `"lastrecv": n, (numeric) time the last message was received in seconds since 1 Jan 1970 GMT`<br /> `"lastsend": n, (numeric) time the last message was sent in seconds since 1 Jan 1970 GMT`<br /> `"bytessent": n, (numeric) total bytes sent`<br /> `"bytesrecv": n, (numeric) total bytes received`<br /> `"conntime": n, (numeric) time the connection was made in seconds since 1 Jan 1970 GMT`<br /> `"pingtime": n, (numeric) number of microseconds the last ping took`<br /> `"pingwait": n, (numeric) number of microseconds a queued ping has been waiting for a response`<br /> `"version": n, (numeric) the protocol version of the peer`<br /> `"subver": "useragent", (string) the user agent of the peer`<br /> `"inbound": true_or_false, (boolean) whether or not the peer is an inbound connection`<br /> `"startingheight": n, (numeric) the latest block height the peer knew about when the connection was established`<br /> `"currentheight": n, (numeric) the latest block height the peer is known to have relayed since connected`<br /> `"syncnode": true_or_false, (boolean) whether or not the peer is the sync peer`<br /> `}, ...`<br />`]`| +|Example Return|`[`<br /> `{`<br /> `"addr": "178.172.xxx.xxx:8333",`<br /> `"services": "00000001",`<br /> `"lastrecv": 1388183523,`<br /> `"lastsend": 1388185470,`<br /> `"bytessent": 287592965,`<br /> `"bytesrecv": 780340,`<br /> `"conntime": 1388182973,`<br /> `"pingtime": 405551,`<br /> `"pingwait": 183023,`<br /> `"version": 70001,`<br /> `"subver": "/btcd:0.4.0/",`<br /> `"inbound": false,`<br /> `"startingheight": 276921,`<br /> `"currentheight": 276955,`<br/> `"syncnode": true,`<br /> `}`<br />`]`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getrawtransaction"/> + +| | | +|---|---| +|Method|getrawtransaction| +|Parameters|1. transaction hash (string, required) - the hash of the transaction<br />2. verbose (int, optional, default=0) - specifies the transaction is returned as a JSON object instead of hex-encoded string| +|Description|Returns information about a transaction given its hash.| +|Returns (verbose=0)|`"data" (string) hex-encoded bytes of the serialized transaction`| +|Returns (verbose=1)|`{ (json object)`<br /> `"hex": "data", (string) hex-encoded transaction`<br /> `"txid": "hash", (string) the hash of the transaction`<br /> `"version": n, (numeric) the transaction version`<br /> `"locktime": n, (numeric) the transaction lock time`<br /> `"vin": [ (array of json objects) the transaction inputs as json objects`<br /> <font color="orange">For coinbase transactions:</font><br /> `{ (json object)`<br /> `"coinbase": "data", (string) the hex-encoded bytes of the signature script`<br /> `"sequence": n, (numeric) the script sequence number`<br /> `}`<br /> <font color="orange">For non-coinbase transactions:</font><br /> `{ (json object)`<br /> `"txid": "hash", (string) the hash of the origin transaction`<br /> `"vout": n, (numeric) the index of the output being redeemed from the origin transaction`<br /> `"scriptSig": { (json object) the signature script used to redeem the origin transaction`<br /> `"asm": "asm", (string) disassembly of the script`<br /> `"hex": "data", (string) hex-encoded bytes of the script`<br /> `}`<br /> `"sequence": n, (numeric) the script sequence number`<br /> `}, ...`<br /> `]`<br /> `"vout": [ (array of json objects) the transaction outputs as json objects`<br /> `{ (json object)`<br /> `"value": n, (numeric) the value in BTC`<br /> `"n": n, (numeric) the index of this transaction output`<br /> `"scriptPubKey": { (json object) the public key script used to pay coins`<br /> `"asm": "asm", (string) disassembly of the script`<br /> `"hex": "data", (string) hex-encoded bytes of the script`<br /> `"reqSigs": n, (numeric) the number of required signatures`<br /> `"type": "scripttype" (string) the type of the script (e.g. 'pubkeyhash')`<br /> `"addresses": [ (json array of string) the bitcoin addresses associated with this output`<br /> `"bitcoinaddress", (string) the bitcoin address`<br /> `...`<br /> `]`<br /> `}`<br /> `}, ...`<br /> `]`<br />`}`| +|Example Return (verbose=0)|`"010000000104be666c7053ef26c6110597dad1c1e81b5e6be53d17a8b9d0b34772054bac60000000`<br />`008c493046022100cb42f8df44eca83dd0a727988dcde9384953e830b1f8004d57485e2ede1b9c8f`<br />`022100fbce8d84fcf2839127605818ac6c3e7a1531ebc69277c504599289fb1e9058df0141045a33`<br />`76eeb85e494330b03c1791619d53327441002832f4bd618fd9efa9e644d242d5e1145cb9c2f71965`<br />`656e276633d4ff1a6db5e7153a0a9042745178ebe0f5ffffffff0280841e00000000001976a91406`<br />`f1b6703d3f56427bfcfd372f952d50d04b64bd88ac4dd52700000000001976a9146b63f291c295ee`<br />`abd9aee6be193ab2d019e7ea7088ac00000000`<br /><font color="orange">**Newlines added for display purposes. The actual return does not contain newlines.**</font>| +|Example Return (verbose=1)|`{`<br /> `"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000f...",`<br /> `"txid": "90743aad855880e517270550d2a881627d84db5265142fd1e7fb7add38b08be9",`<br /> `"version": 1,`<br /> `"locktime": 0,`<br /> `"vin": [`<br /> <font color="orange">For coinbase transactions:</font><br /> `{ (json object)`<br /> `"coinbase": "03708203062f503253482f04066d605108f800080100000ea2122f6f7a636f696e4065757374726174756d2f",`<br /> `"sequence": 0,`<br /> `}`<br /> <font color="orange">For non-coinbase transactions:</font><br /> `{`<br /> `"txid": "60ac4b057247b3d0b9a8173de56b5e1be8c1d1da970511c626ef53706c66be04",`<br /> `"vout": 0,`<br /> `"scriptSig": {`<br /> `"asm": "3046022100cb42f8df44eca83dd0a727988dcde9384953e830b1f8004d57485e2ede1b9c8f0...",`<br /> `"hex": "493046022100cb42f8df44eca83dd0a727988dcde9384953e830b1f8004d57485e2ede1b9c8...",`<br /> `}`<br /> `"sequence": 4294967295,`<br /> `}`<br /> `]`<br /> `"vout": [`<br /> `{`<br /> `"value": 25.1394,`<br /> `"n": 0,`<br /> `"scriptPubKey": {`<br /> `"asm": "OP_DUP OP_HASH160 ea132286328cfc819457b9dec386c4b5c84faa5c OP_EQUALVERIFY OP_CHECKSIG",`<br /> `"hex": "76a914ea132286328cfc819457b9dec386c4b5c84faa5c88ac",`<br /> `"reqSigs": 1,`<br /> `"type": "pubkeyhash"`<br /> `"addresses": [`<br /> `"1NLg3QJMsMQGM5KEUaEu5ADDmKQSLHwmyh",`<br /> `]`<br /> `}`<br /> `}`<br /> `]`<br />`}`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getwork"/> + +| | | +|---|---| +|Method|getwork| +|Parameters|1. data (string, optional) - The hex| +|Description|Returns information about a transaction given its hash.| +|Notes|<font color="orange">NOTE: Since btcd does not have the wallet integrated to provide payment addresses, btcd must be configured via the `--miningaddr` option to provide which payment addresses to pay created blocks to for this RPC to function.</font> +|Returns (data not specified)|`{ (json object)`<br /> `"data": "hex", (string) hex-encoded block data`<br /> `"hash1": "hex", (string) (DEPRECATED) hex-encoded formatted hash buffer`<br /> `"midstate": "hex", (string) (DEPRECATED) hex-encoded precomputed hash state after hashing first half of the data`<br /> `"target": "hex", (string) the hex-encoded little-endian hash target`<br />`}`| +|Returns (data specified)|`true` or `false` (boolean)| +|Example Return (data not specified)|`{`<br /> `"data": "00000002c39b5d2b7a1e8f7356a1efce26b24bd15d7d906e85341ef9cec99b6a000000006474f...",`<br /> `"hash1": "00000000000000000000000000000000000000000000000000000000000000000000008000000...",`<br /> `"midstate": "ae4a80fc51476e452de855b4e20d5f33418c50fc7cae3b1ecd5badb819b8a584",`<br /> `"target": "0000000000000000000000000000000000000000000000008c96010000000000",`<br />`}`| +|Example Return (data specified)|`true`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="help"/> + +| | | +|---|---| +|Method|help| +|Parameters|1. command (string, optional) - the command to get help for| +|Description|Returns a list of all commands or help for a specified command.<br />When no `command` parameter is specified, a list of avaialable commands is returned<br />When `command` is a valid method, the help text for that method is returned.| +|Returns|string| +|Example Return|getblockcount<br />Returns a numeric for the number of blocks in the longest block chain.| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="ping"/> + +| | | +|---|---| +|Method|ping| +|Parameters|None| +|Description|Queues a ping to be sent to each connected peer.<br />Ping times are provided by [getpeerinfo](#getpeerinfo) via the `pingtime` and `pingwait` fields.| +|Returns|Nothing| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="getrawmempool"/> + +| | | +|---|---| +|Method|getrawmempool| +|Parameters|1. verbose (boolean, optional, default=false)| +|Description|Returns an array of hashes for all of the transactions currently in the memory pool.<br />The `verbose` flag specifies that each transaction is returned as a JSON object.| +|Notes|<font color="orange">Since btcd does not perform any mining, the priority related fields `startingpriority` and `currentpriority` that are available when the `verbose` flag is set are always 0.</font>| +|Returns (verbose=false)|`[ (json array of string)`<br /> `"transactionhash", (string) hash of the transaction`<br /> `...`<br />`]`| +|Returns (verbose=true)|`{ (json object)`<br /> `"transactionhash": { (json object)`<br /> `"size": n, (numeric) transaction size in bytes`<br /> `"fee" : n, (numeric) transaction fee in bitcoins`<br /> `"time": n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT`<br /> `"height": n, (numeric) block height when transaction entered the pool`<br /> `"startingpriority": n, (numeric) priority when transaction entered the pool`<br /> `"currentpriority": n, (numeric) current priority`<br /> `"depends": [ (json array) unconfirmed transactions used as inputs for this transaction`<br /> `"transactionhash", (string) hash of the parent transaction`<br /> `...`<br /> `]`<br /> `}, ...`<br />`}`| +|Example Return (verbose=false)|`[`<br /> `"3480058a397b6ffcc60f7e3345a61370fded1ca6bef4b58156ed17987f20d4e7",`<br /> `"cbfe7c056a358c3a1dbced5a22b06d74b8650055d5195c1c2469e6b63a41514a"`<br />`]`| +|Example Return (verbose=true)|`{`<br /> `"1697a19cede08694278f19584e8dcc87945f40c6b59a942dd8906f133ad3f9cc": {`<br /> `"size": 226,`<br /> `"fee" : 0.0001,`<br /> `"time": 1387992789,`<br /> `"height": 276836,`<br /> `"startingpriority": 0,`<br /> `"currentpriority": 0,`<br /> `"depends": [`<br /> `"aa96f672fcc5a1ec6a08a94aa46d6b789799c87bd6542967da25a96b2dee0afb",`<br /> `]`<br />`}`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="setgenerate"/> + +| | | +|---|---| +|Method|setgenerate| +|Parameters|1. generate (boolean, required) - `true` to enable generation, `false` to disable it<br />2. genproclimit (numeric, optional) - the number of processors (cores) to limit generation to or `-1` for default| +|Description|Set the server to generate coins (mine) or not.| +|Notes|NOTE: Since btcd does not have the wallet integrated to provide payment addresses, btcd must be configured via the `--miningaddr` option to provide which payment addresses to pay created blocks to for this RPC to function.| +|Returns|Nothing| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="sendrawtransaction"/> + +| | | +|---|---| +|Method|sendrawtransaction| +|Parameters|1. signedhex (string, required) serialized, hex-encoded signed transaction<br />2. allowhighfees (boolean, optional, default=false) whether or not to allow insanely high fees| +|Description|Submits the serialized, hex-encoded transaction to the local peer and relays it to the network.| +|Notes|<font color="orange">btcd does not yet implement the `allowhighfees` parameter, so it has no effect</font>| +|Returns|`"hash" (string) the hash of the transaction`| +|Example Return|`"1697a19cede08694278f19584e8dcc87945f40c6b59a942dd8906f133ad3f9cc"`| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="submitblock"/> + +| | | +|---|---| +|Method|submitblock| +|Parameters|1. data (string, required) serialized, hex-encoded block<br />2. params (json object, optional, default=nil) this parameter is currently ignored| +|Description|Attempts to submit a new serialized, hex-encoded block to the network.| +|Returns (success)|Success: Nothing<br />Failure: `"rejected: reason"` (string)| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="stop"/> + +| | | +|---|---| +|Method|stop| +|Parameters|None| +|Description|Shutdown btcd.| +|Returns|`"btcd stopping."` (string)| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="validateaddress"/> + +| | | +|---|---| +|Method|validateaddress| +|Parameters|1. address (string, required) - bitcoin address| +|Description|Verify an address is valid.| +|Returns|`{ (json object)`<br /> `"isvalid": true or false, (bool) whether or not the address is valid.`<br /> `"address": "bitcoinaddress", (string) the bitcoin address validated.`<br />}| +[Return to Overview](#MethodOverview)<br /> + +*** +<a name="verifychain"/> + +| | | +|---|---| +|Method|verifychain| +|Parameters|1. checklevel (numeric, optional, default=3) - how in-depth the verification is (0=least amount of checks, higher levels are clamped to the highest supported level)<br />2. numblocks (numeric, optional, default=288) - the number of blocks starting from the end of the chain to verify| +|Description|Verifies the block chain database.<br />The actual checks performed by the `checklevel` parameter is implementation specific. For btcd this is:<br />`checklevel=0` - Look up each block and ensure it can be loaded from the database.<br />`checklevel=1` - Perform basic context-free sanity checks on each block.| +|Notes|<font color="orange">Btcd currently only supports `checklevel` 0 and 1, but the default is still 3 for compatibility. Per the information in the Parameters section above, higher levels are automatically clamped to the highest supported level, so this means the default is effectively 1 for btcd.</font>| +|Returns|`true` or `false` (boolean)| +|Example Return|`true`| +[Return to Overview](#MethodOverview)<br /> + + +<a name="ExtensionMethods" /> +### 6. Extension Methods + +<a name="ExtMethodOverview" /> +**6.1 Method Overview**<br /> + +The following is an overview of the RPC methods which are implemented by btcd, but not the original bitcoind client. Click the method name for further details such as parameter and return information. + +|#|Method|Safe for limited user?|Description| +|---|------|----------|-----------| +|1|[debuglevel](#debuglevel)|N|Dynamically changes the debug logging level.| +|2|[getbestblock](#getbestblock)|Y|Get block height and hash of best block in the main chain.|None| +|3|[getcurrentnet](#getcurrentnet)|Y|Get bitcoin network btcd is running on.|None| +|4|[searchrawtransactions](#searchrawtransactions)|Y|Query for transactions related to a particular address.|None| +|5|[node](#node)|N|Attempts to add or remove a peer. |None| +|6|[generate](#generate)|N|When in simnet or regtest mode, generate a set number of blocks. |None| + + +<a name="ExtMethodDetails" /> +**6.2 Method Details**<br /> + +<a name="debuglevel"/> + +| | | +|---|---| +|Method|debuglevel| +|Parameters|1. _levelspec_ (string)| +|Description|Dynamically changes the debug logging level.<br />The levelspec can either a debug level or of the form `<subsystem>=<level>,<subsystem2>=<level2>,...`<br />The valid debug levels are `trace`, `debug`, `info`, `warn`, `error`, and `critical`.<br />The valid subsystems are `AMGR`, `ADXR`, `BCDB`, `BMGR`, `BTCD`, `CHAN`, `DISC`, `PEER`, `RPCS`, `SCRP`, `SRVR`, and `TXMP`.<br />Additionally, the special keyword `show` can be used to get a list of the available subsystems.| +|Returns|string| +|Example Return|`Done.`| +|Example `show` Return|`Supported subsystems [AMGR ADXR BCDB BMGR BTCD CHAN DISC PEER RPCS SCRP SRVR TXMP]`| +[Return to Overview](#ExtMethodOverview)<br /> + +*** + +<a name="getbestblock"/> + +| | | +|---|---| +|Method|getbestblock| +|Parameters|None| +|Description|Get block height and hash of best block in the main chain.| +|Returns|`{ (json object)`<br /> `"hash": "data", (string) the hex-encoded bytes of the best block hash`<br /> `"height": n (numeric) the block height of the best block`<br />`}`| +[Return to Overview](#ExtMethodOverview)<br /> + +*** + +<a name="getcurrentnet"/> + +| | | +|---|---| +|Method|getcurrentnet| +|Parameters|None| +|Description|Get bitcoin network btcd is running on.| +|Returns|numeric| +|Example Return|`3652501241` (mainnet)<br />`118034699` (testnet3)| +[Return to Overview](#ExtMethodOverview)<br /> + +*** + +<a name="searchrawtransactions"/> + +| | | +|---|---| +|Method|searchrawtransactions| +|Parameters|1. address (string, required) - bitcoin address <br /> 2. verbose (int, optional, default=true) - specifies the transaction is returned as a JSON object instead of hex-encoded string <br />3. skip (int, optional, default=0) - the number of leading transactions to leave out of the final response <br /> 4. count (int, optional, default=100) - the maximum number of transactions to return <br /> 5. vinextra (int, optional, default=0) - Specify that extra data from previous output will be returned in vin <br /> 6. reverse (boolean, optional, default=false) - Specifies that the transactions should be returned in reverse chronological order| +|Description|Returns raw data for transactions involving the passed address. Returned transactions are pulled from both the database, and transactions currently in the mempool. Transactions pulled from the mempool will have the `"confirmations"` field set to 0. Usage of this RPC requires the optional `--addrindex` flag to be activated, otherwise all responses will simply return with an error stating the address index has not yet been built up. Similarly, until the address index has caught up with the current best height, all requests will return an error response in order to avoid serving stale data.| +|Returns (verbose=0)|`[ (json array of strings)` <br/> `"serializedtx", ... hex-encoded bytes of the serialized transaction` <br/>`]` | +|Returns (verbose=1)|`[ (array of json objects)` <br/> `{ (json object)`<br /> `"hex": "data", (string) hex-encoded transaction`<br /> `"txid": "hash", (string) the hash of the transaction`<br /> `"version": n, (numeric) the transaction version`<br /> `"locktime": n, (numeric) the transaction lock time`<br /> `"vin": [ (array of json objects) the transaction inputs as json objects`<br /> <font color="orange">For coinbase transactions:</font><br /> `{ (json object)`<br /> `"coinbase": "data", (string) the hex-encoded bytes of the signature script`<br /> `"sequence": n, (numeric) the script sequence number`<br /> `}`<br /> <font color="orange">For non-coinbase transactions:</font><br /> `{ (json object)`<br /> `"txid": "hash", (string) the hash of the origin transaction`<br /> `"vout": n, (numeric) the index of the output being redeemed from the origin transaction`<br /> `"scriptSig": { (json object) the signature script used to redeem the origin transaction`<br /> `"asm": "asm", (string) disassembly of the script`<br /> `"hex": "data", (string) hex-encoded bytes of the script`<br /> `}`<br /> `"prevOut": { (json object) Data from the origin transaction output with index vout.`<br /> `"addresses": ["value",...], (array of string) previous output addresses`<br /> `"value": n.nnn, (numeric) previous output value`<br /> `}`<br /> `"sequence": n, (numeric) the script sequence number`<br /> `}, ...`<br /> `]`<br /> `"vout": [ (array of json objects) the transaction outputs as json objects`<br /> `{ (json object)`<br /> `"value": n, (numeric) the value in BTC`<br /> `"n": n, (numeric) the index of this transaction output`<br /> `"scriptPubKey": { (json object) the public key script used to pay coins`<br /> `"asm": "asm", (string) disassembly of the script`<br /> `"hex": "data", (string) hex-encoded bytes of the script`<br /> `"reqSigs": n, (numeric) the number of required signatures`<br /> `"type": "scripttype" (string) the type of the script (e.g. 'pubkeyhash')`<br /> `"addresses": [ (json array of string) the bitcoin addresses associated with this output`<br /> `"address", (string) the bitcoin address`<br /> `...`<br /> `]`<br /> `}`<br /> `}, ...`<br /> `]`<br /> `"blockhash":"hash" Hash of the block the transaction is part of.` <br /> `"confirmations":n, Number of numeric confirmations of block.` <br /> `"time":t, Transaction time in seconds since the epoch.` <br /> `"blocktime":t, Block time in seconds since the epoch.`<br />`},...`<br/> `]`| +[Return to Overview](#ExtMethodOverview)<br /> + +*** + +<a name="node"/> + +| | | +|---|---| +|Method|node| +|Parameters|1. command (string, required) - `connect` to add a peer (defaults to temporary), `remove` to remove a persistent peer, or `disconnect` to remove all matching non-persistent peers <br /> 2. peer (string, required) - ip address and port, or ID of the peer to operate on<br /> 3. connection type (string, optional) - `perm` indicates the peer should be added as a permanent peer, `temp` indicates a connection should only be attempted once. | +|Description|Attempts to add or remove a peer.| +|Returns|Nothing| +[Return to Overview](#MethodOverview)<br /> + +*** + +<a name="generate"/> + +| | | +|---|---| +|Method|generate| +|Parameters|1. numblocks (int, required) - The number of blocks to generate | +|Description|When in simnet or regtest mode, generates `numblocks` blocks. If blocks arrive from elsewhere, they are built upon but don't count toward the number of blocks to generate. Only generated blocks are returned. This RPC call will exit with an error if the server is already CPU mining, and will prevent the server from CPU mining for another command while it runs. | +|Returns|`[ (json array of strings)` <br/> `"blockhash", ... hash of the generated block` <br/>`]` | +[Return to Overview](#MethodOverview)<br /> + +*** + +<a name="WSExtMethods" /> +### 7. Websocket Extension Methods (Websocket-specific) + +<a name="WSExtMethodOverview" /> +**7.1 Method Overview**<br /> + +The following is an overview of the RPC method requests available exclusively to Websocket clients. All of these RPC methods are available to the limited +user. Click the method name for further details such as parameter and return information. + +|#|Method|Description|Notifications| +|---|------|-----------|-------------| +|1|[authenticate](#authenticate)|Authenticate the connection against the username and passphrase configured for the RPC server.<br /><font color="orange">NOTE: This is only required if an HTTP Authorization header is not being used.</font>|None| +|2|[notifyblocks](#notifyblocks)|Send notifications when a block is connected or disconnected from the best chain.|[blockconnected](#blockconnected) and [blockdisconnected](#blockdisconnected)| +|3|[stopnotifyblocks](#stopnotifyblocks)|Cancel registered notifications for whenever a block is connected or disconnected from the main (best) chain. |None| +|4|[notifyreceived](#notifyreceived)|Send notifications when a txout spends to an address.|[recvtx](#recvtx) and [redeemingtx](#redeemingtx)| +|5|[stopnotifyreceived](#stopnotifyreceived)|Cancel registered notifications for when a txout spends to any of the passed addresses.|None| +|6|[notifyspent](#notifyspent)|Send notification when a txout is spent.|[redeemingtx](#redeemingtx)| +|7|[stopnotifyspent](#stopnotifyspent)|Cancel registered spending notifications for each passed outpoint.|None| +|8|[rescan](#rescan)|Rescan block chain for transactions to addresses and spent transaction outpoints.|[recvtx](#recvtx), [redeemingtx](#redeemingtx), [rescanprogress](#rescanprogress), and [rescanfinished](#rescanfinished) | +|9|[notifynewtransactions](#notifynewtransactions)|Send notifications for all new transactions as they are accepted into the mempool.|[txaccepted](#txaccepted) or [txacceptedverbose](#txacceptedverbose)| +|10|[stopnotifynewtransactions](#stopnotifynewtransactions)|Stop sending either a txaccepted or a txacceptedverbose notification when a new transaction is accepted into the mempool.|None| +|11|[session](#session)|Return details regarding a websocket client's current connection.|None| + +<a name="WSExtMethodDetails" /> +**7.2 Method Details**<br /> + +<a name="authenticate"/> + +| | | +|---|---| +|Method|authenticate| +|Parameters|1. username (string, required)<br />2. passphrase (string, required)| +|Description|Authenticate the connection against the username and password configured for the RPC server.<br /> Invoking any other method before authenticating with this command will close the connection.<br /><font color="orange">NOTE: This is only required if an HTTP Authorization header is not being used.</font>| +|Returns|Success: Nothing<br />Failure: Nothing (websocket disconnected)| +[Return to Overview](#WSExtMethodOverview)<br /> + +*** + +<a name="notifyblocks"/> + +| | | +|---|---| +|Method|notifyblocks| +|Notifications|[blockconnected](#blockconnected) and [blockdisconnected](#blockdisconnected)| +|Parameters|None| +|Description|Request notifications for whenever a block is connected or disconnected from the main (best) chain.<br />NOTE: If a client subscribes to both block and transaction (recvtx and redeemingtx) notifications, the blockconnected notification will be sent after all transaction notifications have been sent. This allows clients to know when all relevant transactions for a block have been received.| +|Returns|Nothing| +[Return to Overview](#WSExtMethodOverview)<br /> + +*** +<a name="stopnotifyblocks"/> + +| | | +|---|---| +|Method|stopnotifyblocks| +|Notifications|None| +|Parameters|None| +|Description|Cancel sending notifications for whenever a block is connected or disconnected from the main (best) chain.| +|Returns|Nothing| +[Return to Overview](#WSExtMethodOverview)<br /> + +*** + +<a name="notifyreceived"/> + +| | | +|---|---| +|Method|notifyreceived| +|Notifications|[recvtx](#recvtx) and [redeemingtx](#redeemingtx)| +|Parameters|1. Addresses (JSON array, required)<br /> `[ (json array of strings)`<br /> `"bitcoinaddress", (string) the bitcoin address`<br /> `...`<br /> `]`| +|Description|Send a recvtx notification when a transaction added to mempool or appears in a newly-attached block contains a txout pkScript sending to any of the passed addresses. Matching outpoints are automatically registered for redeemingtx notifications.| +|Returns|Nothing| +[Return to Overview](#WSExtMethodOverview)<br /> + +*** + +<a name="stopnotifyreceived"/> + +| | | +|---|---| +|Method|stopnotifyreceived| +|Notifications|None| +|Parameters|1. Addresses (JSON array, required)<br /> `[ (json array of strings)`<br /> `"bitcoinaddress", (string) the bitcoin address`<br /> `...`<br /> `]`| +|Description|Cancel registered receive notifications for each passed address.| +|Returns|Nothing| +[Return to Overview](#WSExtMethodOverview)<br /> + +*** + +<a name="notifyspent"/> + +| | | +|---|---| +|Method|notifyspent| +|Notifications|[redeemingtx](#redeemingtx)| +|Parameters|1. Outpoints (JSON array, required)<br /> `[ (JSON array)`<br /> `{ (JSON object)`<br /> `"hash":"data", (string) the hex-encoded bytes of the outpoint hash`<br /> `"index":n (numeric) the txout index of the outpoint`<br /> `},`<br /> `...`<br /> `]`| +|Description|Send a redeemingtx notification when a transaction spending an outpoint appears in mempool (if relayed to this btcd instance) and when such a transaction first appears in a newly-attached block.| +|Returns|Nothing| +[Return to Overview](#WSExtMethodOverview)<br /> + +*** + +<a name="stopnotifyspent"/> + +| | | +|---|---| +|Method|stopnotifyspent| +|Notifications|None| +|Parameters|1. Outpoints (JSON array, required)<br /> `[ (JSON array)`<br /> `{ (JSON object)`<br /> `"hash":"data", (string) the hex-encoded bytes of the outpoint hash`<br /> `"index":n (numeric) the txout index of the outpoint`<br /> `},`<br /> `...`<br /> `]`| +|Description|Cancel registered spending notifications for each passed outpoint.| +|Returns|Nothing| +[Return to Overview](#WSExtMethodOverview)<br /> + +*** + +<a name="rescan"/> + +| | | +|---|---| +|Method|rescan| +|Notifications|[recvtx](#recvtx), [redeemingtx](#redeemingtx), [rescanprogress](#rescanprogress), and [rescanfinished](#rescanfinished)| +|Parameters|1. BeginBlock (string, required) block hash to begin rescanning from<br />2. Addresses (JSON array, required)<br /> `[ (json array of strings)`<br /> `"bitcoinaddress", (string) the bitcoin address`<br /> `...` <br /> `]`<br />3. Outpoints (JSON array, required)<br /> `[ (JSON array)`<br /> `{ (JSON object)`<br /> `"hash":"data", (string) the hex-encoded bytes of the outpoint hash`<br /> `"index":n (numeric) the txout index of the outpoint`<br /> `},`<br /> `...`<br /> `]`<br />4. EndBlock (string, optional) hash of final block to rescan| +|Description|Rescan block chain for transactions to addresses, starting at block BeginBlock and ending at EndBlock. The current known UTXO set for all passed addresses at height BeginBlock should included in the Outpoints argument. If EndBlock is omitted, the rescan continues through the best block in the main chain. Additionally, if no EndBlock is provided, the client is automatically registered for transaction notifications for all rescanned addresses and the final UTXO set. Rescan results are sent as recvtx and redeemingtx notifications. This call returns once the rescan completes.| +|Returns|Nothing| +[Return to Overview](#WSExtMethodOverview)<br /> + +*** + +<a name="notifynewtransactions"/> + +| | | +|---|---| +|Method|notifynewtransactions| +|Notifications|[txaccepted](#txaccepted) or [txacceptedverbose](#txacceptedverbose)| +|Parameters|1. verbose (boolean, optional, default=false) - specifies which type of notification to receive. If verbose is true, then the caller receives [txacceptedverbose](#txacceptedverbose), otherwise the caller receives [txaccepted](#txaccepted)| +|Description|Send either a [txaccepted](#txaccepted) or a [txacceptedverbose](#txacceptedverbose) notification when a new transaction is accepted into the mempool.| +|Returns|Nothing| +[Return to Overview](#WSExtMethodOverview)<br /> + +*** + +<a name="stopnotifynewtransactions"/> + +| | | +|---|---| +|Method|stopnotifynewtransactions| +|Notifications|None| +|Parameters|None| +|Description|Stop sending either a [txaccepted](#txaccepted) or a [txacceptedverbose](#txacceptedverbose) notification when a new transaction is accepted into the mempool.| +|Returns|Nothing| +[Return to Overview](#WSExtMethodOverview)<br /> + +*** + +<a name="session"/> + +| | | +|---|---| +|Method|session| +|Notifications|None| +|Parameters|None| +|Description|Return a JSON object with details regarding a websocket client's current connection to the RPC server. This currently only includes the session ID, a random unsigned 64-bit integer that is created for each newly connected client. Session IDs may be used to verify that the current connection was not lost and subsequently reestablished.| +|Returns|`{ (json object)`<br /> `"sessionid": n (numeric) the session ID`<br />`}`| +|Example Return|`{`<br /> `"sessionid": 67089679842`<br />`}`| +[Return to Overview](#WSExtMethodOverview)<br /> + + +<a name="Notifications" /> +### 8. Notifications (Websocket-specific) + +btcd uses standard JSON-RPC notifications to notify clients of changes, rather than requiring clients to poll btcd for updates. JSON-RPC notifications are a subset of requests, but do not contain an ID. The notification type is categorized by the `method` field and additional details are sent as a JSON array in the `params` field. + +<a name="NotificationOverview" /> +**8.1 Notification Overview**<br /> + +The following is an overview of the JSON-RPC notifications used for Websocket connections. Click the method name for further details of the context(s) in which they are sent and their parameters. + +|#|Method|Description|Request| +|---|------|-----------|-------| +|1|[blockconnected](#blockconnected)|Block connected to the main chain.|[notifyblocks](#notifyblocks)| +|2|[blockdisconnected](#blockdisconnected)|Block disconnected from the main chain.|[notifyblocks](#notifyblocks)| +|3|[recvtx](#recvtx)|Processed a transaction output spending to a wallet address.|[notifyreceived](#notifyreceived) and [rescan](#rescan)| +|4|[redeemingtx](#redeemingtx)|Processed a transaction that spends a registered outpoint.|[notifyspent](#notifyspent) and [rescan](#rescan)| +|5|[txaccepted](#txaccepted)|Received a new transaction after requesting simple notifications of all new transactions accepted into the mempool.|[notifynewtransactions](#notifynewtransactions)| +|6|[txacceptedverbose](#txacceptedverbose)|Received a new transaction after requesting verbose notifications of all new transactions accepted into the mempool.|[notifynewtransactions](#notifynewtransactions)| +|7|[rescanprogress](#rescanprogress)|A rescan operation that is underway has made progress.|[rescan](#rescan)| +|8|[rescanfinished](#rescanfinished)|A rescan operation has completed.|[rescan](#rescan)| + +<a name="NotificationDetails" /> +**8.2 Notification Details**<br /> + +<a name="blockconnected"/> + +| | | +|---|---| +|Method|blockconnected| +|Request|[notifyblocks](#notifyblocks)| +|Parameters|1. BlockHash (string) hex-encoded bytes of the attached block hash<br />2. BlockHeight (numeric) height of the attached block<br />3. BlockTime (numeric) unix time of the attached block| +|Description|Notifies when a block has been added to the main chain. Notification is sent to all connected clients.| +|Example|Example blockconnected notification for mainnet block 280330 (newlines added for readability):<br />`{`<br /> `"jsonrpc": "1.0",`<br /> `"method": "blockconnected",`<br /> `"params":`<br /> `[`<br /> `"000000000000000004cbdfe387f4df44b914e464ca79838a8ab777b3214dbffd",`<br /> `280330,`<br /> `1389636265`<br /> `],`<br /> `"id": null`<br />`}`| +[Return to Overview](#NotificationOverview)<br /> + +*** + +<a name="blockdisconnected"/> + +| | | +|---|---| +|Method|blockdisconnected| +|Request|[notifyblocks](#notifyblocks)| +|Parameters|1. BlockHash (string) hex-encoded bytes of the disconnected block hash<br />2. BlockHeight (numeric) height of the disconnected block<br />3. BlockTime (numeric) unix time of the disconnected block| +|Description|Notifies when a block has been removed from the main chain. Notification is sent to all connected clients.| +|Example|Example blockdisconnected notification for mainnet block 280330 (newlines added for readability):<br />`{`<br /> `"jsonrpc": "1.0",`<br /> `"method": "blockdisconnected",`<br /> `"params":`<br /> `[`<br /> `"000000000000000004cbdfe387f4df44b914e464ca79838a8ab777b3214dbffd",`<br /> `280330,`<br /> `1389636265`<br /> `],`<br /> `"id": null`<br />`}`| +[Return to Overview](#NotificationOverview)<br /> + +*** + +<a name="recvtx"/> + +| | | +|---|---| +|Method|recvtx| +|Request|[rescan](#rescan) or [notifyreceived](#notifyreceived)| +|Parameters|1. Transaction (string) full transaction encoded as a hex string<br />2. Block details (object, optional) details about a block and the index of the transaction within a block, if the transaction is mined| +|Description|Notifies a client when a transaction is processed that contains at least a single output with a pkScript sending to a requested address. If multiple outputs send to requested addresses, a single notification is sent. If a mempool (unmined) transaction is processed, the block details object (second parameter) is excluded.| +|Example|Example recvtx notification for mainnet transaction 61d3696de4c888730cbe06b0ad8ecb6d72d6108e893895aa9bc067bd7eba3fad when processed by mempool (newlines added for readability):<br />`{`<br /> `"jsonrpc": "1.0",`<br /> `"method": "recvtx",`<br /> `"params":`<br /> `[`<br /> `"010000000114d9ff358894c486b4ae11c2a8cf7851b1df64c53d2e511278eff17c22fb737300000000..."`<br /> `],`<br /> `"id": null`<br />`}`<br />The recvtx notification for the same txout, after the transaction was mined into block 276425:<br />`{`<br /> `"jsonrpc": "1.0",`<br /> `"method": "recvtx",`<br /> `"params":`<br /> `[`<br /> `"010000000114d9ff358894c486b4ae11c2a8cf7851b1df64c53d2e511278eff17c22fb737300000000...",`<br /> `{`<br /> `"height": 276425,`<br /> `"hash": "000000000000000325474bb799b9e591f965ca4461b72cb7012b808db92bb2fc",`<br /> `"index": 684,`<br /> `"time": 1387737310`<br /> `}`<br /> `],`<br /> `"id": null`<br />`}`| +[Return to Overview](#NotificationOverview)<br /> + +*** + +<a name="redeemingtx"/> + +| | | +|---|---| +|Method|redeemingtx| +|Requests|[notifyspent](#notifyspent) and [rescan](#rescan)| +|Parameters|1. Transaction (string) full transaction encoded as a hex string<br />2. Block details (object, optional) details about a block and the index of the transaction within a block, if the transaction is mined| +|Description|Notifies a client when an registered outpoint is spent by a transaction accepted to mempool and/or mined into a block.| +|Example|Example redeemingtx notification for mainnet outpoint 61d3696de4c888730cbe06b0ad8ecb6d72d6108e893895aa9bc067bd7eba3fad:0 after being spent by transaction 4ad0c16ac973ff675dec1f3e5f1273f1c45be2a63554343f21b70240a1e43ece (newlines added for readability):<br />`{`<br /> `"jsonrpc": "1.0",`<br /> `"method": "redeemingtx",`<br /> `"params":`<br /> `[`<br /> `"0100000003ad3fba7ebd67c09baa9538898e10d6726dcb8eadb006be0c7388c8e46d69d3610000000..."`<br /> `],`<br /> `"id": null`<br />`}`<br />The redeemingtx notification for the same txout, after the spending transaction was mined into block 279143:<br />`{`<br /> `"jsonrpc": "1.0",`<br /> `"method": "recvtx",`<br /> `"params":`<br /> `[`<br /> `"0100000003ad3fba7ebd67c09baa9538898e10d6726dcb8eadb006be0c7388c8e46d69d3610000000...",`<br /> `{`<br /> `"height": 279143,`<br /> `"hash": "00000000000000017188b968a371bab95aa43522665353b646e41865abae02a4",`<br /> `"index": 6,`<br /> `"time": 1389115004`<br /> `}`<br /> `],`<br /> `"id": null`<br />`}`| +[Return to Overview](#NotificationOverview)<br /> + +*** + +<a name="txaccepted"/> + +| | | +|---|---| +|Method|txaccepted| +|Request|[notifynewtransactions](#notifynewtransactions)| +|Parameters|1. TxSha (string) hex-encoded bytes of the transaction hash<br />2. Amount (numeric) sum of the value of all the transaction outpoints| +|Description|Notifies when a new transaction has been accepted and the client has requested standard transaction details.| +|Example|Example txaccepted notification for mainnet transaction id "16c54c9d02fe570b9d41b518c0daefae81cc05c69bbe842058e84c6ed5826261" (newlines added for readability):<br />`{`<br /> `"jsonrpc": "1.0",`<br /> `"method": "txaccepted",`<br /> `"params":`<br /> `[`<br /> `"16c54c9d02fe570b9d41b518c0daefae81cc05c69bbe842058e84c6ed5826261",`<br /> `55838384`<br /> `],`<br /> `"id": null`<br />`}`| +[Return to Overview](#NotificationOverview)<br /> + +*** + +<a name="txacceptedverbose"/> + +| | | +|---|---| +|Method|txacceptedverbose| +|Request|[notifynewtransactions](#notifynewtransactions)| +|Parameters|1. RawTx (json object) the transaction as a json object (see getrawtransaction json object details)| +|Description|Notifies when a new transaction has been accepted and the client has requested verbose transaction details.| +|Example|Example txacceptedverbose notification (newlines added for readability):<br />`{`<br /> `"jsonrpc": "1.0",`<br /> `"method": "txacceptedverbose",`<br /> `"params":`<br /> `[`<br /> `{`<br /> `"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000f...",`<br /> `"txid": "90743aad855880e517270550d2a881627d84db5265142fd1e7fb7add38b08be9",`<br /> `"version": 1,`<br /> `"locktime": 0,`<br /> `"vin": [`<br /> <font color="orange">For coinbase transactions:</font><br /> `{ (json object)`<br /> `"coinbase": "03708203062f503253482f04066d605108f800080100000ea2122f6f7a636f696e4065757374726174756d2f",`<br /> `"sequence": 0,`<br /> `}`<br /> <font color="orange">For non-coinbase transactions:</font><br /> `{`<br /> `"txid": "60ac4b057247b3d0b9a8173de56b5e1be8c1d1da970511c626ef53706c66be04",`<br /> `"vout": 0,`<br /> `"scriptSig": {`<br /> `"asm": "3046022100cb42f8df44eca83dd0a727988dcde9384953e830b1f8004d57485e2ede1b9c8f0...",`<br /> `"hex": "493046022100cb42f8df44eca83dd0a727988dcde9384953e830b1f8004d57485e2ede1b9c8...",`<br /> `}`<br /> `"sequence": 4294967295,`<br /> `}`<br /> `],`<br /> `"vout": [`<br /> `{`<br /> `"value": 25.1394,`<br /> `"n": 0,`<br /> `"scriptPubKey": {`<br /> `"asm": "OP_DUP OP_HASH160 ea132286328cfc819457b9dec386c4b5c84faa5c OP_EQUALVERIFY OP_CHECKSIG",`<br /> `"hex": "76a914ea132286328cfc819457b9dec386c4b5c84faa5c88ac",`<br /> `"reqSigs": 1,`<br /> `"type": "pubkeyhash"`<br /> `"addresses": [`<br /> `"1NLg3QJMsMQGM5KEUaEu5ADDmKQSLHwmyh",`<br /> `]`<br /> `}`<br /> `]`<br /> `}`<br /> `],`<br /> `"id": null`<br />`}`| +[Return to Overview](#NotificationOverview)<br /> + +*** + +<a name="rescanprogress"/> + +| | | +|---|---| +|Method|rescanprogress| +|Request|[rescan](#rescan)| +|Parameters|1. Hash (string) hash of the last processed block<br />2. Height (numeric) height of the last processed block<br />3. Time (numeric) UNIX time of the last processed block| +|Description|Notifies a client with the current progress at periodic intervals when a long-running [rescan](#rescan) is underway.| +|Example|`{`<br /> `"jsonrpc": "1.0",`<br /> `"method": "rescanprogress",`<br /> `"params":`<br /> `[`<br /> `"0000000000000ea86b49e11843b2ad937ac89ae74a963c7edd36e0147079b89d",`<br /> `127213,`<br /> `1306533807`<br /> `],`<br /> `"id": null`<br />`}`| +[Return to Overview](#NotificationOverview)<br /> + +*** + +<a name="rescanfinished"/> + +| | | +|---|---| +|Method|rescanfinished| +|Request|[rescan](#rescan)| +|Parameters|1. Hash (string) hash of the last rescanned block<br />2. Height (numeric) height of the last rescanned block<br />3. Time (numeric) UNIX time of the last rescanned block | +|Description|Notifies a client that the [rescan](#rescan) has completed and no further notifications will be sent.| +|Example|`{`<br /> `"jsonrpc": "1.0",`<br /> `"method": "rescanfinished",`<br /> `"params":`<br /> `[`<br /> `"0000000000000ea86b49e11843b2ad937ac89ae74a963c7edd36e0147079b89d",`<br /> `127213,`<br /> `1306533807`<br /> `],`<br /> `"id": null`<br />`}`| +[Return to Overview](#NotificationOverview)<br /> + + +<a name="ExampleCode" /> +### 9. Example Code + +This section provides example code for interacting with the JSON-RPC API in +various languages. + +* [Go](#ExampleGoApp) +* [node.js](#ExampleNodeJsCode) + +<a name="ExampleGoApp" /> +**9.1 Go** + +This section provides examples of using the RPC interface using Go and the +[btcrpcclient](https://github.com/btcsuite/btcrpcclient) package. + +* [Using getblockcount to Retrieve the Current Block Height](#ExampleGetBlockCount) +* [Using getblock to Retrieve the Genesis Block](#ExampleGetBlock) +* [Using notifyblocks to Receive blockconnected and blockdisconnected Notifications (Websocket-specific)](#ExampleNotifyBlocks) + + +<a name="ExampleGetBlockCount" /> +**9.1.1 Using getblockcount to Retrieve the Current Block Height**<br /> + +The following is an example Go application which uses the +[btcrpcclient](https://github.com/btcsuite/btcrpcclient) package to connect with +a btcd instance via Websockets, issues [getblockcount](#getblockcount) to +retrieve the current block height, and displays it. + +```Go +package main + +import ( + "github.com/btcsuite/btcrpcclient" + "github.com/btcsuite/btcutil" + "io/ioutil" + "log" + "path/filepath" +) + +func main() { + // Load the certificate for the TLS connection which is automatically + // generated by btcd when it starts the RPC server and doesn't already + // have one. + btcdHomeDir := btcutil.AppDataDir("btcd", false) + certs, err := ioutil.ReadFile(filepath.Join(btcdHomeDir, "rpc.cert")) + if err != nil { + log.Fatal(err) + } + + // Create a new RPC client using websockets. Since this example is + // not long-lived, the connection will be closed as soon as the program + // exits. + connCfg := &btcrpcclient.ConnConfig{ + Host: "localhost:8334", + Endpoint: "ws", + User: "yourrpcuser", + Pass: "yourrpcpass", + Certificates: certs, + } + client, err := btcrpcclient.New(connCfg, nil) + if err != nil { + log.Fatal(err) + } + defer client.Shutdown() + + // Query the RPC server for the current block count and display it. + blockCount, err := client.GetBlockCount() + if err != nil { + log.Fatal(err) + } + log.Printf("Block count: %d", blockCount) +} +``` + +Which results in: + +```bash +Block count: 276978 +``` + +<a name="ExampleGetBlock" /> +**9.1.2 Using getblock to Retrieve the Genesis Block**<br /> + +The following is an example Go application which uses the +[btcrpcclient](https://github.com/btcsuite/btcrpcclient) package to connect with +a btcd instance via Websockets, issues [getblock](#getblock) to retrieve +information about the Genesis block, and display a few details about it. + +```Go +package main + +import ( + "github.com/btcsuite/btcrpcclient" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcd/wire" + "io/ioutil" + "log" + "path/filepath" + "time" +) + +func main() { + // Load the certificate for the TLS connection which is automatically + // generated by btcd when it starts the RPC server and doesn't already + // have one. + btcdHomeDir := btcutil.AppDataDir("btcd", false) + certs, err := ioutil.ReadFile(filepath.Join(btcdHomeDir, "rpc.cert")) + if err != nil { + log.Fatal(err) + } + + // Create a new RPC client using websockets. Since this example is + // not long-lived, the connection will be closed as soon as the program + // exits. + connCfg := &btcrpcclient.ConnConfig{ + Host: "localhost:18334", + Endpoint: "ws", + User: "yourrpcuser", + Pass: "yourrpcpass", + Certificates: certs, + } + client, err := btcrpcclient.New(connCfg, nil) + if err != nil { + log.Fatal(err) + } + defer client.Shutdown() + + // Query the RPC server for the genesis block using the "getblock" + // command with the verbose flag set to true and the verboseTx flag + // set to false. + genesisHashStr := "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" + blockHash, err := wire.NewShaHashFromStr(genesisHashStr) + if err != nil { + log.Fatal(err) + } + block, err := client.GetBlockVerbose(blockHash, false) + if err != nil { + log.Fatal(err) + } + + // Display some details about the returned block. + log.Printf("Hash: %v\n", block.Hash) + log.Printf("Previous Block: %v\n", block.PreviousHash) + log.Printf("Next Block: %v\n", block.NextHash) + log.Printf("Merkle root: %v\n", block.MerkleRoot) + log.Printf("Timestamp: %v\n", time.Unix(block.Time, 0).UTC()) + log.Printf("Confirmations: %v\n", block.Confirmations) + log.Printf("Difficulty: %f\n", block.Difficulty) + log.Printf("Size (in bytes): %v\n", block.Size) + log.Printf("Num transactions: %v\n", len(block.Tx)) +} +``` + +Which results in: + +```bash +Hash: 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f +Previous Block: 0000000000000000000000000000000000000000000000000000000000000000 +Next Block: 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048 +Merkle root: 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b +Timestamp: 2009-01-03 18:15:05 +0000 UTC +Confirmations: 277290 +Difficulty: 1.000000 +Size (in bytes): 285 +Num transactions: 1 +``` + +<a name="ExampleNotifyBlocks" /> +**9.1.3 Using notifyblocks to Receive blockconnected and blockdisconnected +Notifications (Websocket-specific)**<br /> + +The following is an example Go application which uses the +[btcrpcclient](https://github.com/btcsuite/btcrpcclient) package to connect with +a btcd instance via Websockets and registers for +[blockconnected](#blockconnected) and [blockdisconnected](#blockdisconnected) +notifications with [notifyblocks](#notifyblocks). It also sets up handlers for +the notifications. + +```Go +package main + +import ( + "github.com/btcsuite/btcrpcclient" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcd/wire" + "io/ioutil" + "log" + "path/filepath" + "time" +) + +func main() { + // Setup handlers for blockconnected and blockdisconnected + // notifications. + ntfnHandlers := btcrpcclient.NotificationHandlers{ + OnBlockConnected: func(hash *wire.ShaHash, height int32) { + log.Printf("Block connected: %v (%d)", hash, height) + }, + OnBlockDisconnected: func(hash *wire.ShaHash, height int32) { + log.Printf("Block disconnected: %v", hash, height) + }, + } + + // Load the certificate for the TLS connection which is automatically + // generated by btcd when it starts the RPC server and doesn't already + // have one. + btcdHomeDir := btcutil.AppDataDir("btcd", false) + certs, err := ioutil.ReadFile(filepath.Join(btcdHomeDir, "rpc.cert")) + if err != nil { + log.Fatal(err) + } + + // Create a new RPC client using websockets. + connCfg := &btcrpcclient.ConnConfig{ + Host: "localhost:8334", + Endpoint: "ws", + User: "yourrpcuser", + Pass: "yourrpcpass", + Certificates: certs, + } + client, err := btcrpcclient.New(connCfg, &ntfnHandlers) + if err != nil { + log.Fatal(err) + } + + // Register for blockconnected and blockdisconneted notifications. + if err := client.NotifyBlocks(); err != nil { + client.Shutdown() + log.Fatal(err) + } + + // For this example, gracefully shutdown the client after 10 seconds. + // Ordinarily when to shutdown the client is highly application + // specific. + log.Println("Client shutdown in 10 seconds...") + time.AfterFunc(time.Second*10, func() { + log.Println("Client shutting down...") + client.Shutdown() + log.Println("Client shutdown complete.") + }) + + // Wait until the client either shuts down gracefully (or the user + // terminates the process with Ctrl+C). + client.WaitForShutdown() +} +``` + +Example output: + +``` +2014/05/12 20:33:17 Client shutdown in 10 seconds... +2014/05/12 20:33:19 Block connected: 000000000000000007dff1f95f7b3f5eac2892a4123069517caf34e2c417650d (300461) +2014/05/12 20:33:27 Client shutting down... +2014/05/12 20:31:27 Client shutdown complete. +``` + +<a name="ExampleNodeJsCode" /> +### 9.2. Example node.js Code + +<a name="ExampleNotifyBlocks" /> +**9.2.1 Using notifyblocks to be Notified of Block Connects and Disconnects**<br /> + +The following is example node.js code which uses [ws](https://github.com/einaros/ws) +(can be installed with `npm install ws`) to connect with a btcd instance, +issues [notifyblocks](#notifyblocks) to register for +[blockconnected](#blockconnected) and [blockdisconnected](#blockdisconnected) +notifications, and displays all incoming messages. + +```javascript +var fs = require('fs'); +var WebSocket = require('ws'); + +// Load the certificate for the TLS connection which is automatically +// generated by btcd when it starts the RPC server and doesn't already +// have one. +var cert = fs.readFileSync('/path/to/btcd/appdata/rpc.cert'); +var user = "yourusername"; +var password = "yourpassword"; + + +// Initiate the websocket connection. The btcd generated certificate acts as +// its own certificate authority, so it needs to be specified in the 'ca' array +// for the certificate to properly validate. +var ws = new WebSocket('wss://127.0.0.1:8334/ws', { + headers: { + 'Authorization': 'Basic '+new Buffer(user+':'+password).toString('base64') + }, + cert: cert, + ca: [cert] +}); +ws.on('open', function() { + console.log('CONNECTED'); + // Send a JSON-RPC command to be notified when blocks are connected and + // disconnected from the chain. + ws.send('{"jsonrpc":"1.0","id":"0","method":"notifyblocks","params":[]}'); +}); +ws.on('message', function(data, flags) { + console.log(data); +}); +ws.on('error', function(derp) { + console.log('ERROR:' + derp); +}) +ws.on('close', function(data) { + console.log('DISCONNECTED'); +}) +``` diff --git a/vendor/github.com/btcsuite/btcd/docs/using_bootstrap_dat.md b/vendor/github.com/btcsuite/btcd/docs/using_bootstrap_dat.md new file mode 100644 index 0000000000000000000000000000000000000000..d508831de3b070d8049b34e0f9abee8d03e83acc --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/docs/using_bootstrap_dat.md @@ -0,0 +1,74 @@ +### Table of Contents +1. [What is bootstrap.dat?](#What)<br /> +2. [What are the pros and cons of using bootstrap.dat?](#ProsCons) +3. [Where do I get bootstrap.dat?](#Obtaining) +4. [How do I know I can trust the bootstrap.dat I downloaded?](#Trust) +5. [How do I use bootstrap.dat with btcd?](#Importing) + +<a name="What" /> +### 1. What is bootstrap.dat? + +It is a flat, binary file containing bitcoin blockchain data starting from the +genesis block and continuing through a relatively recent block height depending +on the last time it was updated. + +See [this](https://bitcointalk.org/index.php?topic=145386.0) thread on +bitcointalk for more details. + +**NOTE:** Using bootstrap.dat is entirely optional. Btcd will download the +block chain from other peers through the Bitcoin protocol with no extra +configuration needed. + +<a name="ProsCons" /> +### 2. What are the pros and cons of using bootstrap.dat? + +Pros: +- Typically accelerates the initial process of bringing up a new node as it + downloads from public P2P nodes and generally is able to achieve faster + download speeds +- It is particularly beneficial when bringing up multiple nodes as you only need + to download the data once + +Cons: +- Requires you to setup and configure a torrent client if you don't already have + one available +- Requires roughly twice as much disk space since you'll need the flat file as + well as the imported database + +<a name="Obtaining" /> +### 3. Where do I get bootstrap.dat? + +The bootstrap.dat file is made available via a torrent. See +[this](https://bitcointalk.org/index.php?topic=145386.0) thread on bitcointalk +for the torrent download details. + +<a name="Trust" /> +### 4. How do I know I can trust the bootstrap.dat I downloaded? + +You don't need to trust the file as the `addblock` utility verifies every block +using the same rules that are used when downloading the block chain normally +through the Bitcoin protocol. Additionally, the chain rules contain hard-coded +checkpoints for the known-good block chain at periodic intervals. This ensures +that not only is it a valid chain, but it is the same chain that everyone else +is using. + +<a name="Importing" /> +### 5. How do I use bootstrap.dat with btcd? + +btcd comes with a separate utility named `addblock` which can be used to import +`bootstrap.dat`. This approach is used since the import is a one-time operation +and we prefer to keep the daemon itself as lightweight as possible. + +1. Stop btcd if it is already running. This is required since addblock needs to + access the database used by btcd and it will be locked if btcd is using it. +2. Note the path to the downloaded bootstrap.dat file. +3. Run the addblock utility with the `-i` argument pointing to the location of + boostrap.dat:<br /><br /> +**Windows:** +```bat +C:\> "%PROGRAMFILES%\Btcd Suite\Btcd\addblock" -i C:\Path\To\bootstrap.dat +``` +**Linux/Unix/BSD/POSIX:** +```bash +$ $GOPATH/bin/addblock -i /path/to/bootstrap.dat +``` diff --git a/vendor/github.com/btcsuite/btcd/dynamicbanscore.go b/vendor/github.com/btcsuite/btcd/dynamicbanscore.go new file mode 100644 index 0000000000000000000000000000000000000000..786c7b4071cd5664c39608704e98c121b083586e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/dynamicbanscore.go @@ -0,0 +1,146 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "math" + "sync" + "time" +) + +const ( + // Halflife defines the time (in seconds) by which the transient part + // of the ban score decays to one half of it's original value. + Halflife = 60 + + // lambda is the decaying constant. + lambda = math.Ln2 / Halflife + + // Lifetime defines the maximum age of the transient part of the ban + // score to be considered a non-zero score (in seconds). + Lifetime = 1800 + + // precomputedLen defines the amount of decay factors (one per second) that + // should be precomputed at initialization. + precomputedLen = 64 +) + +// precomputedFactor stores precomputed exponential decay factors for the first +// 'precomputedLen' seconds starting from t == 0. +var precomputedFactor [precomputedLen]float64 + +// init precomputes decay factors. +func init() { + for i := range precomputedFactor { + precomputedFactor[i] = math.Exp(-1.0 * float64(i) * lambda) + } +} + +// decayFactor returns the decay factor at t seconds, using precalculated values +// if available, or calculating the factor if needed. +func decayFactor(t int64) float64 { + if t < precomputedLen { + return precomputedFactor[t] + } + return math.Exp(-1.0 * float64(t) * lambda) +} + +// dynamicBanScore provides dynamic ban scores consisting of a persistent and a +// decaying component. The persistent score could be utilized to create simple +// additive banning policies similar to those found in other bitcoin node +// implementations. +// +// The decaying score enables the creation of evasive logic which handles +// misbehaving peers (especially application layer DoS attacks) gracefully +// by disconnecting and banning peers attempting various kinds of flooding. +// dynamicBanScore allows these two approaches to be used in tandem. +// +// Zero value: Values of type dynamicBanScore are immediately ready for use upon +// declaration. +type dynamicBanScore struct { + lastUnix int64 + transient float64 + persistent uint32 + sync.Mutex +} + +// String returns the ban score as a human-readable string. +func (s *dynamicBanScore) String() string { + s.Lock() + r := fmt.Sprintf("persistent %v + transient %v at %v = %v as of now", + s.persistent, s.transient, s.lastUnix, s.Int()) + s.Unlock() + return r +} + +// Int returns the current ban score, the sum of the persistent and decaying +// scores. +// +// This function is safe for concurrent access. +func (s *dynamicBanScore) Int() uint32 { + s.Lock() + r := s.int(time.Now()) + s.Unlock() + return r +} + +// Increase increases both the persistent and decaying scores by the values +// passed as parameters. The resulting score is returned. +// +// This function is safe for concurrent access. +func (s *dynamicBanScore) Increase(persistent, transient uint32) uint32 { + s.Lock() + r := s.increase(persistent, transient, time.Now()) + s.Unlock() + return r +} + +// Reset set both persistent and decaying scores to zero. +// +// This function is safe for concurrent access. +func (s *dynamicBanScore) Reset() { + s.Lock() + s.persistent = 0 + s.transient = 0 + s.lastUnix = 0 + s.Unlock() +} + +// int returns the ban score, the sum of the persistent and decaying scores at a +// given point in time. +// +// This function is not safe for concurrent access. It is intended to be used +// internally and during testing. +func (s *dynamicBanScore) int(t time.Time) uint32 { + dt := t.Unix() - s.lastUnix + if s.transient < 1 || dt < 0 || Lifetime < dt { + return s.persistent + } + return s.persistent + uint32(s.transient*decayFactor(dt)) +} + +// increase increases the persistent, the decaying or both scores by the values +// passed as parameters. The resulting score is calculated as if the action was +// carried out at the point time represented by the third parameter. The +// resulting score is returned. +// +// This function is not safe for concurrent access. +func (s *dynamicBanScore) increase(persistent, transient uint32, t time.Time) uint32 { + s.persistent += persistent + tu := t.Unix() + dt := tu - s.lastUnix + + if transient > 0 { + if Lifetime < dt { + s.transient = 0 + } else if s.transient > 1 && dt > 0 { + s.transient *= decayFactor(dt) + } + s.transient += float64(transient) + s.lastUnix = tu + } + return s.persistent + uint32(s.transient) +} diff --git a/vendor/github.com/btcsuite/btcd/dynamicbanscore_test.go b/vendor/github.com/btcsuite/btcd/dynamicbanscore_test.go new file mode 100644 index 0000000000000000000000000000000000000000..070bc6aecc1016e6dfdd8096a67e0dbd919de37b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/dynamicbanscore_test.go @@ -0,0 +1,68 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "math" + "testing" + "time" +) + +// TestDynamicBanScoreDecay tests the exponential decay implemented in +// dynamicBanScore. +func TestDynamicBanScoreDecay(t *testing.T) { + var bs dynamicBanScore + base := time.Now() + + r := bs.increase(100, 50, base) + if r != 150 { + t.Errorf("Unexpected result %d after ban score increase.", r) + } + + r = bs.int(base.Add(time.Minute)) + if r != 125 { + t.Errorf("Halflife check failed - %d instead of 125", r) + } + + r = bs.int(base.Add(7 * time.Minute)) + if r != 100 { + t.Errorf("Decay after 7m - %d instead of 100", r) + } +} + +// TestDynamicBanScoreLifetime tests that dynamicBanScore properly yields zero +// once the maximum age is reached. +func TestDynamicBanScoreLifetime(t *testing.T) { + var bs dynamicBanScore + base := time.Now() + + r := bs.increase(0, math.MaxUint32, base) + r = bs.int(base.Add(Lifetime * time.Second)) + if r != 3 { // 3, not 4 due to precision loss and truncating 3.999... + t.Errorf("Pre max age check with MaxUint32 failed - %d", r) + } + r = bs.int(base.Add((Lifetime + 1) * time.Second)) + if r != 0 { + t.Errorf("Zero after max age check failed - %d instead of 0", r) + } +} + +// TestDynamicBanScore tests exported functions of dynamicBanScore. Exponential +// decay or other time based behavior is tested by other functions. +func TestDynamicBanScoreReset(t *testing.T) { + var bs dynamicBanScore + if bs.Int() != 0 { + t.Errorf("Initial state is not zero.") + } + bs.Increase(100, 0) + r := bs.Int() + if r != 100 { + t.Errorf("Unexpected result %d after ban score increase.", r) + } + bs.Reset() + if bs.Int() != 0 { + t.Errorf("Failed to reset ban score.") + } +} diff --git a/vendor/github.com/btcsuite/btcd/goclean.sh b/vendor/github.com/btcsuite/btcd/goclean.sh new file mode 100755 index 0000000000000000000000000000000000000000..eefbdb91b47874d6a725247bbe112c9e0b0ec74a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/goclean.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# The script does automatic checking on a Go package and its sub-packages, including: +# 1. gofmt (http://golang.org/cmd/gofmt/) +# 2. goimports (https://github.com/bradfitz/goimports) +# 3. golint (https://github.com/golang/lint) +# 4. go vet (http://golang.org/cmd/vet) +# 5. race detector (http://blog.golang.org/race-detector) +# 6. test coverage (http://blog.golang.org/cover) + +set -ex + +# Automatic checks +test -z "$(gofmt -l -w . | tee /dev/stderr)" +test -z "$(goimports -l -w . | tee /dev/stderr)" +test -z "$(golint ./... | grep -v 'ALL_CAPS\|OP_\|NewFieldVal\|RpcCommand\|RpcRawCommand\|RpcSend\|Dns' | tee /dev/stderr)" +test -z "$(go tool vet . 2>&1 | grep -v 'Example\|newestSha' | tee /dev/stderr)" +env GORACE="halt_on_error=1" go test -v -race ./... + +# Run test coverage on each subdirectories and merge the coverage profile. + +set +x +echo "mode: count" > profile.cov + +# Standard go tooling behavior is to ignore dirs with leading underscores. +for dir in $(find . -maxdepth 10 -not -path '.' -not -path './.git*' \ + -not -path '*/_*' -not -path './cmd*' -not -path './release*' -type d) +do +if ls $dir/*.go &> /dev/null; then + go test -covermode=count -coverprofile=$dir/profile.tmp $dir + if [ -f $dir/profile.tmp ]; then + cat $dir/profile.tmp | tail -n +2 >> profile.cov + rm $dir/profile.tmp + fi +fi +done + +# To submit the test coverage result to coveralls.io, +# use goveralls (https://github.com/mattn/goveralls) +# goveralls -coverprofile=profile.cov -service=travis-ci diff --git a/vendor/github.com/btcsuite/btcd/limits/limits_plan9.go b/vendor/github.com/btcsuite/btcd/limits/limits_plan9.go new file mode 100644 index 0000000000000000000000000000000000000000..9c4699d6c0824017e964ff7031a497d19633b6f7 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/limits/limits_plan9.go @@ -0,0 +1,10 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package limits + +// SetLimits is a no-op on Plan 9 due to the lack of process accounting. +func SetLimits() error { + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/limits/limits_unix.go b/vendor/github.com/btcsuite/btcd/limits/limits_unix.go new file mode 100644 index 0000000000000000000000000000000000000000..7ebf8667896087cbcb746483c298c40fd306fd6c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/limits/limits_unix.go @@ -0,0 +1,52 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// +build !windows,!plan9 + +package limits + +import ( + "fmt" + "syscall" +) + +const ( + fileLimitWant = 2048 + fileLimitMin = 1024 +) + +// SetLimits raises some process limits to values which allow btcd and +// associated utilities to run. +func SetLimits() error { + var rLimit syscall.Rlimit + + err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit) + if err != nil { + return err + } + if rLimit.Cur > fileLimitWant { + return nil + } + if rLimit.Max < fileLimitMin { + err = fmt.Errorf("need at least %v file descriptors", + fileLimitMin) + return err + } + if rLimit.Max < fileLimitWant { + rLimit.Cur = rLimit.Max + } else { + rLimit.Cur = fileLimitWant + } + err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) + if err != nil { + // try min value + rLimit.Cur = fileLimitMin + err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) + if err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/limits/limits_windows.go b/vendor/github.com/btcsuite/btcd/limits/limits_windows.go new file mode 100644 index 0000000000000000000000000000000000000000..62655d739e0246839f8dd3fc9777d12911fb0e2e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/limits/limits_windows.go @@ -0,0 +1,10 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package limits + +// SetLimits is a no-op on Windows since it's not required there. +func SetLimits() error { + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/log.go b/vendor/github.com/btcsuite/btcd/log.go new file mode 100644 index 0000000000000000000000000000000000000000..b98197b7fa03e11ce4c3a9a695cbddbdedf5fb2e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/log.go @@ -0,0 +1,362 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "strings" + "time" + + "github.com/btcsuite/btcd/addrmgr" + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/blockchain/indexers" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/peer" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btclog" + "github.com/btcsuite/seelog" +) + +const ( + // maxRejectReasonLen is the maximum length of a sanitized reject reason + // that will be logged. + maxRejectReasonLen = 250 +) + +// Loggers per subsystem. Note that backendLog is a seelog logger that all of +// the subsystem loggers route their messages to. When adding new subsystems, +// add a reference here, to the subsystemLoggers map, and the useLogger +// function. +var ( + backendLog = seelog.Disabled + adxrLog = btclog.Disabled + amgrLog = btclog.Disabled + bcdbLog = btclog.Disabled + bmgrLog = btclog.Disabled + btcdLog = btclog.Disabled + chanLog = btclog.Disabled + discLog = btclog.Disabled + indxLog = btclog.Disabled + minrLog = btclog.Disabled + peerLog = btclog.Disabled + rpcsLog = btclog.Disabled + scrpLog = btclog.Disabled + srvrLog = btclog.Disabled + txmpLog = btclog.Disabled +) + +// subsystemLoggers maps each subsystem identifier to its associated logger. +var subsystemLoggers = map[string]btclog.Logger{ + "ADXR": adxrLog, + "AMGR": amgrLog, + "BCDB": bcdbLog, + "BMGR": bmgrLog, + "BTCD": btcdLog, + "CHAN": chanLog, + "DISC": discLog, + "INDX": indxLog, + "MINR": minrLog, + "PEER": peerLog, + "RPCS": rpcsLog, + "SCRP": scrpLog, + "SRVR": srvrLog, + "TXMP": txmpLog, +} + +// logClosure is used to provide a closure over expensive logging operations +// so don't have to be performed when the logging level doesn't warrant it. +type logClosure func() string + +// String invokes the underlying function and returns the result. +func (c logClosure) String() string { + return c() +} + +// newLogClosure returns a new closure over a function that returns a string +// which itself provides a Stringer interface so that it can be used with the +// logging system. +func newLogClosure(c func() string) logClosure { + return logClosure(c) +} + +// useLogger updates the logger references for subsystemID to logger. Invalid +// subsystems are ignored. +func useLogger(subsystemID string, logger btclog.Logger) { + if _, ok := subsystemLoggers[subsystemID]; !ok { + return + } + subsystemLoggers[subsystemID] = logger + + switch subsystemID { + case "ADXR": + adxrLog = logger + + case "AMGR": + amgrLog = logger + addrmgr.UseLogger(logger) + + case "BCDB": + bcdbLog = logger + database.UseLogger(logger) + + case "BMGR": + bmgrLog = logger + + case "BTCD": + btcdLog = logger + + case "CHAN": + chanLog = logger + blockchain.UseLogger(logger) + + case "DISC": + discLog = logger + + case "INDX": + indxLog = logger + indexers.UseLogger(logger) + + case "MINR": + minrLog = logger + + case "PEER": + peerLog = logger + peer.UseLogger(logger) + + case "RPCS": + rpcsLog = logger + + case "SCRP": + scrpLog = logger + txscript.UseLogger(logger) + + case "SRVR": + srvrLog = logger + + case "TXMP": + txmpLog = logger + } +} + +// initSeelogLogger initializes a new seelog logger that is used as the backend +// for all logging subsystems. +func initSeelogLogger(logFile string) { + config := ` + <seelog type="adaptive" mininterval="2000000" maxinterval="100000000" + critmsgcount="500" minlevel="trace"> + <outputs formatid="all"> + <console /> + <rollingfile type="size" filename="%s" maxsize="10485760" maxrolls="3" /> + </outputs> + <formats> + <format id="all" format="%%Time %%Date [%%LEV] %%Msg%%n" /> + </formats> + </seelog>` + config = fmt.Sprintf(config, logFile) + + logger, err := seelog.LoggerFromConfigAsString(config) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to create logger: %v", err) + os.Exit(1) + } + + backendLog = logger +} + +// setLogLevel sets the logging level for provided subsystem. Invalid +// subsystems are ignored. Uninitialized subsystems are dynamically created as +// needed. +func setLogLevel(subsystemID string, logLevel string) { + // Ignore invalid subsystems. + logger, ok := subsystemLoggers[subsystemID] + if !ok { + return + } + + // Default to info if the log level is invalid. + level, ok := btclog.LogLevelFromString(logLevel) + if !ok { + level = btclog.InfoLvl + } + + // Create new logger for the subsystem if needed. + if logger == btclog.Disabled { + logger = btclog.NewSubsystemLogger(backendLog, subsystemID+": ") + useLogger(subsystemID, logger) + } + logger.SetLevel(level) +} + +// setLogLevels sets the log level for all subsystem loggers to the passed +// level. It also dynamically creates the subsystem loggers as needed, so it +// can be used to initialize the logging system. +func setLogLevels(logLevel string) { + // Configure all sub-systems with the new logging level. Dynamically + // create loggers as needed. + for subsystemID := range subsystemLoggers { + setLogLevel(subsystemID, logLevel) + } +} + +// directionString is a helper function that returns a string that represents +// the direction of a connection (inbound or outbound). +func directionString(inbound bool) string { + if inbound { + return "inbound" + } + return "outbound" +} + +// formatLockTime returns a transaction lock time as a human-readable string. +func formatLockTime(lockTime uint32) string { + // The lock time field of a transaction is either a block height at + // which the transaction is finalized or a timestamp depending on if the + // value is before the txscript.LockTimeThreshold. When it is under the + // threshold it is a block height. + if lockTime < txscript.LockTimeThreshold { + return fmt.Sprintf("height %d", lockTime) + } + + return time.Unix(int64(lockTime), 0).String() +} + +// invSummary returns an inventory message as a human-readable string. +func invSummary(invList []*wire.InvVect) string { + // No inventory. + invLen := len(invList) + if invLen == 0 { + return "empty" + } + + // One inventory item. + if invLen == 1 { + iv := invList[0] + switch iv.Type { + case wire.InvTypeError: + return fmt.Sprintf("error %s", iv.Hash) + case wire.InvTypeBlock: + return fmt.Sprintf("block %s", iv.Hash) + case wire.InvTypeTx: + return fmt.Sprintf("tx %s", iv.Hash) + } + + return fmt.Sprintf("unknown (%d) %s", uint32(iv.Type), iv.Hash) + } + + // More than one inv item. + return fmt.Sprintf("size %d", invLen) +} + +// locatorSummary returns a block locator as a human-readable string. +func locatorSummary(locator []*wire.ShaHash, stopHash *wire.ShaHash) string { + if len(locator) > 0 { + return fmt.Sprintf("locator %s, stop %s", locator[0], stopHash) + } + + return fmt.Sprintf("no locator, stop %s", stopHash) + +} + +// sanitizeString strips any characters which are even remotely dangerous, such +// as html control characters, from the passed string. It also limits it to +// the passed maximum size, which can be 0 for unlimited. When the string is +// limited, it will also add "..." to the string to indicate it was truncated. +func sanitizeString(str string, maxLength uint) string { + const safeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY" + + "Z01234567890 .,;_/:?@" + + // Strip any characters not in the safeChars string removed. + str = strings.Map(func(r rune) rune { + if strings.IndexRune(safeChars, r) >= 0 { + return r + } + return -1 + }, str) + + // Limit the string to the max allowed length. + if maxLength > 0 && uint(len(str)) > maxLength { + str = str[:maxLength] + str = str + "..." + } + return str +} + +// messageSummary returns a human-readable string which summarizes a message. +// Not all messages have or need a summary. This is used for debug logging. +func messageSummary(msg wire.Message) string { + switch msg := msg.(type) { + case *wire.MsgVersion: + return fmt.Sprintf("agent %s, pver %d, block %d", + msg.UserAgent, msg.ProtocolVersion, msg.LastBlock) + + case *wire.MsgVerAck: + // No summary. + + case *wire.MsgGetAddr: + // No summary. + + case *wire.MsgAddr: + return fmt.Sprintf("%d addr", len(msg.AddrList)) + + case *wire.MsgPing: + // No summary - perhaps add nonce. + + case *wire.MsgPong: + // No summary - perhaps add nonce. + + case *wire.MsgAlert: + // No summary. + + case *wire.MsgMemPool: + // No summary. + + case *wire.MsgTx: + return fmt.Sprintf("hash %s, %d inputs, %d outputs, lock %s", + msg.TxSha(), len(msg.TxIn), len(msg.TxOut), + formatLockTime(msg.LockTime)) + + case *wire.MsgBlock: + header := &msg.Header + return fmt.Sprintf("hash %s, ver %d, %d tx, %s", msg.BlockSha(), + header.Version, len(msg.Transactions), header.Timestamp) + + case *wire.MsgInv: + return invSummary(msg.InvList) + + case *wire.MsgNotFound: + return invSummary(msg.InvList) + + case *wire.MsgGetData: + return invSummary(msg.InvList) + + case *wire.MsgGetBlocks: + return locatorSummary(msg.BlockLocatorHashes, &msg.HashStop) + + case *wire.MsgGetHeaders: + return locatorSummary(msg.BlockLocatorHashes, &msg.HashStop) + + case *wire.MsgHeaders: + return fmt.Sprintf("num %d", len(msg.Headers)) + + case *wire.MsgReject: + // Ensure the variable length strings don't contain any + // characters which are even remotely dangerous such as HTML + // control characters, etc. Also limit them to sane length for + // logging. + rejCommand := sanitizeString(msg.Cmd, wire.CommandSize) + rejReason := sanitizeString(msg.Reason, maxRejectReasonLen) + summary := fmt.Sprintf("cmd %v, code %v, reason %v", rejCommand, + msg.Code, rejReason) + if rejCommand == wire.CmdBlock || rejCommand == wire.CmdTx { + summary += fmt.Sprintf(", hash %v", msg.Hash) + } + return summary + } + + // No summary for other messages. + return "" +} diff --git a/vendor/github.com/btcsuite/btcd/mempool.go b/vendor/github.com/btcsuite/btcd/mempool.go new file mode 100644 index 0000000000000000000000000000000000000000..9feeb6750b3fdb8f4f43e26da2b6f111a24b7057 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/mempool.go @@ -0,0 +1,1003 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "container/list" + "crypto/rand" + "fmt" + "math" + "math/big" + "sync" + "sync/atomic" + "time" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/blockchain/indexers" + "github.com/btcsuite/btcd/mining" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +const ( + // mempoolHeight is the height used for the "block" height field of the + // contextual transaction information provided in a transaction view. + mempoolHeight = 0x7fffffff +) + +// mempoolTxDesc is a descriptor containing a transaction in the mempool along +// with additional metadata. +type mempoolTxDesc struct { + mining.TxDesc + + // StartingPriority is the priority of the transaction when it was added + // to the pool. + StartingPriority float64 +} + +// mempoolConfig is a descriptor containing the memory pool configuration. +type mempoolConfig struct { + // Policy defines the various mempool configuration options related + // to policy. + Policy mempoolPolicy + + // FetchUtxoView defines the function to use to fetch unspent + // transaction output information. + FetchUtxoView func(*btcutil.Tx) (*blockchain.UtxoViewpoint, error) + + // Chain defines the concurrent safe block chain instance which houses + // the current best chain. + Chain *blockchain.BlockChain + + // SigCache defines a signature cache to use. + SigCache *txscript.SigCache + + // TimeSource defines the timesource to use. + TimeSource blockchain.MedianTimeSource + + // AddrIndex defines the optional address index instance to use for + // indexing the unconfirmed transactions in the memory pool. + // This can be nil if the address index is not enabled. + AddrIndex *indexers.AddrIndex +} + +// mempoolPolicy houses the policy (configuration parameters) which is used to +// control the mempool. +type mempoolPolicy struct { + // DisableRelayPriority defines whether to relay free or low-fee + // transactions that do not have enough priority to be relayed. + DisableRelayPriority bool + + // FreeTxRelayLimit defines the given amount in thousands of bytes + // per minute that transactions with no fee are rate limited to. + FreeTxRelayLimit float64 + + // MaxOrphanTxs is the maximum number of orphan transactions + // that can be queued. + MaxOrphanTxs int + + // MaxOrphanTxSize is the maximum size allowed for orphan transactions. + // This helps prevent memory exhaustion attacks from sending a lot of + // of big orphans. + MaxOrphanTxSize int + + // MaxSigOpsPerTx is the maximum number of signature operations + // in a single transaction we will relay or mine. It is a fraction + // of the max signature operations for a block. + MaxSigOpsPerTx int + + // MinRelayTxFee defines the minimum transaction fee in BTC/kB to be + // considered a non-zero fee. + MinRelayTxFee btcutil.Amount +} + +// txMemPool is used as a source of transactions that need to be mined into +// blocks and relayed to other peers. It is safe for concurrent access from +// multiple peers. +type txMemPool struct { + // The following variables must only be used atomically. + lastUpdated int64 // last time pool was updated + + sync.RWMutex + cfg mempoolConfig + pool map[wire.ShaHash]*mempoolTxDesc + orphans map[wire.ShaHash]*btcutil.Tx + orphansByPrev map[wire.ShaHash]map[wire.ShaHash]*btcutil.Tx + outpoints map[wire.OutPoint]*btcutil.Tx + pennyTotal float64 // exponentially decaying total for penny spends. + lastPennyUnix int64 // unix time of last ``penny spend'' +} + +// Ensure the txMemPool type implements the mining.TxSource interface. +var _ mining.TxSource = (*txMemPool)(nil) + +// removeOrphan is the internal function which implements the public +// RemoveOrphan. See the comment for RemoveOrphan for more details. +// +// This function MUST be called with the mempool lock held (for writes). +func (mp *txMemPool) removeOrphan(txHash *wire.ShaHash) { + // Nothing to do if passed tx is not an orphan. + tx, exists := mp.orphans[*txHash] + if !exists { + return + } + + // Remove the reference from the previous orphan index. + for _, txIn := range tx.MsgTx().TxIn { + originTxHash := txIn.PreviousOutPoint.Hash + if orphans, exists := mp.orphansByPrev[originTxHash]; exists { + delete(orphans, *tx.Sha()) + + // Remove the map entry altogether if there are no + // longer any orphans which depend on it. + if len(orphans) == 0 { + delete(mp.orphansByPrev, originTxHash) + } + } + } + + // Remove the transaction from the orphan pool. + delete(mp.orphans, *txHash) +} + +// RemoveOrphan removes the passed orphan transaction from the orphan pool and +// previous orphan index. +// +// This function is safe for concurrent access. +func (mp *txMemPool) RemoveOrphan(txHash *wire.ShaHash) { + mp.Lock() + mp.removeOrphan(txHash) + mp.Unlock() +} + +// limitNumOrphans limits the number of orphan transactions by evicting a random +// orphan if adding a new one would cause it to overflow the max allowed. +// +// This function MUST be called with the mempool lock held (for writes). +func (mp *txMemPool) limitNumOrphans() error { + if len(mp.orphans)+1 > mp.cfg.Policy.MaxOrphanTxs && + mp.cfg.Policy.MaxOrphanTxs > 0 { + + // Generate a cryptographically random hash. + randHashBytes := make([]byte, wire.HashSize) + _, err := rand.Read(randHashBytes) + if err != nil { + return err + } + randHashNum := new(big.Int).SetBytes(randHashBytes) + + // Try to find the first entry that is greater than the random + // hash. Use the first entry (which is already pseudorandom due + // to Go's range statement over maps) as a fallback if none of + // the hashes in the orphan pool are larger than the random + // hash. + var foundHash *wire.ShaHash + for txHash := range mp.orphans { + if foundHash == nil { + foundHash = &txHash + } + txHashNum := blockchain.ShaHashToBig(&txHash) + if txHashNum.Cmp(randHashNum) > 0 { + foundHash = &txHash + break + } + } + + mp.removeOrphan(foundHash) + } + + return nil +} + +// addOrphan adds an orphan transaction to the orphan pool. +// +// This function MUST be called with the mempool lock held (for writes). +func (mp *txMemPool) addOrphan(tx *btcutil.Tx) { + // Limit the number orphan transactions to prevent memory exhaustion. A + // random orphan is evicted to make room if needed. + mp.limitNumOrphans() + + mp.orphans[*tx.Sha()] = tx + for _, txIn := range tx.MsgTx().TxIn { + originTxHash := txIn.PreviousOutPoint.Hash + if _, exists := mp.orphansByPrev[originTxHash]; !exists { + mp.orphansByPrev[originTxHash] = + make(map[wire.ShaHash]*btcutil.Tx) + } + mp.orphansByPrev[originTxHash][*tx.Sha()] = tx + } + + txmpLog.Debugf("Stored orphan transaction %v (total: %d)", tx.Sha(), + len(mp.orphans)) +} + +// maybeAddOrphan potentially adds an orphan to the orphan pool. +// +// This function MUST be called with the mempool lock held (for writes). +func (mp *txMemPool) maybeAddOrphan(tx *btcutil.Tx) error { + // Ignore orphan transactions that are too large. This helps avoid + // a memory exhaustion attack based on sending a lot of really large + // orphans. In the case there is a valid transaction larger than this, + // it will ultimtely be rebroadcast after the parent transactions + // have been mined or otherwise received. + // + // Note that the number of orphan transactions in the orphan pool is + // also limited, so this equates to a maximum memory used of + // mp.cfg.Policy.MaxOrphanTxSize * mp.cfg.Policy.MaxOrphanTxs (which is ~5MB + // using the default values at the time this comment was written). + serializedLen := tx.MsgTx().SerializeSize() + if serializedLen > mp.cfg.Policy.MaxOrphanTxSize { + str := fmt.Sprintf("orphan transaction size of %d bytes is "+ + "larger than max allowed size of %d bytes", + serializedLen, mp.cfg.Policy.MaxOrphanTxSize) + return txRuleError(wire.RejectNonstandard, str) + } + + // Add the orphan if the none of the above disqualified it. + mp.addOrphan(tx) + + return nil +} + +// isTransactionInPool returns whether or not the passed transaction already +// exists in the main pool. +// +// This function MUST be called with the mempool lock held (for reads). +func (mp *txMemPool) isTransactionInPool(hash *wire.ShaHash) bool { + if _, exists := mp.pool[*hash]; exists { + return true + } + + return false +} + +// IsTransactionInPool returns whether or not the passed transaction already +// exists in the main pool. +// +// This function is safe for concurrent access. +func (mp *txMemPool) IsTransactionInPool(hash *wire.ShaHash) bool { + // Protect concurrent access. + mp.RLock() + defer mp.RUnlock() + + return mp.isTransactionInPool(hash) +} + +// isOrphanInPool returns whether or not the passed transaction already exists +// in the orphan pool. +// +// This function MUST be called with the mempool lock held (for reads). +func (mp *txMemPool) isOrphanInPool(hash *wire.ShaHash) bool { + if _, exists := mp.orphans[*hash]; exists { + return true + } + + return false +} + +// IsOrphanInPool returns whether or not the passed transaction already exists +// in the orphan pool. +// +// This function is safe for concurrent access. +func (mp *txMemPool) IsOrphanInPool(hash *wire.ShaHash) bool { + // Protect concurrent access. + mp.RLock() + defer mp.RUnlock() + + return mp.isOrphanInPool(hash) +} + +// haveTransaction returns whether or not the passed transaction already exists +// in the main pool or in the orphan pool. +// +// This function MUST be called with the mempool lock held (for reads). +func (mp *txMemPool) haveTransaction(hash *wire.ShaHash) bool { + return mp.isTransactionInPool(hash) || mp.isOrphanInPool(hash) +} + +// HaveTransaction returns whether or not the passed transaction already exists +// in the main pool or in the orphan pool. +// +// This function is safe for concurrent access. +func (mp *txMemPool) HaveTransaction(hash *wire.ShaHash) bool { + // Protect concurrent access. + mp.RLock() + defer mp.RUnlock() + + return mp.haveTransaction(hash) +} + +// removeTransaction is the internal function which implements the public +// RemoveTransaction. See the comment for RemoveTransaction for more details. +// +// This function MUST be called with the mempool lock held (for writes). +func (mp *txMemPool) removeTransaction(tx *btcutil.Tx, removeRedeemers bool) { + txHash := tx.Sha() + if removeRedeemers { + // Remove any transactions which rely on this one. + for i := uint32(0); i < uint32(len(tx.MsgTx().TxOut)); i++ { + outpoint := wire.NewOutPoint(txHash, i) + if txRedeemer, exists := mp.outpoints[*outpoint]; exists { + mp.removeTransaction(txRedeemer, true) + } + } + } + + // Remove the transaction if needed. + if txDesc, exists := mp.pool[*txHash]; exists { + // Remove unconfirmed address index entries associated with the + // transaction if enabled. + if mp.cfg.AddrIndex != nil { + mp.cfg.AddrIndex.RemoveUnconfirmedTx(txHash) + } + + // Mark the referenced outpoints as unspent by the pool. + for _, txIn := range txDesc.Tx.MsgTx().TxIn { + delete(mp.outpoints, txIn.PreviousOutPoint) + } + delete(mp.pool, *txHash) + atomic.StoreInt64(&mp.lastUpdated, time.Now().Unix()) + } +} + +// RemoveTransaction removes the passed transaction from the mempool. When the +// removeRedeemers flag is set, any transactions that redeem outputs from the +// removed transaction will also be removed recursively from the mempool, as +// they would otherwise become orphans. +// +// This function is safe for concurrent access. +func (mp *txMemPool) RemoveTransaction(tx *btcutil.Tx, removeRedeemers bool) { + // Protect concurrent access. + mp.Lock() + defer mp.Unlock() + + mp.removeTransaction(tx, removeRedeemers) +} + +// RemoveDoubleSpends removes all transactions which spend outputs spent by the +// passed transaction from the memory pool. Removing those transactions then +// leads to removing all transactions which rely on them, recursively. This is +// necessary when a block is connected to the main chain because the block may +// contain transactions which were previously unknown to the memory pool. +// +// This function is safe for concurrent access. +func (mp *txMemPool) RemoveDoubleSpends(tx *btcutil.Tx) { + // Protect concurrent access. + mp.Lock() + defer mp.Unlock() + + for _, txIn := range tx.MsgTx().TxIn { + if txRedeemer, ok := mp.outpoints[txIn.PreviousOutPoint]; ok { + if !txRedeemer.Sha().IsEqual(tx.Sha()) { + mp.removeTransaction(txRedeemer, true) + } + } + } +} + +// addTransaction adds the passed transaction to the memory pool. It should +// not be called directly as it doesn't perform any validation. This is a +// helper for maybeAcceptTransaction. +// +// This function MUST be called with the mempool lock held (for writes). +func (mp *txMemPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil.Tx, height int32, fee int64) { + // Add the transaction to the pool and mark the referenced outpoints + // as spent by the pool. + mp.pool[*tx.Sha()] = &mempoolTxDesc{ + TxDesc: mining.TxDesc{ + Tx: tx, + Added: time.Now(), + Height: height, + Fee: fee, + }, + StartingPriority: calcPriority(tx.MsgTx(), utxoView, height), + } + for _, txIn := range tx.MsgTx().TxIn { + mp.outpoints[txIn.PreviousOutPoint] = tx + } + atomic.StoreInt64(&mp.lastUpdated, time.Now().Unix()) + + // Add unconfirmed address index entries associated with the transaction + // if enabled. + if mp.cfg.AddrIndex != nil { + mp.cfg.AddrIndex.AddUnconfirmedTx(tx, utxoView) + } +} + +// checkPoolDoubleSpend checks whether or not the passed transaction is +// attempting to spend coins already spent by other transactions in the pool. +// Note it does not check for double spends against transactions already in the +// main chain. +// +// This function MUST be called with the mempool lock held (for reads). +func (mp *txMemPool) checkPoolDoubleSpend(tx *btcutil.Tx) error { + for _, txIn := range tx.MsgTx().TxIn { + if txR, exists := mp.outpoints[txIn.PreviousOutPoint]; exists { + str := fmt.Sprintf("output %v already spent by "+ + "transaction %v in the memory pool", + txIn.PreviousOutPoint, txR.Sha()) + return txRuleError(wire.RejectDuplicate, str) + } + } + + return nil +} + +// fetchInputUtxos loads utxo details about the input transactions referenced by +// the passed transaction. First, it loads the details form the viewpoint of +// the main chain, then it adjusts them based upon the contents of the +// transaction pool. +// +// This function MUST be called with the mempool lock held (for reads). +func (mp *txMemPool) fetchInputUtxos(tx *btcutil.Tx) (*blockchain.UtxoViewpoint, error) { + utxoView, err := mp.cfg.FetchUtxoView(tx) + if err != nil { + return nil, err + } + + // Attempt to populate any missing inputs from the transaction pool. + for originHash, entry := range utxoView.Entries() { + if entry != nil && !entry.IsFullySpent() { + continue + } + + if poolTxDesc, exists := mp.pool[originHash]; exists { + utxoView.AddTxOuts(poolTxDesc.Tx, mempoolHeight) + } + } + return utxoView, nil +} + +// FetchTransaction returns the requested transaction from the transaction pool. +// This only fetches from the main transaction pool and does not include +// orphans. +// +// This function is safe for concurrent access. +func (mp *txMemPool) FetchTransaction(txHash *wire.ShaHash) (*btcutil.Tx, error) { + // Protect concurrent access. + mp.RLock() + defer mp.RUnlock() + + if txDesc, exists := mp.pool[*txHash]; exists { + return txDesc.Tx, nil + } + + return nil, fmt.Errorf("transaction is not in the pool") +} + +// maybeAcceptTransaction is the internal function which implements the public +// MaybeAcceptTransaction. See the comment for MaybeAcceptTransaction for +// more details. +// +// This function MUST be called with the mempool lock held (for writes). +func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit bool) ([]*wire.ShaHash, error) { + txHash := tx.Sha() + + // Don't accept the transaction if it already exists in the pool. This + // applies to orphan transactions as well. This check is intended to + // be a quick check to weed out duplicates. + if mp.haveTransaction(txHash) { + str := fmt.Sprintf("already have transaction %v", txHash) + return nil, txRuleError(wire.RejectDuplicate, str) + } + + // Perform preliminary sanity checks on the transaction. This makes + // use of btcchain which contains the invariant rules for what + // transactions are allowed into blocks. + err := blockchain.CheckTransactionSanity(tx) + if err != nil { + if cerr, ok := err.(blockchain.RuleError); ok { + return nil, chainRuleError(cerr) + } + return nil, err + } + + // A standalone transaction must not be a coinbase transaction. + if blockchain.IsCoinBase(tx) { + str := fmt.Sprintf("transaction %v is an individual coinbase", + txHash) + return nil, txRuleError(wire.RejectInvalid, str) + } + + // Don't accept transactions with a lock time after the maximum int32 + // value for now. This is an artifact of older bitcoind clients which + // treated this field as an int32 and would treat anything larger + // incorrectly (as negative). + if tx.MsgTx().LockTime > math.MaxInt32 { + str := fmt.Sprintf("transaction %v has a lock time after "+ + "2038 which is not accepted yet", txHash) + return nil, txRuleError(wire.RejectNonstandard, str) + } + + // Get the current height of the main chain. A standalone transaction + // will be mined into the next block at best, so its height is at least + // one more than the current height. + best := mp.cfg.Chain.BestSnapshot() + nextBlockHeight := best.Height + 1 + + // Don't allow non-standard transactions if the network parameters + // forbid their relaying. + if !activeNetParams.RelayNonStdTxs { + err := checkTransactionStandard(tx, nextBlockHeight, + mp.cfg.TimeSource, mp.cfg.Policy.MinRelayTxFee) + if err != nil { + // Attempt to extract a reject code from the error so + // it can be retained. When not possible, fall back to + // a non standard error. + rejectCode, found := extractRejectCode(err) + if !found { + rejectCode = wire.RejectNonstandard + } + str := fmt.Sprintf("transaction %v is not standard: %v", + txHash, err) + return nil, txRuleError(rejectCode, str) + } + } + + // The transaction may not use any of the same outputs as other + // transactions already in the pool as that would ultimately result in a + // double spend. This check is intended to be quick and therefore only + // detects double spends within the transaction pool itself. The + // transaction could still be double spending coins from the main chain + // at this point. There is a more in-depth check that happens later + // after fetching the referenced transaction inputs from the main chain + // which examines the actual spend data and prevents double spends. + err = mp.checkPoolDoubleSpend(tx) + if err != nil { + return nil, err + } + + // Fetch all of the unspent transaction outputs referenced by the inputs + // to this transaction. This function also attempts to fetch the + // transaction itself to be used for detecting a duplicate transaction + // without needing to do a separate lookup. + utxoView, err := mp.fetchInputUtxos(tx) + if err != nil { + if cerr, ok := err.(blockchain.RuleError); ok { + return nil, chainRuleError(cerr) + } + return nil, err + } + + // Don't allow the transaction if it exists in the main chain and is not + // not already fully spent. + txEntry := utxoView.LookupEntry(txHash) + if txEntry != nil && !txEntry.IsFullySpent() { + return nil, txRuleError(wire.RejectDuplicate, + "transaction already exists") + } + delete(utxoView.Entries(), *txHash) + + // Transaction is an orphan if any of the referenced input transactions + // don't exist. Adding orphans to the orphan pool is not handled by + // this function, and the caller should use maybeAddOrphan if this + // behavior is desired. + var missingParents []*wire.ShaHash + for originHash, entry := range utxoView.Entries() { + if entry == nil || entry.IsFullySpent() { + // Must make a copy of the hash here since the iterator + // is replaced and taking its address directly would + // result in all of the entries pointing to the same + // memory location and thus all be the final hash. + hashCopy := originHash + missingParents = append(missingParents, &hashCopy) + } + } + if len(missingParents) > 0 { + return missingParents, nil + } + + // Perform several checks on the transaction inputs using the invariant + // rules in btcchain for what transactions are allowed into blocks. + // Also returns the fees associated with the transaction which will be + // used later. + txFee, err := blockchain.CheckTransactionInputs(tx, nextBlockHeight, + utxoView) + if err != nil { + if cerr, ok := err.(blockchain.RuleError); ok { + return nil, chainRuleError(cerr) + } + return nil, err + } + + // Don't allow transactions with non-standard inputs if the network + // parameters forbid their relaying. + if !activeNetParams.RelayNonStdTxs { + err := checkInputsStandard(tx, utxoView) + if err != nil { + // Attempt to extract a reject code from the error so + // it can be retained. When not possible, fall back to + // a non standard error. + rejectCode, found := extractRejectCode(err) + if !found { + rejectCode = wire.RejectNonstandard + } + str := fmt.Sprintf("transaction %v has a non-standard "+ + "input: %v", txHash, err) + return nil, txRuleError(rejectCode, str) + } + } + + // NOTE: if you modify this code to accept non-standard transactions, + // you should add code here to check that the transaction does a + // reasonable number of ECDSA signature verifications. + + // Don't allow transactions with an excessive number of signature + // operations which would result in making it impossible to mine. Since + // the coinbase address itself can contain signature operations, the + // maximum allowed signature operations per transaction is less than + // the maximum allowed signature operations per block. + numSigOps, err := blockchain.CountP2SHSigOps(tx, false, utxoView) + if err != nil { + if cerr, ok := err.(blockchain.RuleError); ok { + return nil, chainRuleError(cerr) + } + return nil, err + } + numSigOps += blockchain.CountSigOps(tx) + if numSigOps > mp.cfg.Policy.MaxSigOpsPerTx { + str := fmt.Sprintf("transaction %v has too many sigops: %d > %d", + txHash, numSigOps, mp.cfg.Policy.MaxSigOpsPerTx) + return nil, txRuleError(wire.RejectNonstandard, str) + } + + // Don't allow transactions with fees too low to get into a mined block. + // + // Most miners allow a free transaction area in blocks they mine to go + // alongside the area used for high-priority transactions as well as + // transactions with fees. A transaction size of up to 1000 bytes is + // considered safe to go into this section. Further, the minimum fee + // calculated below on its own would encourage several small + // transactions to avoid fees rather than one single larger transaction + // which is more desirable. Therefore, as long as the size of the + // transaction does not exceeed 1000 less than the reserved space for + // high-priority transactions, don't require a fee for it. + serializedSize := int64(tx.MsgTx().SerializeSize()) + minFee := calcMinRequiredTxRelayFee(serializedSize, + mp.cfg.Policy.MinRelayTxFee) + if serializedSize >= (defaultBlockPrioritySize-1000) && txFee < minFee { + str := fmt.Sprintf("transaction %v has %d fees which is under "+ + "the required amount of %d", txHash, txFee, + minFee) + return nil, txRuleError(wire.RejectInsufficientFee, str) + } + + // Require that free transactions have sufficient priority to be mined + // in the next block. Transactions which are being added back to the + // memory pool from blocks that have been disconnected during a reorg + // are exempted. + if isNew && !mp.cfg.Policy.DisableRelayPriority && txFee < minFee { + currentPriority := calcPriority(tx.MsgTx(), utxoView, + nextBlockHeight) + if currentPriority <= minHighPriority { + str := fmt.Sprintf("transaction %v has insufficient "+ + "priority (%g <= %g)", txHash, + currentPriority, minHighPriority) + return nil, txRuleError(wire.RejectInsufficientFee, str) + } + } + + // Free-to-relay transactions are rate limited here to prevent + // penny-flooding with tiny transactions as a form of attack. + if rateLimit && txFee < minFee { + nowUnix := time.Now().Unix() + // we decay passed data with an exponentially decaying ~10 + // minutes window - matches bitcoind handling. + mp.pennyTotal *= math.Pow(1.0-1.0/600.0, + float64(nowUnix-mp.lastPennyUnix)) + mp.lastPennyUnix = nowUnix + + // Are we still over the limit? + if mp.pennyTotal >= mp.cfg.Policy.FreeTxRelayLimit*10*1000 { + str := fmt.Sprintf("transaction %v has been rejected "+ + "by the rate limiter due to low fees", txHash) + return nil, txRuleError(wire.RejectInsufficientFee, str) + } + oldTotal := mp.pennyTotal + + mp.pennyTotal += float64(serializedSize) + txmpLog.Tracef("rate limit: curTotal %v, nextTotal: %v, "+ + "limit %v", oldTotal, mp.pennyTotal, + mp.cfg.Policy.FreeTxRelayLimit*10*1000) + } + + // Verify crypto signatures for each input and reject the transaction if + // any don't verify. + err = blockchain.ValidateTransactionScripts(tx, utxoView, + txscript.StandardVerifyFlags, mp.cfg.SigCache) + if err != nil { + if cerr, ok := err.(blockchain.RuleError); ok { + return nil, chainRuleError(cerr) + } + return nil, err + } + + // Add to transaction pool. + mp.addTransaction(utxoView, tx, best.Height, txFee) + + txmpLog.Debugf("Accepted transaction %v (pool size: %v)", txHash, + len(mp.pool)) + + return nil, nil +} + +// MaybeAcceptTransaction is the main workhorse for handling insertion of new +// free-standing transactions into a memory pool. It includes functionality +// such as rejecting duplicate transactions, ensuring transactions follow all +// rules, detecting orphan transactions, and insertion into the memory pool. +// +// If the transaction is an orphan (missing parent transactions), the +// transaction is NOT added to the orphan pool, but each unknown referenced +// parent is returned. Use ProcessTransaction instead if new orphans should +// be added to the orphan pool. +// +// This function is safe for concurrent access. +func (mp *txMemPool) MaybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit bool) ([]*wire.ShaHash, error) { + // Protect concurrent access. + mp.Lock() + defer mp.Unlock() + + return mp.maybeAcceptTransaction(tx, isNew, rateLimit) +} + +// processOrphans is the internal function which implements the public +// ProcessOrphans. See the comment for ProcessOrphans for more details. +// +// This function MUST be called with the mempool lock held (for writes). +func (mp *txMemPool) processOrphans(hash *wire.ShaHash) []*btcutil.Tx { + var acceptedTxns []*btcutil.Tx + + // Start with processing at least the passed hash. + processHashes := list.New() + processHashes.PushBack(hash) + for processHashes.Len() > 0 { + // Pop the first hash to process. + firstElement := processHashes.Remove(processHashes.Front()) + processHash := firstElement.(*wire.ShaHash) + + // Look up all orphans that are referenced by the transaction we + // just accepted. This will typically only be one, but it could + // be multiple if the referenced transaction contains multiple + // outputs. Skip to the next item on the list of hashes to + // process if there are none. + orphans, exists := mp.orphansByPrev[*processHash] + if !exists || orphans == nil { + continue + } + + for _, tx := range orphans { + // Remove the orphan from the orphan pool. Current + // behavior requires that all saved orphans with + // a newly accepted parent are removed from the orphan + // pool and potentially added to the memory pool, but + // transactions which cannot be added to memory pool + // (including due to still being orphans) are expunged + // from the orphan pool. + // + // TODO(jrick): The above described behavior sounds + // like a bug, and I think we should investigate + // potentially moving orphans to the memory pool, but + // leaving them in the orphan pool if not all parent + // transactions are known yet. + orphanHash := tx.Sha() + mp.removeOrphan(orphanHash) + + // Potentially accept the transaction into the + // transaction pool. + missingParents, err := mp.maybeAcceptTransaction(tx, + true, true) + if err != nil { + // TODO: Remove orphans that depend on this + // failed transaction. + txmpLog.Debugf("Unable to move "+ + "orphan transaction %v to mempool: %v", + tx.Sha(), err) + continue + } + + if len(missingParents) > 0 { + // Transaction is still an orphan, so add it + // back. + mp.addOrphan(tx) + continue + } + + // Add this transaction to the list of transactions + // that are no longer orphans. + acceptedTxns = append(acceptedTxns, tx) + + // Add this transaction to the list of transactions to + // process so any orphans that depend on this one are + // handled too. + // + // TODO(jrick): In the case that this is still an orphan, + // we know that any other transactions in the orphan + // pool with this orphan as their parent are still + // orphans as well, and should be removed. While + // recursively calling removeOrphan and + // maybeAcceptTransaction on these transactions is not + // wrong per se, it is overkill if all we care about is + // recursively removing child transactions of this + // orphan. + processHashes.PushBack(orphanHash) + } + } + + return acceptedTxns +} + +// ProcessOrphans determines if there are any orphans which depend on the passed +// transaction hash (it is possible that they are no longer orphans) and +// potentially accepts them to the memory pool. It repeats the process for the +// newly accepted transactions (to detect further orphans which may no longer be +// orphans) until there are no more. +// +// It returns a slice of transactions added to the mempool. A nil slice means +// no transactions were moved from the orphan pool to the mempool. +// +// This function is safe for concurrent access. +func (mp *txMemPool) ProcessOrphans(hash *wire.ShaHash) []*btcutil.Tx { + mp.Lock() + acceptedTxns := mp.processOrphans(hash) + mp.Unlock() + + return acceptedTxns +} + +// ProcessTransaction is the main workhorse for handling insertion of new +// free-standing transactions into the memory pool. It includes functionality +// such as rejecting duplicate transactions, ensuring transactions follow all +// rules, orphan transaction handling, and insertion into the memory pool. +// +// It returns a slice of transactions added to the mempool. When the +// error is nil, the list will include the passed transaction itself along +// with any additional orphan transaactions that were added as a result of +// the passed one being accepted. +// +// This function is safe for concurrent access. +func (mp *txMemPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool) ([]*btcutil.Tx, error) { + // Protect concurrent access. + mp.Lock() + defer mp.Unlock() + + txmpLog.Tracef("Processing transaction %v", tx.Sha()) + + // Potentially accept the transaction to the memory pool. + missingParents, err := mp.maybeAcceptTransaction(tx, true, rateLimit) + if err != nil { + return nil, err + } + + if len(missingParents) == 0 { + // Accept any orphan transactions that depend on this + // transaction (they may no longer be orphans if all inputs + // are now available) and repeat for those accepted + // transactions until there are no more. + newTxs := mp.processOrphans(tx.Sha()) + acceptedTxs := make([]*btcutil.Tx, len(newTxs)+1) + + // Add the parent transaction first so remote nodes + // do not add orphans. + acceptedTxs[0] = tx + copy(acceptedTxs[1:], newTxs) + + return acceptedTxs, nil + } + + // The transaction is an orphan (has inputs missing). Reject + // it if the flag to allow orphans is not set. + if !allowOrphan { + // Only use the first missing parent transaction in + // the error message. + // + // NOTE: RejectDuplicate is really not an accurate + // reject code here, but it matches the reference + // implementation and there isn't a better choice due + // to the limited number of reject codes. Missing + // inputs is assumed to mean they are already spent + // which is not really always the case. + str := fmt.Sprintf("orphan transaction %v references "+ + "outputs of unknown or fully-spent "+ + "transaction %v", tx.Sha(), missingParents[0]) + return nil, txRuleError(wire.RejectDuplicate, str) + } + + // Potentially add the orphan transaction to the orphan pool. + err = mp.maybeAddOrphan(tx) + if err != nil { + return nil, err + } + + return nil, nil +} + +// Count returns the number of transactions in the main pool. It does not +// include the orphan pool. +// +// This function is safe for concurrent access. +func (mp *txMemPool) Count() int { + mp.RLock() + defer mp.RUnlock() + + return len(mp.pool) +} + +// TxShas returns a slice of hashes for all of the transactions in the memory +// pool. +// +// This function is safe for concurrent access. +func (mp *txMemPool) TxShas() []*wire.ShaHash { + mp.RLock() + defer mp.RUnlock() + + hashes := make([]*wire.ShaHash, len(mp.pool)) + i := 0 + for hash := range mp.pool { + hashCopy := hash + hashes[i] = &hashCopy + i++ + } + + return hashes +} + +// TxDescs returns a slice of descriptors for all the transactions in the pool. +// The descriptors are to be treated as read only. +// +// This function is safe for concurrent access. +func (mp *txMemPool) TxDescs() []*mempoolTxDesc { + mp.RLock() + defer mp.RUnlock() + + descs := make([]*mempoolTxDesc, len(mp.pool)) + i := 0 + for _, desc := range mp.pool { + descs[i] = desc + i++ + } + + return descs +} + +// MiningDescs returns a slice of mining descriptors for all the transactions +// in the pool. +// +// This is part of the mining.TxSource interface implementation and is safe for +// concurrent access as required by the interface contract. +func (mp *txMemPool) MiningDescs() []*mining.TxDesc { + mp.RLock() + defer mp.RUnlock() + + descs := make([]*mining.TxDesc, len(mp.pool)) + i := 0 + for _, desc := range mp.pool { + descs[i] = &desc.TxDesc + i++ + } + + return descs +} + +// LastUpdated returns the last time a transaction was added to or removed from +// the main pool. It does not include the orphan pool. +// +// This function is safe for concurrent access. +func (mp *txMemPool) LastUpdated() time.Time { + return time.Unix(atomic.LoadInt64(&mp.lastUpdated), 0) +} + +// newTxMemPool returns a new memory pool for validating and storing standalone +// transactions until they are mined into a block. +func newTxMemPool(cfg *mempoolConfig) *txMemPool { + memPool := &txMemPool{ + cfg: *cfg, + pool: make(map[wire.ShaHash]*mempoolTxDesc), + orphans: make(map[wire.ShaHash]*btcutil.Tx), + orphansByPrev: make(map[wire.ShaHash]map[wire.ShaHash]*btcutil.Tx), + outpoints: make(map[wire.OutPoint]*btcutil.Tx), + } + return memPool +} diff --git a/vendor/github.com/btcsuite/btcd/mempoolerror.go b/vendor/github.com/btcsuite/btcd/mempoolerror.go new file mode 100644 index 0000000000000000000000000000000000000000..437e9e2462d244975c58bb34c5a141c3c0718bee --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/mempoolerror.go @@ -0,0 +1,135 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/wire" +) + +// RuleError identifies a rule violation. It is used to indicate that +// processing of a transaction failed due to one of the many validation +// rules. The caller can use type assertions to determine if a failure was +// specifically due to a rule violation and use the Err field to access the +// underlying error, which will be either a TxRuleError or a +// blockchain.RuleError. +type RuleError struct { + Err error +} + +// Error satisfies the error interface and prints human-readable errors. +func (e RuleError) Error() string { + if e.Err == nil { + return "<nil>" + } + return e.Err.Error() +} + +// TxRuleError identifies a rule violation. It is used to indicate that +// processing of a transaction failed due to one of the many validation +// rules. The caller can use type assertions to determine if a failure was +// specifically due to a rule violation and access the ErrorCode field to +// ascertain the specific reason for the rule violation. +type TxRuleError struct { + RejectCode wire.RejectCode // The code to send with reject messages + Description string // Human readable description of the issue +} + +// Error satisfies the error interface and prints human-readable errors. +func (e TxRuleError) Error() string { + return e.Description +} + +// txRuleError creates an underlying TxRuleError with the given a set of +// arguments and returns a RuleError that encapsulates it. +func txRuleError(c wire.RejectCode, desc string) RuleError { + return RuleError{ + Err: TxRuleError{RejectCode: c, Description: desc}, + } +} + +// chainRuleError returns a RuleError that encapsulates the given +// blockchain.RuleError. +func chainRuleError(chainErr blockchain.RuleError) RuleError { + return RuleError{ + Err: chainErr, + } +} + +// extractRejectCode attempts to return a relevant reject code for a given error +// by examining the error for known types. It will return true if a code +// was successfully extracted. +func extractRejectCode(err error) (wire.RejectCode, bool) { + // Pull the underlying error out of a RuleError. + if rerr, ok := err.(RuleError); ok { + err = rerr.Err + } + + switch err := err.(type) { + case blockchain.RuleError: + // Convert the chain error to a reject code. + var code wire.RejectCode + switch err.ErrorCode { + // Rejected due to duplicate. + case blockchain.ErrDuplicateBlock: + fallthrough + case blockchain.ErrDoubleSpend: + code = wire.RejectDuplicate + + // Rejected due to obsolete version. + case blockchain.ErrBlockVersionTooOld: + code = wire.RejectObsolete + + // Rejected due to checkpoint. + case blockchain.ErrCheckpointTimeTooOld: + fallthrough + case blockchain.ErrDifficultyTooLow: + fallthrough + case blockchain.ErrBadCheckpoint: + fallthrough + case blockchain.ErrForkTooOld: + code = wire.RejectCheckpoint + + // Everything else is due to the block or transaction being invalid. + default: + code = wire.RejectInvalid + } + + return code, true + + case TxRuleError: + return err.RejectCode, true + + case nil: + return wire.RejectInvalid, false + } + + return wire.RejectInvalid, false +} + +// errToRejectErr examines the underlying type of the error and returns a reject +// code and string appropriate to be sent in a wire.MsgReject message. +func errToRejectErr(err error) (wire.RejectCode, string) { + // Return the reject code along with the error text if it can be + // extracted from the error. + rejectCode, found := extractRejectCode(err) + if found { + return rejectCode, err.Error() + } + + // Return a generic rejected string if there is no error. This really + // should not happen unless the code elsewhere is not setting an error + // as it should be, but it's best to be safe and simply return a generic + // string rather than allowing the following code that dereferences the + // err to panic. + if err == nil { + return wire.RejectInvalid, "rejected" + } + + // When the underlying error is not one of the above cases, just return + // wire.RejectInvalid with a generic rejected string plus the error + // text. + return wire.RejectInvalid, "rejected: " + err.Error() +} diff --git a/vendor/github.com/btcsuite/btcd/mining.go b/vendor/github.com/btcsuite/btcd/mining.go new file mode 100644 index 0000000000000000000000000000000000000000..e38806a85f8afaabac7a25537346d730feffa07e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/mining.go @@ -0,0 +1,821 @@ +// Copyright (c) 2014-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "container/heap" + "container/list" + "fmt" + "time" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/mining" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +const ( + // generatedBlockVersion is the version of the block being generated. + // It is defined as a constant here rather than using the + // wire.BlockVersion constant since a change in the block version + // will require changes to the generated block. Using the wire constant + // for generated block version could allow creation of invalid blocks + // for the updated version. + generatedBlockVersion = 4 + + // minHighPriority is the minimum priority value that allows a + // transaction to be considered high priority. + minHighPriority = btcutil.SatoshiPerBitcoin * 144.0 / 250 + + // blockHeaderOverhead is the max number of bytes it takes to serialize + // a block header and max possible transaction count. + blockHeaderOverhead = wire.MaxBlockHeaderPayload + wire.MaxVarIntPayload + + // coinbaseFlags is added to the coinbase script of a generated block + // and is used to monitor BIP16 support as well as blocks that are + // generated via btcd. + coinbaseFlags = "/P2SH/btcd/" +) + +// txPrioItem houses a transaction along with extra information that allows the +// transaction to be prioritized and track dependencies on other transactions +// which have not been mined into a block yet. +type txPrioItem struct { + tx *btcutil.Tx + fee int64 + priority float64 + feePerKB int64 + + // dependsOn holds a map of transaction hashes which this one depends + // on. It will only be set when the transaction references other + // transactions in the source pool and hence must come after them in + // a block. + dependsOn map[wire.ShaHash]struct{} +} + +// txPriorityQueueLessFunc describes a function that can be used as a compare +// function for a transaction priority queue (txPriorityQueue). +type txPriorityQueueLessFunc func(*txPriorityQueue, int, int) bool + +// txPriorityQueue implements a priority queue of txPrioItem elements that +// supports an arbitrary compare function as defined by txPriorityQueueLessFunc. +type txPriorityQueue struct { + lessFunc txPriorityQueueLessFunc + items []*txPrioItem +} + +// Len returns the number of items in the priority queue. It is part of the +// heap.Interface implementation. +func (pq *txPriorityQueue) Len() int { + return len(pq.items) +} + +// Less returns whether the item in the priority queue with index i should sort +// before the item with index j by deferring to the assigned less function. It +// is part of the heap.Interface implementation. +func (pq *txPriorityQueue) Less(i, j int) bool { + return pq.lessFunc(pq, i, j) +} + +// Swap swaps the items at the passed indices in the priority queue. It is +// part of the heap.Interface implementation. +func (pq *txPriorityQueue) Swap(i, j int) { + pq.items[i], pq.items[j] = pq.items[j], pq.items[i] +} + +// Push pushes the passed item onto the priority queue. It is part of the +// heap.Interface implementation. +func (pq *txPriorityQueue) Push(x interface{}) { + pq.items = append(pq.items, x.(*txPrioItem)) +} + +// Pop removes the highest priority item (according to Less) from the priority +// queue and returns it. It is part of the heap.Interface implementation. +func (pq *txPriorityQueue) Pop() interface{} { + n := len(pq.items) + item := pq.items[n-1] + pq.items[n-1] = nil + pq.items = pq.items[0 : n-1] + return item +} + +// SetLessFunc sets the compare function for the priority queue to the provided +// function. It also invokes heap.Init on the priority queue using the new +// function so it can immediately be used with heap.Push/Pop. +func (pq *txPriorityQueue) SetLessFunc(lessFunc txPriorityQueueLessFunc) { + pq.lessFunc = lessFunc + heap.Init(pq) +} + +// txPQByPriority sorts a txPriorityQueue by transaction priority and then fees +// per kilobyte. +func txPQByPriority(pq *txPriorityQueue, i, j int) bool { + // Using > here so that pop gives the highest priority item as opposed + // to the lowest. Sort by priority first, then fee. + if pq.items[i].priority == pq.items[j].priority { + return pq.items[i].feePerKB > pq.items[j].feePerKB + } + return pq.items[i].priority > pq.items[j].priority + +} + +// txPQByFee sorts a txPriorityQueue by fees per kilobyte and then transaction +// priority. +func txPQByFee(pq *txPriorityQueue, i, j int) bool { + // Using > here so that pop gives the highest fee item as opposed + // to the lowest. Sort by fee first, then priority. + if pq.items[i].feePerKB == pq.items[j].feePerKB { + return pq.items[i].priority > pq.items[j].priority + } + return pq.items[i].feePerKB > pq.items[j].feePerKB +} + +// newTxPriorityQueue returns a new transaction priority queue that reserves the +// passed amount of space for the elements. The new priority queue uses either +// the txPQByPriority or the txPQByFee compare function depending on the +// sortByFee parameter and is already initialized for use with heap.Push/Pop. +// The priority queue can grow larger than the reserved space, but extra copies +// of the underlying array can be avoided by reserving a sane value. +func newTxPriorityQueue(reserve int, sortByFee bool) *txPriorityQueue { + pq := &txPriorityQueue{ + items: make([]*txPrioItem, 0, reserve), + } + if sortByFee { + pq.SetLessFunc(txPQByFee) + } else { + pq.SetLessFunc(txPQByPriority) + } + return pq +} + +// BlockTemplate houses a block that has yet to be solved along with additional +// details about the fees and the number of signature operations for each +// transaction in the block. +type BlockTemplate struct { + // Block is a block that is ready to be solved by miners. Thus, it is + // completely valid with the exception of satisfying the proof-of-work + // requirement. + Block *wire.MsgBlock + + // Fees contains the amount of fees each transaction in the generated + // template pays in base units. Since the first transaction is the + // coinbase, the first entry (offset 0) will contain the negative of the + // sum of the fees of all other transactions. + Fees []int64 + + // SigOpCounts contains the number of signature operations each + // transaction in the generated template performs. + SigOpCounts []int64 + + // Height is the height at which the block template connects to the main + // chain. + Height int32 + + // ValidPayAddress indicates whether or not the template coinbase pays + // to an address or is redeemable by anyone. See the documentation on + // NewBlockTemplate for details on which this can be useful to generate + // templates without a coinbase payment address. + ValidPayAddress bool +} + +// mergeUtxoView adds all of the entries in view to viewA. The result is that +// viewA will contain all of its original entries plus all of the entries +// in viewB. It will replace any entries in viewB which also exist in viewA +// if the entry in viewA is fully spent. +func mergeUtxoView(viewA *blockchain.UtxoViewpoint, viewB *blockchain.UtxoViewpoint) { + viewAEntries := viewA.Entries() + for hash, entryB := range viewB.Entries() { + if entryA, exists := viewAEntries[hash]; !exists || + entryA == nil || entryA.IsFullySpent() { + + viewAEntries[hash] = entryB + } + } +} + +// standardCoinbaseScript returns a standard script suitable for use as the +// signature script of the coinbase transaction of a new block. In particular, +// it starts with the block height that is required by version 2 blocks and adds +// the extra nonce as well as additional coinbase flags. +func standardCoinbaseScript(nextBlockHeight int32, extraNonce uint64) ([]byte, error) { + return txscript.NewScriptBuilder().AddInt64(int64(nextBlockHeight)). + AddInt64(int64(extraNonce)).AddData([]byte(coinbaseFlags)). + Script() +} + +// createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy +// based on the passed block height to the provided address. When the address +// is nil, the coinbase transaction will instead be redeemable by anyone. +// +// See the comment for NewBlockTemplate for more information about why the nil +// address handling is useful. +func createCoinbaseTx(coinbaseScript []byte, nextBlockHeight int32, addr btcutil.Address) (*btcutil.Tx, error) { + // Create the script to pay to the provided payment address if one was + // specified. Otherwise create a script that allows the coinbase to be + // redeemable by anyone. + var pkScript []byte + if addr != nil { + var err error + pkScript, err = txscript.PayToAddrScript(addr) + if err != nil { + return nil, err + } + } else { + var err error + scriptBuilder := txscript.NewScriptBuilder() + pkScript, err = scriptBuilder.AddOp(txscript.OP_TRUE).Script() + if err != nil { + return nil, err + } + } + + tx := wire.NewMsgTx() + tx.AddTxIn(&wire.TxIn{ + // Coinbase transactions have no inputs, so previous outpoint is + // zero hash and max index. + PreviousOutPoint: *wire.NewOutPoint(&wire.ShaHash{}, + wire.MaxPrevOutIndex), + SignatureScript: coinbaseScript, + Sequence: wire.MaxTxInSequenceNum, + }) + tx.AddTxOut(&wire.TxOut{ + Value: blockchain.CalcBlockSubsidy(nextBlockHeight, + activeNetParams.Params), + PkScript: pkScript, + }) + return btcutil.NewTx(tx), nil +} + +// spendTransaction updates the passed view by marking the inputs to the passed +// transaction as spent. It also adds all outputs in the passed transaction +// which are not provably unspendable as available unspent transaction outputs. +func spendTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil.Tx, height int32) error { + for _, txIn := range tx.MsgTx().TxIn { + originHash := &txIn.PreviousOutPoint.Hash + originIndex := txIn.PreviousOutPoint.Index + entry := utxoView.LookupEntry(originHash) + if entry != nil { + entry.SpendOutput(originIndex) + } + } + + utxoView.AddTxOuts(tx, height) + return nil +} + +// logSkippedDeps logs any dependencies which are also skipped as a result of +// skipping a transaction while generating a block template at the trace level. +func logSkippedDeps(tx *btcutil.Tx, deps *list.List) { + if deps == nil { + return + } + + for e := deps.Front(); e != nil; e = e.Next() { + item := e.Value.(*txPrioItem) + minrLog.Tracef("Skipping tx %s since it depends on %s\n", + item.tx.Sha(), tx.Sha()) + } +} + +// minimumMedianTime returns the minimum allowed timestamp for a block building +// on the end of the current best chain. In particular, it is one second after +// the median timestamp of the last several blocks per the chain consensus +// rules. +func minimumMedianTime(chainState *chainState) (time.Time, error) { + chainState.Lock() + defer chainState.Unlock() + if chainState.pastMedianTimeErr != nil { + return time.Time{}, chainState.pastMedianTimeErr + } + + return chainState.pastMedianTime.Add(time.Second), nil +} + +// medianAdjustedTime returns the current time adjusted to ensure it is at least +// one second after the median timestamp of the last several blocks per the +// chain consensus rules. +func medianAdjustedTime(chainState *chainState, timeSource blockchain.MedianTimeSource) (time.Time, error) { + chainState.Lock() + defer chainState.Unlock() + if chainState.pastMedianTimeErr != nil { + return time.Time{}, chainState.pastMedianTimeErr + } + + // The timestamp for the block must not be before the median timestamp + // of the last several blocks. Thus, choose the maximum between the + // current time and one second after the past median time. The current + // timestamp is truncated to a second boundary before comparison since a + // block timestamp does not supported a precision greater than one + // second. + newTimestamp := timeSource.AdjustedTime() + minTimestamp := chainState.pastMedianTime.Add(time.Second) + if newTimestamp.Before(minTimestamp) { + newTimestamp = minTimestamp + } + + return newTimestamp, nil +} + +// NewBlockTemplate returns a new block template that is ready to be solved +// using the transactions from the passed transaction source pool and a coinbase +// that either pays to the passed address if it is not nil, or a coinbase that +// is redeemable by anyone if the passed address is nil. The nil address +// functionality is useful since there are cases such as the getblocktemplate +// RPC where external mining software is responsible for creating their own +// coinbase which will replace the one generated for the block template. Thus +// the need to have configured address can be avoided. +// +// The transactions selected and included are prioritized according to several +// factors. First, each transaction has a priority calculated based on its +// value, age of inputs, and size. Transactions which consist of larger +// amounts, older inputs, and small sizes have the highest priority. Second, a +// fee per kilobyte is calculated for each transaction. Transactions with a +// higher fee per kilobyte are preferred. Finally, the block generation related +// policy settings are all taken into account. +// +// Transactions which only spend outputs from other transactions already in the +// block chain are immediately added to a priority queue which either +// prioritizes based on the priority (then fee per kilobyte) or the fee per +// kilobyte (then priority) depending on whether or not the BlockPrioritySize +// policy setting allots space for high-priority transactions. Transactions +// which spend outputs from other transactions in the source pool are added to a +// dependency map so they can be added to the priority queue once the +// transactions they depend on have been included. +// +// Once the high-priority area (if configured) has been filled with +// transactions, or the priority falls below what is considered high-priority, +// the priority queue is updated to prioritize by fees per kilobyte (then +// priority). +// +// When the fees per kilobyte drop below the TxMinFreeFee policy setting, the +// transaction will be skipped unless the BlockMinSize policy setting is +// nonzero, in which case the block will be filled with the low-fee/free +// transactions until the block size reaches that minimum size. +// +// Any transactions which would cause the block to exceed the BlockMaxSize +// policy setting, exceed the maximum allowed signature operations per block, or +// otherwise cause the block to be invalid are skipped. +// +// Given the above, a block generated by this function is of the following form: +// +// ----------------------------------- -- -- +// | Coinbase Transaction | | | +// |-----------------------------------| | | +// | | | | ----- policy.BlockPrioritySize +// | High-priority Transactions | | | +// | | | | +// |-----------------------------------| | -- +// | | | +// | | | +// | | |--- policy.BlockMaxSize +// | Transactions prioritized by fee | | +// | until <= policy.TxMinFreeFee | | +// | | | +// | | | +// | | | +// |-----------------------------------| | +// | Low-fee/Non high-priority (free) | | +// | transactions (while block size | | +// | <= policy.BlockMinSize) | | +// ----------------------------------- -- +func NewBlockTemplate(policy *mining.Policy, server *server, payToAddress btcutil.Address) (*BlockTemplate, error) { + var txSource mining.TxSource = server.txMemPool + blockManager := server.blockManager + timeSource := server.timeSource + chainState := &blockManager.chainState + + // Extend the most recently known best block. + chainState.Lock() + prevHash := chainState.newestHash + nextBlockHeight := chainState.newestHeight + 1 + chainState.Unlock() + + // Create a standard coinbase transaction paying to the provided + // address. NOTE: The coinbase value will be updated to include the + // fees from the selected transactions later after they have actually + // been selected. It is created here to detect any errors early + // before potentially doing a lot of work below. The extra nonce helps + // ensure the transaction is not a duplicate transaction (paying the + // same value to the same public key address would otherwise be an + // identical transaction for block version 1). + extraNonce := uint64(0) + coinbaseScript, err := standardCoinbaseScript(nextBlockHeight, extraNonce) + if err != nil { + return nil, err + } + coinbaseTx, err := createCoinbaseTx(coinbaseScript, nextBlockHeight, + payToAddress) + if err != nil { + return nil, err + } + numCoinbaseSigOps := int64(blockchain.CountSigOps(coinbaseTx)) + + // Get the current source transactions and create a priority queue to + // hold the transactions which are ready for inclusion into a block + // along with some priority related and fee metadata. Reserve the same + // number of items that are available for the priority queue. Also, + // choose the initial sort order for the priority queue based on whether + // or not there is an area allocated for high-priority transactions. + sourceTxns := txSource.MiningDescs() + sortedByFee := policy.BlockPrioritySize == 0 + priorityQueue := newTxPriorityQueue(len(sourceTxns), sortedByFee) + + // Create a slice to hold the transactions to be included in the + // generated block with reserved space. Also create a utxo view to + // house all of the input transactions so multiple lookups can be + // avoided. + blockTxns := make([]*btcutil.Tx, 0, len(sourceTxns)) + blockTxns = append(blockTxns, coinbaseTx) + blockUtxos := blockchain.NewUtxoViewpoint() + + // dependers is used to track transactions which depend on another + // transaction in the source pool. This, in conjunction with the + // dependsOn map kept with each dependent transaction helps quickly + // determine which dependent transactions are now eligible for inclusion + // in the block once each transaction has been included. + dependers := make(map[wire.ShaHash]*list.List) + + // Create slices to hold the fees and number of signature operations + // for each of the selected transactions and add an entry for the + // coinbase. This allows the code below to simply append details about + // a transaction as it is selected for inclusion in the final block. + // However, since the total fees aren't known yet, use a dummy value for + // the coinbase fee which will be updated later. + txFees := make([]int64, 0, len(sourceTxns)) + txSigOpCounts := make([]int64, 0, len(sourceTxns)) + txFees = append(txFees, -1) // Updated once known + txSigOpCounts = append(txSigOpCounts, numCoinbaseSigOps) + + minrLog.Debugf("Considering %d transactions for inclusion to new block", + len(sourceTxns)) + +mempoolLoop: + for _, txDesc := range sourceTxns { + // A block can't have more than one coinbase or contain + // non-finalized transactions. + tx := txDesc.Tx + if blockchain.IsCoinBase(tx) { + minrLog.Tracef("Skipping coinbase tx %s", tx.Sha()) + continue + } + if !blockchain.IsFinalizedTransaction(tx, nextBlockHeight, + timeSource.AdjustedTime()) { + + minrLog.Tracef("Skipping non-finalized tx %s", tx.Sha()) + continue + } + + // Fetch all of the utxos referenced by the this transaction. + // NOTE: This intentionally does not fetch inputs from the + // mempool since a transaction which depends on other + // transactions in the mempool must come after those + // dependencies in the final generated block. + utxos, err := blockManager.chain.FetchUtxoView(tx) + if err != nil { + minrLog.Warnf("Unable to fetch utxo view for tx %s: "+ + "%v", tx.Sha(), err) + continue + } + + // Setup dependencies for any transactions which reference + // other transactions in the mempool so they can be properly + // ordered below. + prioItem := &txPrioItem{tx: tx} + for _, txIn := range tx.MsgTx().TxIn { + originHash := &txIn.PreviousOutPoint.Hash + originIndex := txIn.PreviousOutPoint.Index + utxoEntry := utxos.LookupEntry(originHash) + if utxoEntry == nil || utxoEntry.IsOutputSpent(originIndex) { + if !txSource.HaveTransaction(originHash) { + minrLog.Tracef("Skipping tx %s because "+ + "it references unspent output "+ + "%s which is not available", + tx.Sha(), txIn.PreviousOutPoint) + continue mempoolLoop + } + + // The transaction is referencing another + // transaction in the source pool, so setup an + // ordering dependency. + depList, exists := dependers[*originHash] + if !exists { + depList = list.New() + dependers[*originHash] = depList + } + depList.PushBack(prioItem) + if prioItem.dependsOn == nil { + prioItem.dependsOn = make( + map[wire.ShaHash]struct{}) + } + prioItem.dependsOn[*originHash] = struct{}{} + + // Skip the check below. We already know the + // referenced transaction is available. + continue + } + } + + // Calculate the final transaction priority using the input + // value age sum as well as the adjusted transaction size. The + // formula is: sum(inputValue * inputAge) / adjustedTxSize + prioItem.priority = calcPriority(tx.MsgTx(), utxos, + nextBlockHeight) + + // Calculate the fee in Satoshi/kB. + txSize := tx.MsgTx().SerializeSize() + prioItem.feePerKB = (txDesc.Fee * 1000) / int64(txSize) + prioItem.fee = txDesc.Fee + + // Add the transaction to the priority queue to mark it ready + // for inclusion in the block unless it has dependencies. + if prioItem.dependsOn == nil { + heap.Push(priorityQueue, prioItem) + } + + // Merge the referenced outputs from the input transactions to + // this transaction into the block utxo view. This allows the + // code below to avoid a second lookup. + mergeUtxoView(blockUtxos, utxos) + } + + minrLog.Tracef("Priority queue len %d, dependers len %d", + priorityQueue.Len(), len(dependers)) + + // The starting block size is the size of the block header plus the max + // possible transaction count size, plus the size of the coinbase + // transaction. + blockSize := blockHeaderOverhead + uint32(coinbaseTx.MsgTx().SerializeSize()) + blockSigOps := numCoinbaseSigOps + totalFees := int64(0) + + // Choose which transactions make it into the block. + for priorityQueue.Len() > 0 { + // Grab the highest priority (or highest fee per kilobyte + // depending on the sort order) transaction. + prioItem := heap.Pop(priorityQueue).(*txPrioItem) + tx := prioItem.tx + + // Grab the list of transactions which depend on this one (if + // any) and remove the entry for this transaction as it will + // either be included or skipped, but in either case the deps + // are no longer needed. + deps := dependers[*tx.Sha()] + delete(dependers, *tx.Sha()) + + // Enforce maximum block size. Also check for overflow. + txSize := uint32(tx.MsgTx().SerializeSize()) + blockPlusTxSize := blockSize + txSize + if blockPlusTxSize < blockSize || blockPlusTxSize >= policy.BlockMaxSize { + minrLog.Tracef("Skipping tx %s because it would exceed "+ + "the max block size", tx.Sha()) + logSkippedDeps(tx, deps) + continue + } + + // Enforce maximum signature operations per block. Also check + // for overflow. + numSigOps := int64(blockchain.CountSigOps(tx)) + if blockSigOps+numSigOps < blockSigOps || + blockSigOps+numSigOps > blockchain.MaxSigOpsPerBlock { + minrLog.Tracef("Skipping tx %s because it would "+ + "exceed the maximum sigops per block", tx.Sha()) + logSkippedDeps(tx, deps) + continue + } + numP2SHSigOps, err := blockchain.CountP2SHSigOps(tx, false, + blockUtxos) + if err != nil { + minrLog.Tracef("Skipping tx %s due to error in "+ + "CountP2SHSigOps: %v", tx.Sha(), err) + logSkippedDeps(tx, deps) + continue + } + numSigOps += int64(numP2SHSigOps) + if blockSigOps+numSigOps < blockSigOps || + blockSigOps+numSigOps > blockchain.MaxSigOpsPerBlock { + minrLog.Tracef("Skipping tx %s because it would "+ + "exceed the maximum sigops per block (p2sh)", + tx.Sha()) + logSkippedDeps(tx, deps) + continue + } + + // Skip free transactions once the block is larger than the + // minimum block size. + if sortedByFee && + prioItem.feePerKB < int64(policy.TxMinFreeFee) && + blockPlusTxSize >= policy.BlockMinSize { + + minrLog.Tracef("Skipping tx %s with feePerKB %.2f "+ + "< TxMinFreeFee %d and block size %d >= "+ + "minBlockSize %d", tx.Sha(), prioItem.feePerKB, + policy.TxMinFreeFee, blockPlusTxSize, + policy.BlockMinSize) + logSkippedDeps(tx, deps) + continue + } + + // Prioritize by fee per kilobyte once the block is larger than + // the priority size or there are no more high-priority + // transactions. + if !sortedByFee && (blockPlusTxSize >= policy.BlockPrioritySize || + prioItem.priority <= minHighPriority) { + + minrLog.Tracef("Switching to sort by fees per "+ + "kilobyte blockSize %d >= BlockPrioritySize "+ + "%d || priority %.2f <= minHighPriority %.2f", + blockPlusTxSize, policy.BlockPrioritySize, + prioItem.priority, minHighPriority) + + sortedByFee = true + priorityQueue.SetLessFunc(txPQByFee) + + // Put the transaction back into the priority queue and + // skip it so it is re-priortized by fees if it won't + // fit into the high-priority section or the priority is + // too low. Otherwise this transaction will be the + // final one in the high-priority section, so just fall + // though to the code below so it is added now. + if blockPlusTxSize > policy.BlockPrioritySize || + prioItem.priority < minHighPriority { + + heap.Push(priorityQueue, prioItem) + continue + } + } + + // Ensure the transaction inputs pass all of the necessary + // preconditions before allowing it to be added to the block. + _, err = blockchain.CheckTransactionInputs(tx, nextBlockHeight, + blockUtxos) + if err != nil { + minrLog.Tracef("Skipping tx %s due to error in "+ + "CheckTransactionInputs: %v", tx.Sha(), err) + logSkippedDeps(tx, deps) + continue + } + err = blockchain.ValidateTransactionScripts(tx, blockUtxos, + txscript.StandardVerifyFlags, server.sigCache) + if err != nil { + minrLog.Tracef("Skipping tx %s due to error in "+ + "ValidateTransactionScripts: %v", tx.Sha(), err) + logSkippedDeps(tx, deps) + continue + } + + // Spend the transaction inputs in the block utxo view and add + // an entry for it to ensure any transactions which reference + // this one have it available as an input and can ensure they + // aren't double spending. + spendTransaction(blockUtxos, tx, nextBlockHeight) + + // Add the transaction to the block, increment counters, and + // save the fees and signature operation counts to the block + // template. + blockTxns = append(blockTxns, tx) + blockSize += txSize + blockSigOps += numSigOps + totalFees += prioItem.fee + txFees = append(txFees, prioItem.fee) + txSigOpCounts = append(txSigOpCounts, numSigOps) + + minrLog.Tracef("Adding tx %s (priority %.2f, feePerKB %.2f)", + prioItem.tx.Sha(), prioItem.priority, prioItem.feePerKB) + + // Add transactions which depend on this one (and also do not + // have any other unsatisified dependencies) to the priority + // queue. + if deps != nil { + for e := deps.Front(); e != nil; e = e.Next() { + // Add the transaction to the priority queue if + // there are no more dependencies after this + // one. + item := e.Value.(*txPrioItem) + delete(item.dependsOn, *tx.Sha()) + if len(item.dependsOn) == 0 { + heap.Push(priorityQueue, item) + } + } + } + } + + // Now that the actual transactions have been selected, update the + // block size for the real transaction count and coinbase value with + // the total fees accordingly. + blockSize -= wire.MaxVarIntPayload - + uint32(wire.VarIntSerializeSize(uint64(len(blockTxns)))) + coinbaseTx.MsgTx().TxOut[0].Value += totalFees + txFees[0] = -totalFees + + // Calculate the required difficulty for the block. The timestamp + // is potentially adjusted to ensure it comes after the median time of + // the last several blocks per the chain consensus rules. + ts, err := medianAdjustedTime(chainState, timeSource) + if err != nil { + return nil, err + } + reqDifficulty, err := blockManager.chain.CalcNextRequiredDifficulty(ts) + if err != nil { + return nil, err + } + + // Create a new block ready to be solved. + merkles := blockchain.BuildMerkleTreeStore(blockTxns) + var msgBlock wire.MsgBlock + msgBlock.Header = wire.BlockHeader{ + Version: generatedBlockVersion, + PrevBlock: *prevHash, + MerkleRoot: *merkles[len(merkles)-1], + Timestamp: ts, + Bits: reqDifficulty, + } + for _, tx := range blockTxns { + if err := msgBlock.AddTransaction(tx.MsgTx()); err != nil { + return nil, err + } + } + + // Finally, perform a full check on the created block against the chain + // consensus rules to ensure it properly connects to the current best + // chain with no issues. + block := btcutil.NewBlock(&msgBlock) + block.SetHeight(nextBlockHeight) + if err := blockManager.chain.CheckConnectBlock(block); err != nil { + return nil, err + } + + minrLog.Debugf("Created new block template (%d transactions, %d in "+ + "fees, %d signature operations, %d bytes, target difficulty "+ + "%064x)", len(msgBlock.Transactions), totalFees, blockSigOps, + blockSize, blockchain.CompactToBig(msgBlock.Header.Bits)) + + return &BlockTemplate{ + Block: &msgBlock, + Fees: txFees, + SigOpCounts: txSigOpCounts, + Height: nextBlockHeight, + ValidPayAddress: payToAddress != nil, + }, nil +} + +// UpdateBlockTime updates the timestamp in the header of the passed block to +// the current time while taking into account the median time of the last +// several blocks to ensure the new time is after that time per the chain +// consensus rules. Finally, it will update the target difficulty if needed +// based on the new time for the test networks since their target difficulty can +// change based upon time. +func UpdateBlockTime(msgBlock *wire.MsgBlock, bManager *blockManager) error { + // The new timestamp is potentially adjusted to ensure it comes after + // the median time of the last several blocks per the chain consensus + // rules. + newTimestamp, err := medianAdjustedTime(&bManager.chainState, + bManager.server.timeSource) + if err != nil { + return err + } + msgBlock.Header.Timestamp = newTimestamp + + // If running on a network that requires recalculating the difficulty, + // do so now. + if activeNetParams.ResetMinDifficulty { + difficulty, err := bManager.chain.CalcNextRequiredDifficulty( + newTimestamp) + if err != nil { + return err + } + msgBlock.Header.Bits = difficulty + } + + return nil +} + +// UpdateExtraNonce updates the extra nonce in the coinbase script of the passed +// block by regenerating the coinbase script with the passed value and block +// height. It also recalculates and updates the new merkle root that results +// from changing the coinbase script. +func UpdateExtraNonce(msgBlock *wire.MsgBlock, blockHeight int32, extraNonce uint64) error { + coinbaseScript, err := standardCoinbaseScript(blockHeight, extraNonce) + if err != nil { + return err + } + if len(coinbaseScript) > blockchain.MaxCoinbaseScriptLen { + return fmt.Errorf("coinbase transaction script length "+ + "of %d is out of range (min: %d, max: %d)", + len(coinbaseScript), blockchain.MinCoinbaseScriptLen, + blockchain.MaxCoinbaseScriptLen) + } + msgBlock.Transactions[0].TxIn[0].SignatureScript = coinbaseScript + + // TODO(davec): A btcutil.Block should use saved in the state to avoid + // recalculating all of the other transaction hashes. + // block.Transactions[0].InvalidateCache() + + // Recalculate the merkle root with the updated extra nonce. + block := btcutil.NewBlock(msgBlock) + merkles := blockchain.BuildMerkleTreeStore(block.Transactions()) + msgBlock.Header.MerkleRoot = *merkles[len(merkles)-1] + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/mining/README.md b/vendor/github.com/btcsuite/btcd/mining/README.md new file mode 100644 index 0000000000000000000000000000000000000000..07eccdea87ddfcd5c85f404d11a1290cd446757e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/mining/README.md @@ -0,0 +1,23 @@ +mining +====== + +[] +(https://travis-ci.org/btcsuite/btcd) [![ISC License] +(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[] +(http://godoc.org/github.com/btcsuite/btcd/mining) + +## Overview + +This package is currently a work in progress. + +## Installation and Updating + +```bash +$ go get -u github.com/btcsuite/btcd/mining +``` + +## License + +Package mining is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/vendor/github.com/btcsuite/btcd/mining/mining.go b/vendor/github.com/btcsuite/btcd/mining/mining.go new file mode 100644 index 0000000000000000000000000000000000000000..13cbec783d2e78a16406f5ef626beb2558bdce36 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/mining/mining.go @@ -0,0 +1,48 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package mining + +import ( + "time" + + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// TxDesc is a descriptor about a transaction in a transaction source along with +// additional metadata. +type TxDesc struct { + // Tx is the transaction associated with the entry. + Tx *btcutil.Tx + + // Added is the time when the entry was added to the source pool. + Added time.Time + + // Height is the block height when the entry was added to the the source + // pool. + Height int32 + + // Fee is the total fee the transaction associated with the entry pays. + Fee int64 +} + +// TxSource represents a source of transactions to consider for inclusion in +// new blocks. +// +// The interface contract requires that all of these methods are safe for +// concurrent access with respect to the source. +type TxSource interface { + // LastUpdated returns the last time a transaction was added to or + // removed from the source pool. + LastUpdated() time.Time + + // MiningDescs returns a slice of mining descriptors for all the + // transactions in the source pool. + MiningDescs() []*TxDesc + + // HaveTransaction returns whether or not the passed transaction hash + // exists in the source pool. + HaveTransaction(hash *wire.ShaHash) bool +} diff --git a/vendor/github.com/btcsuite/btcd/mining/policy.go b/vendor/github.com/btcsuite/btcd/mining/policy.go new file mode 100644 index 0000000000000000000000000000000000000000..26b1f4e65b42a126183b45d6e190456dcc38105c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/mining/policy.go @@ -0,0 +1,29 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package mining + +import "github.com/btcsuite/btcutil" + +// Policy houses the policy (configuration parameters) which is used to control +// the generation of block templates. See the documentation for +// NewBlockTemplate for more details on each of these parameters are used. +type Policy struct { + // BlockMinSize is the minimum block size in bytes to be used when + // generating a block template. + BlockMinSize uint32 + + // BlockMaxSize is the maximum block size in bytes to be used when + // generating a block template. + BlockMaxSize uint32 + + // BlockPrioritySize is the size in bytes for high-priority / low-fee + // transactions to be used when generating a block template. + BlockPrioritySize uint32 + + // TxMinFreeFee is the minimum fee in Satoshi/1000 bytes that is + // required for a transaction to be treated as free for mining purposes + // (block template generation). + TxMinFreeFee btcutil.Amount +} diff --git a/vendor/github.com/btcsuite/btcd/mining_test.go b/vendor/github.com/btcsuite/btcd/mining_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e0b5bd727bc6569d8fe6b7ea9ae11ef696f01cfc --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/mining_test.go @@ -0,0 +1,110 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "container/heap" + "math/rand" + "testing" + + "github.com/btcsuite/btcutil" +) + +// TestTxFeePrioHeap ensures the priority queue for transaction fees and +// priorities works as expected. +func TestTxFeePrioHeap(t *testing.T) { + // Create some fake priority items that exercise the expected sort + // edge conditions. + testItems := []*txPrioItem{ + {feePerKB: 5678, priority: 3}, + {feePerKB: 5678, priority: 1}, + {feePerKB: 5678, priority: 1}, // Duplicate fee and prio + {feePerKB: 5678, priority: 5}, + {feePerKB: 5678, priority: 2}, + {feePerKB: 1234, priority: 3}, + {feePerKB: 1234, priority: 1}, + {feePerKB: 1234, priority: 5}, + {feePerKB: 1234, priority: 5}, // Duplicate fee and prio + {feePerKB: 1234, priority: 2}, + {feePerKB: 10000, priority: 0}, // Higher fee, smaller prio + {feePerKB: 0, priority: 10000}, // Higher prio, lower fee + } + + // Add random data in addition to the edge conditions already manually + // specified. + randSeed := rand.Int63() + defer func() { + if t.Failed() { + t.Logf("Random numbers using seed: %v", randSeed) + } + }() + prng := rand.New(rand.NewSource(randSeed)) + for i := 0; i < 1000; i++ { + testItems = append(testItems, &txPrioItem{ + feePerKB: int64(prng.Float64() * btcutil.SatoshiPerBitcoin), + priority: prng.Float64() * 100, + }) + } + + // Test sorting by fee per KB then priority. + var highest *txPrioItem + priorityQueue := newTxPriorityQueue(len(testItems), true) + for i := 0; i < len(testItems); i++ { + prioItem := testItems[i] + if highest == nil { + highest = prioItem + } + if prioItem.feePerKB >= highest.feePerKB && + prioItem.priority > highest.priority { + + highest = prioItem + } + heap.Push(priorityQueue, prioItem) + } + + for i := 0; i < len(testItems); i++ { + prioItem := heap.Pop(priorityQueue).(*txPrioItem) + if prioItem.feePerKB >= highest.feePerKB && + prioItem.priority > highest.priority { + + t.Fatalf("fee sort: item (fee per KB: %v, "+ + "priority: %v) higher than than prev "+ + "(fee per KB: %v, priority %v)", + prioItem.feePerKB, prioItem.priority, + highest.feePerKB, highest.priority) + } + highest = prioItem + } + + // Test sorting by priority then fee per KB. + highest = nil + priorityQueue = newTxPriorityQueue(len(testItems), false) + for i := 0; i < len(testItems); i++ { + prioItem := testItems[i] + if highest == nil { + highest = prioItem + } + if prioItem.priority >= highest.priority && + prioItem.feePerKB > highest.feePerKB { + + highest = prioItem + } + heap.Push(priorityQueue, prioItem) + } + + for i := 0; i < len(testItems); i++ { + prioItem := heap.Pop(priorityQueue).(*txPrioItem) + if prioItem.priority >= highest.priority && + prioItem.feePerKB > highest.feePerKB { + + t.Fatalf("priority sort: item (fee per KB: %v, "+ + "priority: %v) higher than than prev "+ + "(fee per KB: %v, priority %v)", + prioItem.feePerKB, prioItem.priority, + highest.feePerKB, highest.priority) + } + highest = prioItem + } +} diff --git a/vendor/github.com/btcsuite/btcd/params.go b/vendor/github.com/btcsuite/btcd/params.go new file mode 100644 index 0000000000000000000000000000000000000000..14eeff0717a12ebbed9088d511bdcd51502f46a2 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/params.go @@ -0,0 +1,74 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" +) + +// activeNetParams is a pointer to the parameters specific to the +// currently active bitcoin network. +var activeNetParams = &mainNetParams + +// params is used to group parameters for various networks such as the main +// network and test networks. +type params struct { + *chaincfg.Params + rpcPort string +} + +// mainNetParams contains parameters specific to the main network +// (wire.MainNet). NOTE: The RPC port is intentionally different than the +// reference implementation because btcd does not handle wallet requests. The +// separate wallet process listens on the well-known port and forwards requests +// it does not handle on to btcd. This approach allows the wallet process +// to emulate the full reference implementation RPC API. +var mainNetParams = params{ + Params: &chaincfg.MainNetParams, + rpcPort: "8334", +} + +// regressionNetParams contains parameters specific to the regression test +// network (wire.TestNet). NOTE: The RPC port is intentionally different +// than the reference implementation - see the mainNetParams comment for +// details. +var regressionNetParams = params{ + Params: &chaincfg.RegressionNetParams, + rpcPort: "18334", +} + +// testNet3Params contains parameters specific to the test network (version 3) +// (wire.TestNet3). NOTE: The RPC port is intentionally different than the +// reference implementation - see the mainNetParams comment for details. +var testNet3Params = params{ + Params: &chaincfg.TestNet3Params, + rpcPort: "18334", +} + +// simNetParams contains parameters specific to the simulation test network +// (wire.SimNet). +var simNetParams = params{ + Params: &chaincfg.SimNetParams, + rpcPort: "18556", +} + +// netName returns the name used when referring to a bitcoin network. At the +// time of writing, btcd currently places blocks for testnet version 3 in the +// data and log directory "testnet", which does not match the Name field of the +// chaincfg parameters. This function can be used to override this directory +// name as "testnet" when the passed active network matches wire.TestNet3. +// +// A proper upgrade to move the data and log directories for this network to +// "testnet3" is planned for the future, at which point this function can be +// removed and the network parameter's name used instead. +func netName(chainParams *params) string { + switch chainParams.Net { + case wire.TestNet3: + return "testnet" + default: + return chainParams.Name + } +} diff --git a/vendor/github.com/btcsuite/btcd/peer/README.md b/vendor/github.com/btcsuite/btcd/peer/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2f19e913be20cde2da6ffde0007b42ae48b3f415 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/peer/README.md @@ -0,0 +1,76 @@ +peer +==== + +[] +(https://travis-ci.org/btcsuite/btcd) [![ISC License] +(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[] +(http://godoc.org/github.com/btcsuite/btcd/peer) + +Package peer provides a common base for creating and managing bitcoin network +peers. + +This package has intentionally been designed so it can be used as a standalone +package for any projects needing a full featured bitcoin peer base to build on. + +## Overview + +This package builds upon the wire package, which provides the fundamental +primitives necessary to speak the bitcoin wire protocol, in order to simplify +the process of creating fully functional peers. In essence, it provides a +common base for creating concurrent safe fully validating nodes, Simplified +Payment Verification (SPV) nodes, proxies, etc. + +A quick overview of the major features peer provides are as follows: + + - Provides a basic concurrent safe bitcoin peer for handling bitcoin + communications via the peer-to-peer protocol + - Full duplex reading and writing of bitcoin protocol messages + - Automatic handling of the initial handshake process including protocol + version negotiation + - Asynchronous message queueing of outbound messages with optional channel for + notification when the message is actually sent + - Flexible peer configuration + - Caller is responsible for creating outgoing connections and listening for + incoming connections so they have flexibility to establish connections as + they see fit (proxies, etc) + - User agent name and version + - Bitcoin network + - Service support signalling (full nodes, bloom filters, etc) + - Maximum supported protocol version + - Ability to register callbacks for handling bitcoin protocol messages + - Inventory message batching and send trickling with known inventory detection + and avoidance + - Automatic periodic keep-alive pinging and pong responses + - Random nonce generation and self connection detection + - Proper handling of bloom filter related commands when the caller does not + specify the related flag to signal support + - Disconnects the peer when the protocol version is high enough + - Does not invoke the related callbacks for older protocol versions + - Snapshottable peer statistics such as the total number of bytes read and + written, the remote address, user agent, and negotiated protocol version + - Helper functions pushing addresses, getblocks, getheaders, and reject + messages + - These could all be sent manually via the standard message output function, + but the helpers provide additional nice functionality such as duplicate + filtering and address randomization + - Ability to wait for shutdown/disconnect + - Comprehensive test coverage + +## Installation and Updating + +```bash +$ go get -u github.com/btcsuite/btcd/peer +``` + +## Examples + +* [New Outbound Peer Example] + (https://godoc.org/github.com/btcsuite/btcd/peer#example-package--NewOutboundPeer) + Demonstrates the basic process for initializing and creating an outbound peer. + Peers negotiate by exchanging version and verack messages. For demonstration, + a simple handler for the version message is attached to the peer. + +## License + +Package peer is licensed under the [copyfree](http://copyfree.org) ISC License. diff --git a/vendor/github.com/btcsuite/btcd/peer/doc.go b/vendor/github.com/btcsuite/btcd/peer/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..cd822fe1cfb77ab9f2444c57846e7eebd31a6071 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/peer/doc.go @@ -0,0 +1,150 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package peer provides a common base for creating and managing Bitcoin network +peers. + +Overview + +This package builds upon the wire package, which provides the fundamental +primitives necessary to speak the bitcoin wire protocol, in order to simplify +the process of creating fully functional peers. In essence, it provides a +common base for creating concurrent safe fully validating nodes, Simplified +Payment Verification (SPV) nodes, proxies, etc. + +A quick overview of the major features peer provides are as follows: + + - Provides a basic concurrent safe bitcoin peer for handling bitcoin + communications via the peer-to-peer protocol + - Full duplex reading and writing of bitcoin protocol messages + - Automatic handling of the initial handshake process including protocol + version negotiation + - Asynchronous message queuing of outbound messages with optional channel for + notification when the message is actually sent + - Flexible peer configuration + - Caller is responsible for creating outgoing connections and listening for + incoming connections so they have flexibility to establish connections as + they see fit (proxies, etc) + - User agent name and version + - Bitcoin network + - Service support signalling (full nodes, bloom filters, etc) + - Maximum supported protocol version + - Ability to register callbacks for handling bitcoin protocol messages + - Inventory message batching and send trickling with known inventory detection + and avoidance + - Automatic periodic keep-alive pinging and pong responses + - Random nonce generation and self connection detection + - Proper handling of bloom filter related commands when the caller does not + specify the related flag to signal support + - Disconnects the peer when the protocol version is high enough + - Does not invoke the related callbacks for older protocol versions + - Snapshottable peer statistics such as the total number of bytes read and + written, the remote address, user agent, and negotiated protocol version + - Helper functions pushing addresses, getblocks, getheaders, and reject + messages + - These could all be sent manually via the standard message output function, + but the helpers provide additional nice functionality such as duplicate + filtering and address randomization + - Ability to wait for shutdown/disconnect + - Comprehensive test coverage + +Peer Configuration + +All peer configuration is handled with the Config struct. This allows the +caller to specify things such as the user agent name and version, the bitcoin +network to use, which services it supports, and callbacks to invoke when bitcoin +messages are received. See the documentation for each field of the Config +struct for more details. + +Inbound and Outbound Peers + +A peer can either be inbound or outbound. The caller is responsible for +establishing the connection to remote peers and listening for incoming peers. +This provides high flexibility for things such as connecting via proxies, acting +as a proxy, creating bridge peers, choosing whether to listen for inbound peers, +etc. + +NewOutboundPeer and NewInboundPeer functions must be followed by calling Connect +with a net.Conn instance to the peer. This will start all async I/O goroutines +and initiate the protocol negotiation process. Once finished with the peer call +Disconnect to disconnect from the peer and clean up all resources. +WaitForDisconnect can be used to block until peer disconnection and resource +cleanup has completed. + +Callbacks + +In order to do anything useful with a peer, it is necessary to react to bitcoin +messages. This is accomplished by creating an instance of the MessageListeners +struct with the callbacks to be invoke specified and setting the Listeners field +of the Config struct specified when creating a peer to it. + +For convenience, a callback hook for all of the currently supported bitcoin +messages is exposed which receives the peer instance and the concrete message +type. In addition, a hook for OnRead is provided so even custom messages types +for which this package does not directly provide a hook, as long as they +implement the wire.Message interface, can be used. Finally, the OnWrite hook +is provided, which in conjunction with OnRead, can be used to track server-wide +byte counts. + +It is often useful to use closures which encapsulate state when specifying the +callback handlers. This provides a clean method for accessing that state when +callbacks are invoked. + +Queuing Messages and Inventory + +The QueueMessage function provides the fundamental means to send messages to the +remote peer. As the name implies, this employs a non-blocking queue. A done +channel which will be notified when the message is actually sent can optionally +be specified. There are certain message types which are better sent using other +functions which provide additional functionality. + +Of special interest are inventory messages. Rather than manually sending MsgInv +messages via Queuemessage, the inventory vectors should be queued using the +QueueInventory function. It employs batching and trickling along with +intelligent known remote peer inventory detection and avoidance through the use +of a most-recently used algorithm. + +Message Sending Helper Functions + +In addition to the bare QueueMessage function previously described, the +PushAddrMsg, PushGetBlocksMsg, PushGetHeadersMsg, and PushRejectMsg functions +are provided as a convenience. While it is of course possible to create and +send these message manually via QueueMessage, these helper functions provided +additional useful functionality that is typically desired. + +For example, the PushAddrMsg function automatically limits the addresses to the +maximum number allowed by the message and randomizes the chosen addresses when +there are too many. This allows the caller to simply provide a slice of known +addresses, such as that returned by the addrmgr package, without having to worry +about the details. + +Next, the PushGetBlocksMsg and PushGetHeadersMsg functions will construct proper +messages using a block locator and ignore back to back duplicate requests. + +Finally, the PushRejectMsg function can be used to easily create and send an +appropriate reject message based on the provided parameters as well as +optionally provides a flag to cause it to block until the message is actually +sent. + +Peer Statistics + +A snapshot of the current peer statistics can be obtained with the StatsSnapshot +function. This includes statistics such as the total number of bytes read and +written, the remote address, user agent, and negotiated protocol version. + +Logging + +This package provides extensive logging capabilities through the UseLogger +function which allows a btclog.Logger to be specified. For example, logging at +the debug level provides summaries of every message sent and received, and +logging at the trace level provides full dumps of parsed messages as well as the +raw message bytes using a format similar to hexdump -C. + +Bitcoin Improvement Proposals + +This package supports all BIPS supported by the wire package. +(https://godoc.org/github.com/btcsuite/btcd/wire#hdr-Bitcoin_Improvement_Proposals) +*/ +package peer diff --git a/vendor/github.com/btcsuite/btcd/peer/example_test.go b/vendor/github.com/btcsuite/btcd/peer/example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7ca8f74cf1182aae8adbf67e751d93c4b76e5322 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/peer/example_test.go @@ -0,0 +1,114 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package peer_test + +import ( + "fmt" + "net" + "time" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/peer" + "github.com/btcsuite/btcd/wire" +) + +// mockRemotePeer creates a basic inbound peer listening on the simnet port for +// use with Example_peerConnection. It does not return until the listner is +// active. +func mockRemotePeer() error { + // Configure peer to act as a simnet node that offers no services. + peerCfg := &peer.Config{ + UserAgentName: "peer", // User agent name to advertise. + UserAgentVersion: "1.0.0", // User agent version to advertise. + ChainParams: &chaincfg.SimNetParams, + } + + // Accept connections on the simnet port. + listener, err := net.Listen("tcp", "127.0.0.1:18555") + if err != nil { + return err + } + go func() { + conn, err := listener.Accept() + if err != nil { + fmt.Printf("Accept: error %v\n", err) + return + } + + // Create and start the inbound peer. + p := peer.NewInboundPeer(peerCfg) + if err := p.Connect(conn); err != nil { + fmt.Printf("Connect: error %v\n", err) + return + } + }() + + return nil +} + +// This example demonstrates the basic process for initializing and creating an +// outbound peer. Peers negotiate by exchanging version and verack messages. +// For demonstration, a simple handler for version message is attached to the +// peer. +func Example_newOutboundPeer() { + // Ordinarily this will not be needed since the outbound peer will be + // connecting to a remote peer, however, since this example is executed + // and tested, a mock remote peer is needed to listen for the outbound + // peer. + if err := mockRemotePeer(); err != nil { + fmt.Printf("mockRemotePeer: unexpected error %v\n", err) + return + } + + // Create an outbound peer that is configured to act as a simnet node + // that offers no services and has listeners for the version and verack + // messages. The verack listener is used here to signal the code below + // when the handshake has been finished by signalling a channel. + verack := make(chan struct{}) + peerCfg := &peer.Config{ + UserAgentName: "peer", // User agent name to advertise. + UserAgentVersion: "1.0.0", // User agent version to advertise. + ChainParams: &chaincfg.SimNetParams, + Services: 0, + Listeners: peer.MessageListeners{ + OnVersion: func(p *peer.Peer, msg *wire.MsgVersion) { + fmt.Println("outbound: received version") + }, + OnVerAck: func(p *peer.Peer, msg *wire.MsgVerAck) { + verack <- struct{}{} + }, + }, + } + p, err := peer.NewOutboundPeer(peerCfg, "127.0.0.1:18555") + if err != nil { + fmt.Printf("NewOutboundPeer: error %v\n", err) + return + } + + // Establish the connection to the peer address and mark it connected. + conn, err := net.Dial("tcp", p.Addr()) + if err != nil { + fmt.Printf("net.Dial: error %v\n", err) + return + } + if err := p.Connect(conn); err != nil { + fmt.Printf("Connect: error %v\n", err) + return + } + + // Wait for the verack message or timeout in case of failure. + select { + case <-verack: + case <-time.After(time.Second * 1): + fmt.Printf("Example_peerConnection: verack timeout") + } + + // Disconnect the peer. + p.Disconnect() + p.WaitForDisconnect() + + // Output: + // outbound: received version +} diff --git a/vendor/github.com/btcsuite/btcd/peer/export_test.go b/vendor/github.com/btcsuite/btcd/peer/export_test.go new file mode 100644 index 0000000000000000000000000000000000000000..06ec78a1a5753ba7ae01fc9da1c6e7334ef16f67 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/peer/export_test.go @@ -0,0 +1,18 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +This test file is part of the peer package rather than than the peer_test +package so it can bridge access to the internals to properly test cases which +are either not possible or can't reliably be tested via the public interface. +The functions are only exported while the tests are being run. +*/ + +package peer + +// TstAllowSelfConns allows the test package to allow self connections by +// disabling the detection logic. +func TstAllowSelfConns() { + allowSelfConns = true +} diff --git a/vendor/github.com/btcsuite/btcd/peer/log.go b/vendor/github.com/btcsuite/btcd/peer/log.go new file mode 100644 index 0000000000000000000000000000000000000000..acfdbf4774db30b268d165c5407ff4e60c8c380a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/peer/log.go @@ -0,0 +1,241 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package peer + +import ( + "errors" + "fmt" + "io" + "strings" + "time" + + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btclog" +) + +const ( + // maxRejectReasonLen is the maximum length of a sanitized reject reason + // that will be logged. + maxRejectReasonLen = 250 +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + DisableLog() +} + +// DisableLog disables all library log output. Logging output is disabled +// by default until either UseLogger or SetLogWriter are called. +func DisableLog() { + log = btclog.Disabled +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} + +// SetLogWriter uses a specified io.Writer to output package logging info. +// This allows a caller to direct package logging output without needing a +// dependency on seelog. If the caller is also using btclog, UseLogger should +// be used instead. +func SetLogWriter(w io.Writer, level string) error { + if w == nil { + return errors.New("nil writer") + } + + lvl, ok := btclog.LogLevelFromString(level) + if !ok { + return errors.New("invalid log level") + } + + l, err := btclog.NewLoggerFromWriter(w, lvl) + if err != nil { + return err + } + + UseLogger(l) + return nil +} + +// LogClosure is a closure that can be printed with %v to be used to +// generate expensive-to-create data for a detailed log level and avoid doing +// the work if the data isn't printed. +type logClosure func() string + +func (c logClosure) String() string { + return c() +} + +func newLogClosure(c func() string) logClosure { + return logClosure(c) +} + +// directionString is a helper function that returns a string that represents +// the direction of a connection (inbound or outbound). +func directionString(inbound bool) string { + if inbound { + return "inbound" + } + return "outbound" +} + +// formatLockTime returns a transaction lock time as a human-readable string. +func formatLockTime(lockTime uint32) string { + // The lock time field of a transaction is either a block height at + // which the transaction is finalized or a timestamp depending on if the + // value is before the lockTimeThreshold. When it is under the + // threshold it is a block height. + if lockTime < txscript.LockTimeThreshold { + return fmt.Sprintf("height %d", lockTime) + } + + return time.Unix(int64(lockTime), 0).String() +} + +// invSummary returns an inventory message as a human-readable string. +func invSummary(invList []*wire.InvVect) string { + // No inventory. + invLen := len(invList) + if invLen == 0 { + return "empty" + } + + // One inventory item. + if invLen == 1 { + iv := invList[0] + switch iv.Type { + case wire.InvTypeError: + return fmt.Sprintf("error %s", iv.Hash) + case wire.InvTypeBlock: + return fmt.Sprintf("block %s", iv.Hash) + case wire.InvTypeTx: + return fmt.Sprintf("tx %s", iv.Hash) + } + + return fmt.Sprintf("unknown (%d) %s", uint32(iv.Type), iv.Hash) + } + + // More than one inv item. + return fmt.Sprintf("size %d", invLen) +} + +// locatorSummary returns a block locator as a human-readable string. +func locatorSummary(locator []*wire.ShaHash, stopHash *wire.ShaHash) string { + if len(locator) > 0 { + return fmt.Sprintf("locator %s, stop %s", locator[0], stopHash) + } + + return fmt.Sprintf("no locator, stop %s", stopHash) + +} + +// sanitizeString strips any characters which are even remotely dangerous, such +// as html control characters, from the passed string. It also limits it to +// the passed maximum size, which can be 0 for unlimited. When the string is +// limited, it will also add "..." to the string to indicate it was truncated. +func sanitizeString(str string, maxLength uint) string { + const safeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY" + + "Z01234567890 .,;_/:?@" + + // Strip any characters not in the safeChars string removed. + str = strings.Map(func(r rune) rune { + if strings.IndexRune(safeChars, r) >= 0 { + return r + } + return -1 + }, str) + + // Limit the string to the max allowed length. + if maxLength > 0 && uint(len(str)) > maxLength { + str = str[:maxLength] + str = str + "..." + } + return str +} + +// messageSummary returns a human-readable string which summarizes a message. +// Not all messages have or need a summary. This is used for debug logging. +func messageSummary(msg wire.Message) string { + switch msg := msg.(type) { + case *wire.MsgVersion: + return fmt.Sprintf("agent %s, pver %d, block %d", + msg.UserAgent, msg.ProtocolVersion, msg.LastBlock) + + case *wire.MsgVerAck: + // No summary. + + case *wire.MsgGetAddr: + // No summary. + + case *wire.MsgAddr: + return fmt.Sprintf("%d addr", len(msg.AddrList)) + + case *wire.MsgPing: + // No summary - perhaps add nonce. + + case *wire.MsgPong: + // No summary - perhaps add nonce. + + case *wire.MsgAlert: + // No summary. + + case *wire.MsgMemPool: + // No summary. + + case *wire.MsgTx: + return fmt.Sprintf("hash %s, %d inputs, %d outputs, lock %s", + msg.TxSha(), len(msg.TxIn), len(msg.TxOut), + formatLockTime(msg.LockTime)) + + case *wire.MsgBlock: + header := &msg.Header + return fmt.Sprintf("hash %s, ver %d, %d tx, %s", msg.BlockSha(), + header.Version, len(msg.Transactions), header.Timestamp) + + case *wire.MsgInv: + return invSummary(msg.InvList) + + case *wire.MsgNotFound: + return invSummary(msg.InvList) + + case *wire.MsgGetData: + return invSummary(msg.InvList) + + case *wire.MsgGetBlocks: + return locatorSummary(msg.BlockLocatorHashes, &msg.HashStop) + + case *wire.MsgGetHeaders: + return locatorSummary(msg.BlockLocatorHashes, &msg.HashStop) + + case *wire.MsgHeaders: + return fmt.Sprintf("num %d", len(msg.Headers)) + + case *wire.MsgReject: + // Ensure the variable length strings don't contain any + // characters which are even remotely dangerous such as HTML + // control characters, etc. Also limit them to sane length for + // logging. + rejCommand := sanitizeString(msg.Cmd, wire.CommandSize) + rejReason := sanitizeString(msg.Reason, maxRejectReasonLen) + summary := fmt.Sprintf("cmd %v, code %v, reason %v", rejCommand, + msg.Code, rejReason) + if rejCommand == wire.CmdBlock || rejCommand == wire.CmdTx { + summary += fmt.Sprintf(", hash %v", msg.Hash) + } + return summary + } + + // No summary for other messages. + return "" +} diff --git a/vendor/github.com/btcsuite/btcd/peer/log_test.go b/vendor/github.com/btcsuite/btcd/peer/log_test.go new file mode 100644 index 0000000000000000000000000000000000000000..39b49388bd989ed6fbe16f0366b49c8e37c86f34 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/peer/log_test.go @@ -0,0 +1,65 @@ +// Copyright (c) 2015 The btcsuite developers Use of this source code is +// governed by an ISC license that can be found in the LICENSE file. + +package peer_test + +import ( + "bytes" + "errors" + "io" + "testing" + + "github.com/btcsuite/btcd/peer" +) + +func TestSetLogWriter(t *testing.T) { + tests := []struct { + name string + w io.Writer + level string + expected error + }{ + { + name: "nil writer", + w: nil, + level: "trace", + expected: errors.New("nil writer"), + }, + { + name: "invalid log level", + w: bytes.NewBuffer(nil), + level: "wrong", + expected: errors.New("invalid log level"), + }, + { + name: "use off level", + w: bytes.NewBuffer(nil), + level: "off", + expected: errors.New("min level can't be greater than max. Got min: 6, max: 5"), + }, + { + name: "pass", + w: bytes.NewBuffer(nil), + level: "debug", + expected: nil, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + err := peer.SetLogWriter(test.w, test.level) + if err != nil { + if err.Error() != test.expected.Error() { + t.Errorf("SetLogWriter #%d (%s) wrong result\n"+ + "got: %v\nwant: %v", i, test.name, err, + test.expected) + } + } else { + if test.expected != nil { + t.Errorf("SetLogWriter #%d (%s) wrong result\n"+ + "got: %v\nwant: %v", i, test.name, err, + test.expected) + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/peer/mruinvmap.go b/vendor/github.com/btcsuite/btcd/peer/mruinvmap.go new file mode 100644 index 0000000000000000000000000000000000000000..89d4a8419cae95b30436064b5eee0edd93f36378 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/peer/mruinvmap.go @@ -0,0 +1,131 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package peer + +import ( + "bytes" + "container/list" + "fmt" + "sync" + + "github.com/btcsuite/btcd/wire" +) + +// mruInventoryMap provides a concurrency safe map that is limited to a maximum +// number of items with eviction for the oldest entry when the limit is +// exceeded. +type mruInventoryMap struct { + invMtx sync.Mutex + invMap map[wire.InvVect]*list.Element // nearly O(1) lookups + invList *list.List // O(1) insert, update, delete + limit uint +} + +// String returns the map as a human-readable string. +// +// This function is safe for concurrent access. +func (m *mruInventoryMap) String() string { + m.invMtx.Lock() + defer m.invMtx.Unlock() + + lastEntryNum := len(m.invMap) - 1 + curEntry := 0 + buf := bytes.NewBufferString("[") + for iv := range m.invMap { + buf.WriteString(fmt.Sprintf("%v", iv)) + if curEntry < lastEntryNum { + buf.WriteString(", ") + } + curEntry++ + } + buf.WriteString("]") + + return fmt.Sprintf("<%d>%s", m.limit, buf.String()) +} + +// Exists returns whether or not the passed inventory item is in the map. +// +// This function is safe for concurrent access. +func (m *mruInventoryMap) Exists(iv *wire.InvVect) bool { + m.invMtx.Lock() + defer m.invMtx.Unlock() + + if _, exists := m.invMap[*iv]; exists { + return true + } + return false +} + +// Add adds the passed inventory to the map and handles eviction of the oldest +// item if adding the new item would exceed the max limit. Adding an existing +// item makes it the most recently used item. +// +// This function is safe for concurrent access. +func (m *mruInventoryMap) Add(iv *wire.InvVect) { + m.invMtx.Lock() + defer m.invMtx.Unlock() + + // When the limit is zero, nothing can be added to the map, so just + // return. + if m.limit == 0 { + return + } + + // When the entry already exists move it to the front of the list + // thereby marking it most recently used. + if node, exists := m.invMap[*iv]; exists { + m.invList.MoveToFront(node) + return + } + + // Evict the least recently used entry (back of the list) if the the new + // entry would exceed the size limit for the map. Also reuse the list + // node so a new one doesn't have to be allocated. + if uint(len(m.invMap))+1 > m.limit { + node := m.invList.Back() + lru := node.Value.(*wire.InvVect) + + // Evict least recently used item. + delete(m.invMap, *lru) + + // Reuse the list node of the item that was just evicted for the + // new item. + node.Value = iv + m.invList.MoveToFront(node) + m.invMap[*iv] = node + return + } + + // The limit hasn't been reached yet, so just add the new item. + node := m.invList.PushFront(iv) + m.invMap[*iv] = node + return +} + +// Delete deletes the passed inventory item from the map (if it exists). +// +// This function is safe for concurrent access. +func (m *mruInventoryMap) Delete(iv *wire.InvVect) { + m.invMtx.Lock() + defer m.invMtx.Unlock() + + if node, exists := m.invMap[*iv]; exists { + m.invList.Remove(node) + delete(m.invMap, *iv) + } +} + +// newMruInventoryMap returns a new inventory map that is limited to the number +// of entries specified by limit. When the number of entries exceeds the limit, +// the oldest (least recently used) entry will be removed to make room for the +// new entry. +func newMruInventoryMap(limit uint) *mruInventoryMap { + m := mruInventoryMap{ + invMap: make(map[wire.InvVect]*list.Element), + invList: list.New(), + limit: limit, + } + return &m +} diff --git a/vendor/github.com/btcsuite/btcd/peer/mruinvmap_test.go b/vendor/github.com/btcsuite/btcd/peer/mruinvmap_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0568e8b0225dd0570d8f36d05977bba64f1d2be4 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/peer/mruinvmap_test.go @@ -0,0 +1,169 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package peer + +import ( + "crypto/rand" + "fmt" + "testing" + + "github.com/btcsuite/btcd/wire" +) + +// TestMruInventoryMap ensures the MruInventoryMap behaves as expected including +// limiting, eviction of least-recently used entries, specific entry removal, +// and existence tests. +func TestMruInventoryMap(t *testing.T) { + // Create a bunch of fake inventory vectors to use in testing the mru + // inventory code. + numInvVects := 10 + invVects := make([]*wire.InvVect, 0, numInvVects) + for i := 0; i < numInvVects; i++ { + hash := &wire.ShaHash{byte(i)} + iv := wire.NewInvVect(wire.InvTypeBlock, hash) + invVects = append(invVects, iv) + } + + tests := []struct { + name string + limit int + }{ + {name: "limit 0", limit: 0}, + {name: "limit 1", limit: 1}, + {name: "limit 5", limit: 5}, + {name: "limit 7", limit: 7}, + {name: "limit one less than available", limit: numInvVects - 1}, + {name: "limit all available", limit: numInvVects}, + } + +testLoop: + for i, test := range tests { + // Create a new mru inventory map limited by the specified test + // limit and add all of the test inventory vectors. This will + // cause evicition since there are more test inventory vectors + // than the limits. + mruInvMap := newMruInventoryMap(uint(test.limit)) + for j := 0; j < numInvVects; j++ { + mruInvMap.Add(invVects[j]) + } + + // Ensure the limited number of most recent entries in the + // inventory vector list exist. + for j := numInvVects - test.limit; j < numInvVects; j++ { + if !mruInvMap.Exists(invVects[j]) { + t.Errorf("Exists #%d (%s) entry %s does not "+ + "exist", i, test.name, *invVects[j]) + continue testLoop + } + } + + // Ensure the entries before the limited number of most recent + // entries in the inventory vector list do not exist. + for j := 0; j < numInvVects-test.limit; j++ { + if mruInvMap.Exists(invVects[j]) { + t.Errorf("Exists #%d (%s) entry %s exists", i, + test.name, *invVects[j]) + continue testLoop + } + } + + // Readd the entry that should currently be the least-recently + // used entry so it becomes the most-recently used entry, then + // force an eviction by adding an entry that doesn't exist and + // ensure the evicted entry is the new least-recently used + // entry. + // + // This check needs at least 2 entries. + if test.limit > 1 { + origLruIndex := numInvVects - test.limit + mruInvMap.Add(invVects[origLruIndex]) + + iv := wire.NewInvVect(wire.InvTypeBlock, + &wire.ShaHash{0x00, 0x01}) + mruInvMap.Add(iv) + + // Ensure the original lru entry still exists since it + // was updated and should've have become the mru entry. + if !mruInvMap.Exists(invVects[origLruIndex]) { + t.Errorf("MRU #%d (%s) entry %s does not exist", + i, test.name, *invVects[origLruIndex]) + continue testLoop + } + + // Ensure the entry that should've become the new lru + // entry was evicted. + newLruIndex := origLruIndex + 1 + if mruInvMap.Exists(invVects[newLruIndex]) { + t.Errorf("MRU #%d (%s) entry %s exists", i, + test.name, *invVects[newLruIndex]) + continue testLoop + } + } + + // Delete all of the entries in the inventory vector list, + // including those that don't exist in the map, and ensure they + // no longer exist. + for j := 0; j < numInvVects; j++ { + mruInvMap.Delete(invVects[j]) + if mruInvMap.Exists(invVects[j]) { + t.Errorf("Delete #%d (%s) entry %s exists", i, + test.name, *invVects[j]) + continue testLoop + } + } + } +} + +// TestMruInventoryMapStringer tests the stringized output for the +// MruInventoryMap type. +func TestMruInventoryMapStringer(t *testing.T) { + // Create a couple of fake inventory vectors to use in testing the mru + // inventory stringer code. + hash1 := &wire.ShaHash{0x01} + hash2 := &wire.ShaHash{0x02} + iv1 := wire.NewInvVect(wire.InvTypeBlock, hash1) + iv2 := wire.NewInvVect(wire.InvTypeBlock, hash2) + + // Create new mru inventory map and add the inventory vectors. + mruInvMap := newMruInventoryMap(uint(2)) + mruInvMap.Add(iv1) + mruInvMap.Add(iv2) + + // Ensure the stringer gives the expected result. Since map iteration + // is not ordered, either entry could be first, so account for both + // cases. + wantStr1 := fmt.Sprintf("<%d>[%s, %s]", 2, *iv1, *iv2) + wantStr2 := fmt.Sprintf("<%d>[%s, %s]", 2, *iv2, *iv1) + gotStr := mruInvMap.String() + if gotStr != wantStr1 && gotStr != wantStr2 { + t.Fatalf("unexpected string representation - got %q, want %q "+ + "or %q", gotStr, wantStr1, wantStr2) + } +} + +// BenchmarkMruInventoryList performs basic benchmarks on the most recently +// used inventory handling. +func BenchmarkMruInventoryList(b *testing.B) { + // Create a bunch of fake inventory vectors to use in benchmarking + // the mru inventory code. + b.StopTimer() + numInvVects := 100000 + invVects := make([]*wire.InvVect, 0, numInvVects) + for i := 0; i < numInvVects; i++ { + hashBytes := make([]byte, wire.HashSize) + rand.Read(hashBytes) + hash, _ := wire.NewShaHash(hashBytes) + iv := wire.NewInvVect(wire.InvTypeBlock, hash) + invVects = append(invVects, iv) + } + b.StartTimer() + + // Benchmark the add plus evicition code. + limit := 20000 + mruInvMap := newMruInventoryMap(uint(limit)) + for i := 0; i < b.N; i++ { + mruInvMap.Add(invVects[i%numInvVects]) + } +} diff --git a/vendor/github.com/btcsuite/btcd/peer/mrunoncemap.go b/vendor/github.com/btcsuite/btcd/peer/mrunoncemap.go new file mode 100644 index 0000000000000000000000000000000000000000..1bf5442993e9b36f922ae9fc6fed4f2a154fe6e4 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/peer/mrunoncemap.go @@ -0,0 +1,129 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package peer + +import ( + "bytes" + "container/list" + "fmt" + "sync" +) + +// mruNonceMap provides a concurrency safe map that is limited to a maximum +// number of items with eviction for the oldest entry when the limit is +// exceeded. +type mruNonceMap struct { + mtx sync.Mutex + nonceMap map[uint64]*list.Element // nearly O(1) lookups + nonceList *list.List // O(1) insert, update, delete + limit uint +} + +// String returns the map as a human-readable string. +// +// This function is safe for concurrent access. +func (m *mruNonceMap) String() string { + m.mtx.Lock() + defer m.mtx.Unlock() + + lastEntryNum := len(m.nonceMap) - 1 + curEntry := 0 + buf := bytes.NewBufferString("[") + for nonce := range m.nonceMap { + buf.WriteString(fmt.Sprintf("%d", nonce)) + if curEntry < lastEntryNum { + buf.WriteString(", ") + } + curEntry++ + } + buf.WriteString("]") + + return fmt.Sprintf("<%d>%s", m.limit, buf.String()) +} + +// Exists returns whether or not the passed nonce is in the map. +// +// This function is safe for concurrent access. +func (m *mruNonceMap) Exists(nonce uint64) bool { + m.mtx.Lock() + defer m.mtx.Unlock() + + if _, exists := m.nonceMap[nonce]; exists { + return true + } + return false +} + +// Add adds the passed nonce to the map and handles eviction of the oldest item +// if adding the new item would exceed the max limit. Adding an existing item +// makes it the most recently used item. +// +// This function is safe for concurrent access. +func (m *mruNonceMap) Add(nonce uint64) { + m.mtx.Lock() + defer m.mtx.Unlock() + + // When the limit is zero, nothing can be added to the map, so just + // return. + if m.limit == 0 { + return + } + + // When the entry already exists move it to the front of the list + // thereby marking it most recently used. + if node, exists := m.nonceMap[nonce]; exists { + m.nonceList.MoveToFront(node) + return + } + + // Evict the least recently used entry (back of the list) if the the new + // entry would exceed the size limit for the map. Also reuse the list + // node so a new one doesn't have to be allocated. + if uint(len(m.nonceMap))+1 > m.limit { + node := m.nonceList.Back() + lru := node.Value.(uint64) + + // Evict least recently used item. + delete(m.nonceMap, lru) + + // Reuse the list node of the item that was just evicted for the + // new item. + node.Value = nonce + m.nonceList.MoveToFront(node) + m.nonceMap[nonce] = node + return + } + + // The limit hasn't been reached yet, so just add the new item. + node := m.nonceList.PushFront(nonce) + m.nonceMap[nonce] = node + return +} + +// Delete deletes the passed nonce from the map (if it exists). +// +// This function is safe for concurrent access. +func (m *mruNonceMap) Delete(nonce uint64) { + m.mtx.Lock() + defer m.mtx.Unlock() + + if node, exists := m.nonceMap[nonce]; exists { + m.nonceList.Remove(node) + delete(m.nonceMap, nonce) + } +} + +// newMruNonceMap returns a new nonce map that is limited to the number of +// entries specified by limit. When the number of entries exceeds the limit, +// the oldest (least recently used) entry will be removed to make room for the +// new entry. +func newMruNonceMap(limit uint) *mruNonceMap { + m := mruNonceMap{ + nonceMap: make(map[uint64]*list.Element), + nonceList: list.New(), + limit: limit, + } + return &m +} diff --git a/vendor/github.com/btcsuite/btcd/peer/mrunoncemap_test.go b/vendor/github.com/btcsuite/btcd/peer/mrunoncemap_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bd7fb1082ef2fbe1461ef7f1ed5a5d6836863f3d --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/peer/mrunoncemap_test.go @@ -0,0 +1,152 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package peer + +import ( + "fmt" + "testing" +) + +// TestMruNonceMap ensures the mruNonceMap behaves as expected including +// limiting, eviction of least-recently used entries, specific entry removal, +// and existence tests. +func TestMruNonceMap(t *testing.T) { + // Create a bunch of fake nonces to use in testing the mru nonce code. + numNonces := 10 + nonces := make([]uint64, 0, numNonces) + for i := 0; i < numNonces; i++ { + nonces = append(nonces, uint64(i)) + } + + tests := []struct { + name string + limit int + }{ + {name: "limit 0", limit: 0}, + {name: "limit 1", limit: 1}, + {name: "limit 5", limit: 5}, + {name: "limit 7", limit: 7}, + {name: "limit one less than available", limit: numNonces - 1}, + {name: "limit all available", limit: numNonces}, + } + +testLoop: + for i, test := range tests { + // Create a new mru nonce map limited by the specified test + // limit and add all of the test nonces. This will cause + // evicition since there are more test nonces than the limits. + mruNonceMap := newMruNonceMap(uint(test.limit)) + for j := 0; j < numNonces; j++ { + mruNonceMap.Add(nonces[j]) + } + + // Ensure the limited number of most recent entries in the list + // exist. + for j := numNonces - test.limit; j < numNonces; j++ { + if !mruNonceMap.Exists(nonces[j]) { + t.Errorf("Exists #%d (%s) entry %d does not "+ + "exist", i, test.name, nonces[j]) + continue testLoop + } + } + + // Ensure the entries before the limited number of most recent + // entries in the list do not exist. + for j := 0; j < numNonces-test.limit; j++ { + if mruNonceMap.Exists(nonces[j]) { + t.Errorf("Exists #%d (%s) entry %d exists", i, + test.name, nonces[j]) + continue testLoop + } + } + + // Readd the entry that should currently be the least-recently + // used entry so it becomes the most-recently used entry, then + // force an eviction by adding an entry that doesn't exist and + // ensure the evicted entry is the new least-recently used + // entry. + // + // This check needs at least 2 entries. + if test.limit > 1 { + origLruIndex := numNonces - test.limit + mruNonceMap.Add(nonces[origLruIndex]) + + mruNonceMap.Add(uint64(numNonces) + 1) + + // Ensure the original lru entry still exists since it + // was updated and should've have become the mru entry. + if !mruNonceMap.Exists(nonces[origLruIndex]) { + t.Errorf("MRU #%d (%s) entry %d does not exist", + i, test.name, nonces[origLruIndex]) + continue testLoop + } + + // Ensure the entry that should've become the new lru + // entry was evicted. + newLruIndex := origLruIndex + 1 + if mruNonceMap.Exists(nonces[newLruIndex]) { + t.Errorf("MRU #%d (%s) entry %d exists", i, + test.name, nonces[newLruIndex]) + continue testLoop + } + } + + // Delete all of the entries in the list, including those that + // don't exist in the map, and ensure they no longer exist. + for j := 0; j < numNonces; j++ { + mruNonceMap.Delete(nonces[j]) + if mruNonceMap.Exists(nonces[j]) { + t.Errorf("Delete #%d (%s) entry %d exists", i, + test.name, nonces[j]) + continue testLoop + } + } + } +} + +// TestMruNonceMapStringer tests the stringized output for the mruNonceMap type. +func TestMruNonceMapStringer(t *testing.T) { + // Create a couple of fake nonces to use in testing the mru nonce + // stringer code. + nonce1 := uint64(10) + nonce2 := uint64(20) + + // Create new mru nonce map and add the nonces. + mruNonceMap := newMruNonceMap(uint(2)) + mruNonceMap.Add(nonce1) + mruNonceMap.Add(nonce2) + + // Ensure the stringer gives the expected result. Since map iteration + // is not ordered, either entry could be first, so account for both + // cases. + wantStr1 := fmt.Sprintf("<%d>[%d, %d]", 2, nonce1, nonce2) + wantStr2 := fmt.Sprintf("<%d>[%d, %d]", 2, nonce2, nonce1) + gotStr := mruNonceMap.String() + if gotStr != wantStr1 && gotStr != wantStr2 { + t.Fatalf("unexpected string representation - got %q, want %q "+ + "or %q", gotStr, wantStr1, wantStr2) + } +} + +// BenchmarkMruNonceList performs basic benchmarks on the most recently used +// nonce handling. +func BenchmarkMruNonceList(b *testing.B) { + // Create a bunch of fake nonces to use in benchmarking the mru nonce + // code. + b.StopTimer() + numNonces := 100000 + nonces := make([]uint64, 0, numNonces) + for i := 0; i < numNonces; i++ { + nonces = append(nonces, uint64(i)) + } + b.StartTimer() + + // Benchmark the add plus evicition code. + limit := 20000 + mruNonceMap := newMruNonceMap(uint(limit)) + for i := 0; i < b.N; i++ { + mruNonceMap.Add(nonces[i%numNonces]) + } +} diff --git a/vendor/github.com/btcsuite/btcd/peer/peer.go b/vendor/github.com/btcsuite/btcd/peer/peer.go new file mode 100644 index 0000000000000000000000000000000000000000..cf773c9481d223373c705f60b1b09314b640fe72 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/peer/peer.go @@ -0,0 +1,2106 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package peer + +import ( + "bytes" + "container/list" + "errors" + "fmt" + "io" + "math/rand" + "net" + "strconv" + "sync" + "sync/atomic" + "time" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/go-socks/socks" + "github.com/davecgh/go-spew/spew" +) + +const ( + // MaxProtocolVersion is the max protocol version the peer supports. + MaxProtocolVersion = wire.SendHeadersVersion + + // outputBufferSize is the number of elements the output channels use. + outputBufferSize = 50 + + // invTrickleSize is the maximum amount of inventory to send in a single + // message when trickling inventory to remote peers. + maxInvTrickleSize = 1000 + + // maxKnownInventory is the maximum number of items to keep in the known + // inventory cache. + maxKnownInventory = 1000 + + // pingInterval is the interval of time to wait in between sending ping + // messages. + pingInterval = 2 * time.Minute + + // negotiateTimeout is the duration of inactivity before we timeout a + // peer that hasn't completed the initial version negotiation. + negotiateTimeout = 30 * time.Second + + // idleTimeout is the duration of inactivity before we time out a peer. + idleTimeout = 5 * time.Minute + + // stallTickInterval is the interval of time between each check for + // stalled peers. + stallTickInterval = 15 * time.Second + + // stallResponseTimeout is the base maximum amount of time messages that + // expect a response will wait before disconnecting the peer for + // stalling. The deadlines are adjusted for callback running times and + // only checked on each stall tick interval. + stallResponseTimeout = 30 * time.Second + + // trickleTimeout is the duration of the ticker which trickles down the + // inventory to a peer. + trickleTimeout = 10 * time.Second +) + +var ( + // nodeCount is the total number of peer connections made since startup + // and is used to assign an id to a peer. + nodeCount int32 + + // zeroHash is the zero value hash (all zeros). It is defined as a + // convenience. + zeroHash wire.ShaHash + + // sentNonces houses the unique nonces that are generated when pushing + // version messages that are used to detect self connections. + sentNonces = newMruNonceMap(50) + + // allowSelfConns is only used to allow the tests to bypass the self + // connection detecting and disconnect logic since they intentionally + // do so for testing purposes. + allowSelfConns bool +) + +// MessageListeners defines callback function pointers to invoke with message +// listeners for a peer. Any listener which is not set to a concrete callback +// during peer initialization is ignored. Execution of multiple message +// listeners occurs serially, so one callback blocks the excution of the next. +// +// NOTE: Unless otherwise documented, these listeners must NOT directly call any +// blocking calls (such as WaitForShutdown) on the peer instance since the input +// handler goroutine blocks until the callback has completed. Doing so will +// result in a deadlock. +type MessageListeners struct { + // OnGetAddr is invoked when a peer receives a getaddr bitcoin message. + OnGetAddr func(p *Peer, msg *wire.MsgGetAddr) + + // OnAddr is invoked when a peer receives an addr bitcoin message. + OnAddr func(p *Peer, msg *wire.MsgAddr) + + // OnPing is invoked when a peer receives a ping bitcoin message. + OnPing func(p *Peer, msg *wire.MsgPing) + + // OnPong is invoked when a peer receives a pong bitcoin message. + OnPong func(p *Peer, msg *wire.MsgPong) + + // OnAlert is invoked when a peer receives an alert bitcoin message. + OnAlert func(p *Peer, msg *wire.MsgAlert) + + // OnMemPool is invoked when a peer receives a mempool bitcoin message. + OnMemPool func(p *Peer, msg *wire.MsgMemPool) + + // OnTx is invoked when a peer receives a tx bitcoin message. + OnTx func(p *Peer, msg *wire.MsgTx) + + // OnBlock is invoked when a peer receives a block bitcoin message. + OnBlock func(p *Peer, msg *wire.MsgBlock, buf []byte) + + // OnInv is invoked when a peer receives an inv bitcoin message. + OnInv func(p *Peer, msg *wire.MsgInv) + + // OnHeaders is invoked when a peer receives a headers bitcoin message. + OnHeaders func(p *Peer, msg *wire.MsgHeaders) + + // OnNotFound is invoked when a peer receives a notfound bitcoin + // message. + OnNotFound func(p *Peer, msg *wire.MsgNotFound) + + // OnGetData is invoked when a peer receives a getdata bitcoin message. + OnGetData func(p *Peer, msg *wire.MsgGetData) + + // OnGetBlocks is invoked when a peer receives a getblocks bitcoin + // message. + OnGetBlocks func(p *Peer, msg *wire.MsgGetBlocks) + + // OnGetHeaders is invoked when a peer receives a getheaders bitcoin + // message. + OnGetHeaders func(p *Peer, msg *wire.MsgGetHeaders) + + // OnFilterAdd is invoked when a peer receives a filteradd bitcoin message. + // Peers that do not advertise support for bloom filters and negotiate to a + // protocol version before BIP0111 will simply ignore the message while + // those that negotiate to the BIP0111 protocol version or higher will be + // immediately disconnected. + OnFilterAdd func(p *Peer, msg *wire.MsgFilterAdd) + + // OnFilterClear is invoked when a peer receives a filterclear bitcoin + // message. + // Peers that do not advertise support for bloom filters and negotiate to a + // protocol version before BIP0111 will simply ignore the message while + // those that negotiate to the BIP0111 protocol version or higher will be + // immediately disconnected. + OnFilterClear func(p *Peer, msg *wire.MsgFilterClear) + + // OnFilterLoad is invoked when a peer receives a filterload bitcoin + // message. + // Peers that do not advertise support for bloom filters and negotiate to a + // protocol version before BIP0111 will simply ignore the message while + // those that negotiate to the BIP0111 protocol version or higher will be + // immediately disconnected. + OnFilterLoad func(p *Peer, msg *wire.MsgFilterLoad) + + // OnMerkleBlock is invoked when a peer receives a merkleblock bitcoin + // message. + OnMerkleBlock func(p *Peer, msg *wire.MsgMerkleBlock) + + // OnVersion is invoked when a peer receives a version bitcoin message. + OnVersion func(p *Peer, msg *wire.MsgVersion) + + // OnVerAck is invoked when a peer receives a verack bitcoin message. + OnVerAck func(p *Peer, msg *wire.MsgVerAck) + + // OnReject is invoked when a peer receives a reject bitcoin message. + OnReject func(p *Peer, msg *wire.MsgReject) + + // OnSendHeaders is invoked when a peer receives a sendheaders bitcoin + // message. + OnSendHeaders func(p *Peer, msg *wire.MsgSendHeaders) + + // OnRead is invoked when a peer receives a bitcoin message. It + // consists of the number of bytes read, the message, and whether or not + // an error in the read occurred. Typically, callers will opt to use + // the callbacks for the specific message types, however this can be + // useful for circumstances such as keeping track of server-wide byte + // counts or working with custom message types for which the peer does + // not directly provide a callback. + OnRead func(p *Peer, bytesRead int, msg wire.Message, err error) + + // OnWrite is invoked when a peer receives a bitcoin message. It + // consists of the number of bytes written, the message, and whether or + // not an error in the write occurred. This can be useful for + // circumstances such as keeping track of server-wide byte counts. + OnWrite func(p *Peer, bytesWritten int, msg wire.Message, err error) +} + +// Config is the struct to hold configuration options useful to Peer. +type Config struct { + // NewestBlock specifies a callback which provides the newest block + // details to the peer as needed. This can be nil in which case the + // peer will report a block height of 0, however it is good practice for + // peers to specify this so their currently best known is accurately + // reported. + NewestBlock ShaFunc + + // BestLocalAddress returns the best local address for a given address. + BestLocalAddress AddrFunc + + // HostToNetAddress returns the netaddress for the given host. This can be + // nil in which case the host will be parsed as an IP address. + HostToNetAddress HostToNetAddrFunc + + // Proxy indicates a proxy is being used for connections. The only + // effect this has is to prevent leaking the tor proxy address, so it + // only needs to specified if using a tor proxy. + Proxy string + + // UserAgentName specifies the user agent name to advertise. It is + // highly recommended to specify this value. + UserAgentName string + + // UserAgentVersion specifies the user agent version to advertise. It + // is highly recommended to specify this value and that it follows the + // form "major.minor.revision" e.g. "2.6.41". + UserAgentVersion string + + // ChainParams identifies which chain parameters the peer is associated + // with. It is highly recommended to specify this field, however it can + // be omitted in which case the test network will be used. + ChainParams *chaincfg.Params + + // Services specifies which services to advertise as supported by the + // local peer. This field can be omitted in which case it will be 0 + // and therefore advertise no supported services. + Services wire.ServiceFlag + + // ProtocolVersion specifies the maximum protocol version to use and + // advertise. This field can be omitted in which case + // peer.MaxProtocolVersion will be used. + ProtocolVersion uint32 + + // DisableRelayTx specifies if the remote peer should be informed to + // not send inv messages for transactions. + DisableRelayTx bool + + // Listeners houses callback functions to be invoked on receiving peer + // messages. + Listeners MessageListeners +} + +// minUint32 is a helper function to return the minimum of two uint32s. +// This avoids a math import and the need to cast to floats. +func minUint32(a, b uint32) uint32 { + if a < b { + return a + } + return b +} + +// newNetAddress attempts to extract the IP address and port from the passed +// net.Addr interface and create a bitcoin NetAddress structure using that +// information. +func newNetAddress(addr net.Addr, services wire.ServiceFlag) (*wire.NetAddress, error) { + // addr will be a net.TCPAddr when not using a proxy. + if tcpAddr, ok := addr.(*net.TCPAddr); ok { + ip := tcpAddr.IP + port := uint16(tcpAddr.Port) + na := wire.NewNetAddressIPPort(ip, port, services) + return na, nil + } + + // addr will be a socks.ProxiedAddr when using a proxy. + if proxiedAddr, ok := addr.(*socks.ProxiedAddr); ok { + ip := net.ParseIP(proxiedAddr.Host) + if ip == nil { + ip = net.ParseIP("0.0.0.0") + } + port := uint16(proxiedAddr.Port) + na := wire.NewNetAddressIPPort(ip, port, services) + return na, nil + } + + // For the most part, addr should be one of the two above cases, but + // to be safe, fall back to trying to parse the information from the + // address string as a last resort. + host, portStr, err := net.SplitHostPort(addr.String()) + if err != nil { + return nil, err + } + ip := net.ParseIP(host) + port, err := strconv.ParseUint(portStr, 10, 16) + if err != nil { + return nil, err + } + na := wire.NewNetAddressIPPort(ip, uint16(port), services) + return na, nil +} + +// outMsg is used to house a message to be sent along with a channel to signal +// when the message has been sent (or won't be sent due to things such as +// shutdown) +type outMsg struct { + msg wire.Message + doneChan chan<- struct{} +} + +// stallControlCmd represents the command of a stall control message. +type stallControlCmd uint8 + +// Constants for the command of a stall control message. +const ( + // sccSendMessage indicates a message is being sent to the remote peer. + sccSendMessage stallControlCmd = iota + + // sccReceiveMessage indicates a message has been received from the + // remote peer. + sccReceiveMessage + + // sccHandlerStart indicates a callback handler is about to be invoked. + sccHandlerStart + + // sccHandlerStart indicates a callback handler has completed. + sccHandlerDone +) + +// stallControlMsg is used to signal the stall handler about specific events +// so it can properly detect and handle stalled remote peers. +type stallControlMsg struct { + command stallControlCmd + message wire.Message +} + +// StatsSnap is a snapshot of peer stats at a point in time. +type StatsSnap struct { + ID int32 + Addr string + Services wire.ServiceFlag + LastSend time.Time + LastRecv time.Time + BytesSent uint64 + BytesRecv uint64 + ConnTime time.Time + TimeOffset int64 + Version uint32 + UserAgent string + Inbound bool + StartingHeight int32 + LastBlock int32 + LastPingNonce uint64 + LastPingTime time.Time + LastPingMicros int64 +} + +// ShaFunc is a function which returns a block sha, height and error +// It is used as a callback to get newest block details. +type ShaFunc func() (sha *wire.ShaHash, height int32, err error) + +// AddrFunc is a func which takes an address and returns a related address. +type AddrFunc func(remoteAddr *wire.NetAddress) *wire.NetAddress + +// HostToNetAddrFunc is a func which takes a host, port, services and returns +// the netaddress. +type HostToNetAddrFunc func(host string, port uint16, + services wire.ServiceFlag) (*wire.NetAddress, error) + +// NOTE: The overall data flow of a peer is split into 3 goroutines. Inbound +// messages are read via the inHandler goroutine and generally dispatched to +// their own handler. For inbound data-related messages such as blocks, +// transactions, and inventory, the data is handled by the corresponding +// message handlers. The data flow for outbound messages is split into 2 +// goroutines, queueHandler and outHandler. The first, queueHandler, is used +// as a way for external entities to queue messages, by way of the QueueMessage +// function, quickly regardless of whether the peer is currently sending or not. +// It acts as the traffic cop between the external world and the actual +// goroutine which writes to the network socket. + +// Peer provides a basic concurrent safe bitcoin peer for handling bitcoin +// communications via the peer-to-peer protocol. It provides full duplex +// reading and writing, automatic handling of the initial handshake process, +// querying of usage statistics and other information about the remote peer such +// as its address, user agent, and protocol version, output message queuing, +// inventory trickling, and the ability to dynamically register and unregister +// callbacks for handling bitcoin protocol messages. +// +// Outbound messages are typically queued via QueueMessage or QueueInventory. +// QueueMessage is intended for all messages, including responses to data such +// as blocks and transactions. QueueInventory, on the other hand, is only +// intended for relaying inventory as it employs a trickling mechanism to batch +// the inventory together. However, some helper functions for pushing messages +// of specific types that typically require common special handling are +// provided as a convenience. +type Peer struct { + // The following variables must only be used atomically. + connected int32 + disconnect int32 + bytesReceived uint64 + bytesSent uint64 + + conn net.Conn + + // These fields are set at creation time and never modified, so they are + // safe to read from concurrently without a mutex. + addr string + cfg Config + inbound bool + + flagsMtx sync.Mutex // protects the peer flags below + na *wire.NetAddress + id int32 + userAgent string + services wire.ServiceFlag + versionKnown bool + protocolVersion uint32 + sendHeadersPreferred bool // peer sent a sendheaders message + versionSent bool + verAckReceived bool + + knownInventory *mruInventoryMap + prevGetBlocksMtx sync.Mutex + prevGetBlocksBegin *wire.ShaHash + prevGetBlocksStop *wire.ShaHash + prevGetHdrsMtx sync.Mutex + prevGetHdrsBegin *wire.ShaHash + prevGetHdrsStop *wire.ShaHash + + // These fields keep track of statistics for the peer and are protected + // by the statsMtx mutex. + statsMtx sync.RWMutex + timeOffset int64 + timeConnected time.Time + lastSend time.Time + lastRecv time.Time + startingHeight int32 + lastBlock int32 + lastAnnouncedBlock *wire.ShaHash + lastPingNonce uint64 // Set to nonce if we have a pending ping. + lastPingTime time.Time // Time we sent last ping. + lastPingMicros int64 // Time for last ping to return. + + stallControl chan stallControlMsg + outputQueue chan outMsg + sendQueue chan outMsg + sendDoneQueue chan struct{} + outputInvChan chan *wire.InvVect + inQuit chan struct{} + queueQuit chan struct{} + outQuit chan struct{} + quit chan struct{} +} + +// String returns the peer's address and directionality as a human-readable +// string. +// +// This function is safe for concurrent access. +func (p *Peer) String() string { + return fmt.Sprintf("%s (%s)", p.addr, directionString(p.inbound)) +} + +// UpdateLastBlockHeight updates the last known block for the peer. +// +// This function is safe for concurrent access. +func (p *Peer) UpdateLastBlockHeight(newHeight int32) { + p.statsMtx.Lock() + log.Tracef("Updating last block height of peer %v from %v to %v", + p.addr, p.lastBlock, newHeight) + p.lastBlock = int32(newHeight) + p.statsMtx.Unlock() +} + +// UpdateLastAnnouncedBlock updates meta-data about the last block sha this +// peer is known to have announced. +// +// This function is safe for concurrent access. +func (p *Peer) UpdateLastAnnouncedBlock(blkSha *wire.ShaHash) { + log.Tracef("Updating last blk for peer %v, %v", p.addr, blkSha) + + p.statsMtx.Lock() + p.lastAnnouncedBlock = blkSha + p.statsMtx.Unlock() +} + +// AddKnownInventory adds the passed inventory to the cache of known inventory +// for the peer. +// +// This function is safe for concurrent access. +func (p *Peer) AddKnownInventory(invVect *wire.InvVect) { + p.knownInventory.Add(invVect) +} + +// StatsSnapshot returns a snapshot of the current peer flags and statistics. +// +// This function is safe for concurrent access. +func (p *Peer) StatsSnapshot() *StatsSnap { + p.statsMtx.RLock() + defer p.statsMtx.RUnlock() + + p.flagsMtx.Lock() + id := p.id + addr := p.addr + userAgent := p.userAgent + services := p.services + protocolVersion := p.protocolVersion + p.flagsMtx.Unlock() + + // Get a copy of all relevant flags and stats. + return &StatsSnap{ + ID: id, + Addr: addr, + UserAgent: userAgent, + Services: services, + LastSend: p.lastSend, + LastRecv: p.lastRecv, + BytesSent: atomic.LoadUint64(&p.bytesSent), + BytesRecv: atomic.LoadUint64(&p.bytesReceived), + ConnTime: p.timeConnected, + TimeOffset: p.timeOffset, + Version: protocolVersion, + Inbound: p.inbound, + StartingHeight: p.startingHeight, + LastBlock: p.lastBlock, + LastPingNonce: p.lastPingNonce, + LastPingMicros: p.lastPingMicros, + LastPingTime: p.lastPingTime, + } +} + +// ID returns the peer id. +// +// This function is safe for concurrent access. +func (p *Peer) ID() int32 { + p.flagsMtx.Lock() + defer p.flagsMtx.Unlock() + + return p.id +} + +// NA returns the peer network address. +// +// This function is safe for concurrent access. +func (p *Peer) NA() *wire.NetAddress { + p.flagsMtx.Lock() + defer p.flagsMtx.Unlock() + + return p.na +} + +// Addr returns the peer address. +// +// This function is safe for concurrent access. +func (p *Peer) Addr() string { + // The address doesn't change after initialization, therefore it is not + // protected by a mutex. + return p.addr +} + +// Inbound returns whether the peer is inbound. +// +// This function is safe for concurrent access. +func (p *Peer) Inbound() bool { + return p.inbound +} + +// Services returns the services flag of the remote peer. +// +// This function is safe for concurrent access. +func (p *Peer) Services() wire.ServiceFlag { + p.flagsMtx.Lock() + defer p.flagsMtx.Unlock() + + return p.services +} + +// UserAgent returns the user agent of the remote peer. +// +// This function is safe for concurrent access. +func (p *Peer) UserAgent() string { + p.flagsMtx.Lock() + defer p.flagsMtx.Unlock() + + return p.userAgent +} + +// LastAnnouncedBlock returns the last announced block of the remote peer. +// +// This function is safe for concurrent access. +func (p *Peer) LastAnnouncedBlock() *wire.ShaHash { + p.statsMtx.RLock() + defer p.statsMtx.RUnlock() + + return p.lastAnnouncedBlock +} + +// LastPingNonce returns the last ping nonce of the remote peer. +// +// This function is safe for concurrent access. +func (p *Peer) LastPingNonce() uint64 { + p.statsMtx.RLock() + defer p.statsMtx.RUnlock() + + return p.lastPingNonce +} + +// LastPingTime returns the last ping time of the remote peer. +// +// This function is safe for concurrent access. +func (p *Peer) LastPingTime() time.Time { + p.statsMtx.RLock() + defer p.statsMtx.RUnlock() + + return p.lastPingTime +} + +// LastPingMicros returns the last ping micros of the remote peer. +// +// This function is safe for concurrent access. +func (p *Peer) LastPingMicros() int64 { + p.statsMtx.RLock() + defer p.statsMtx.RUnlock() + + return p.lastPingMicros +} + +// VersionKnown returns the whether or not the version of a peer is known +// locally. +// +// This function is safe for concurrent access. +func (p *Peer) VersionKnown() bool { + p.flagsMtx.Lock() + defer p.flagsMtx.Unlock() + + return p.versionKnown +} + +// VerAckReceived returns whether or not a verack message was received by the +// peer. +// +// This function is safe for concurrent access. +func (p *Peer) VerAckReceived() bool { + p.flagsMtx.Lock() + defer p.flagsMtx.Unlock() + + return p.verAckReceived +} + +// ProtocolVersion returns the peer protocol version. +// +// This function is safe for concurrent access. +func (p *Peer) ProtocolVersion() uint32 { + p.flagsMtx.Lock() + defer p.flagsMtx.Unlock() + + return p.protocolVersion +} + +// LastBlock returns the last block of the peer. +// +// This function is safe for concurrent access. +func (p *Peer) LastBlock() int32 { + p.statsMtx.RLock() + defer p.statsMtx.RUnlock() + + return p.lastBlock +} + +// LastSend returns the last send time of the peer. +// +// This function is safe for concurrent access. +func (p *Peer) LastSend() time.Time { + p.statsMtx.RLock() + defer p.statsMtx.RUnlock() + + return p.lastSend +} + +// LastRecv returns the last recv time of the peer. +// +// This function is safe for concurrent access. +func (p *Peer) LastRecv() time.Time { + p.statsMtx.RLock() + defer p.statsMtx.RUnlock() + + return p.lastRecv +} + +// BytesSent returns the total number of bytes sent by the peer. +// +// This function is safe for concurrent access. +func (p *Peer) BytesSent() uint64 { + return atomic.LoadUint64(&p.bytesSent) +} + +// BytesReceived returns the total number of bytes received by the peer. +// +// This function is safe for concurrent access. +func (p *Peer) BytesReceived() uint64 { + return atomic.LoadUint64(&p.bytesReceived) +} + +// TimeConnected returns the time at which the peer connected. +// +// This function is safe for concurrent access. +func (p *Peer) TimeConnected() time.Time { + p.statsMtx.RLock() + defer p.statsMtx.RUnlock() + + return p.timeConnected +} + +// TimeOffset returns the number of seconds the local time was offset from the +// time the peer reported during the initial negotiation phase. Negative values +// indicate the remote peer's time is before the local time. +// +// This function is safe for concurrent access. +func (p *Peer) TimeOffset() int64 { + p.statsMtx.RLock() + defer p.statsMtx.RUnlock() + + return p.timeOffset +} + +// StartingHeight returns the last known height the peer reported during the +// initial negotiation phase. +// +// This function is safe for concurrent access. +func (p *Peer) StartingHeight() int32 { + p.statsMtx.RLock() + defer p.statsMtx.RUnlock() + + return p.startingHeight +} + +// WantsHeaders returns if the peer wants header messages instead of +// inventory vectors for blocks. +// +// This function is safe for concurrent access. +func (p *Peer) WantsHeaders() bool { + p.flagsMtx.Lock() + defer p.flagsMtx.Unlock() + + return p.sendHeadersPreferred +} + +// pushVersionMsg sends a version message to the connected peer using the +// current state. +func (p *Peer) pushVersionMsg() error { + var blockNum int32 + if p.cfg.NewestBlock != nil { + var err error + _, blockNum, err = p.cfg.NewestBlock() + if err != nil { + return err + } + } + + theirNA := p.na + + // If we are behind a proxy and the connection comes from the proxy then + // we return an unroutable address as their address. This is to prevent + // leaking the tor proxy address. + if p.cfg.Proxy != "" { + proxyaddress, _, err := net.SplitHostPort(p.cfg.Proxy) + // invalid proxy means poorly configured, be on the safe side. + if err != nil || p.na.IP.String() == proxyaddress { + theirNA = &wire.NetAddress{ + Timestamp: time.Now(), + IP: net.IP([]byte{0, 0, 0, 0}), + } + } + } + + // TODO(tuxcanfly): In case BestLocalAddress is nil, ourNA defaults to + // remote NA, which is wrong. Need to fix this. + ourNA := p.na + if p.cfg.BestLocalAddress != nil { + ourNA = p.cfg.BestLocalAddress(p.na) + } + + // Generate a unique nonce for this peer so self connections can be + // detected. This is accomplished by adding it to a size-limited map of + // recently seen nonces. + nonce, err := wire.RandomUint64() + if err != nil { + fmt.Println(err) + return err + } + sentNonces.Add(nonce) + + // Version message. + msg := wire.NewMsgVersion(ourNA, theirNA, nonce, int32(blockNum)) + msg.AddUserAgent(p.cfg.UserAgentName, p.cfg.UserAgentVersion) + + // XXX: bitcoind appears to always enable the full node services flag + // of the remote peer netaddress field in the version message regardless + // of whether it knows it supports it or not. Also, bitcoind sets + // the services field of the local peer to 0 regardless of support. + // + // Realistically, this should be set as follows: + // - For outgoing connections: + // - Set the local netaddress services to what the local peer + // actually supports + // - Set the remote netaddress services to 0 to indicate no services + // as they are still unknown + // - For incoming connections: + // - Set the local netaddress services to what the local peer + // actually supports + // - Set the remote netaddress services to the what was advertised by + // by the remote peer in its version message + msg.AddrYou.Services = wire.SFNodeNetwork + + // Advertise the services flag + msg.Services = p.cfg.Services + + // Advertise our max supported protocol version. + msg.ProtocolVersion = int32(p.ProtocolVersion()) + + // Advertise if inv messages for transactions are desired. + msg.DisableRelayTx = p.cfg.DisableRelayTx + + p.QueueMessage(msg, nil) + return nil +} + +// PushAddrMsg sends an addr message to the connected peer using the provided +// addresses. This function is useful over manually sending the message via +// QueueMessage since it automatically limits the addresses to the maximum +// number allowed by the message and randomizes the chosen addresses when there +// are too many. It returns the addresses that were actually sent and no +// message will be sent if there are no entries in the provided addresses slice. +// +// This function is safe for concurrent access. +func (p *Peer) PushAddrMsg(addresses []*wire.NetAddress) ([]*wire.NetAddress, error) { + + // Nothing to send. + if len(addresses) == 0 { + return nil, nil + } + + msg := wire.NewMsgAddr() + msg.AddrList = make([]*wire.NetAddress, len(addresses)) + copy(msg.AddrList, addresses) + + // Randomize the addresses sent if there are more than the maximum allowed. + if len(msg.AddrList) > wire.MaxAddrPerMsg { + // Shuffle the address list. + for i := range msg.AddrList { + j := rand.Intn(i + 1) + msg.AddrList[i], msg.AddrList[j] = msg.AddrList[j], msg.AddrList[i] + } + + // Truncate it to the maximum size. + msg.AddrList = msg.AddrList[:wire.MaxAddrPerMsg] + } + + p.QueueMessage(msg, nil) + return msg.AddrList, nil +} + +// PushGetBlocksMsg sends a getblocks message for the provided block locator +// and stop hash. It will ignore back-to-back duplicate requests. +// +// This function is safe for concurrent access. +func (p *Peer) PushGetBlocksMsg(locator blockchain.BlockLocator, stopHash *wire.ShaHash) error { + // Extract the begin hash from the block locator, if one was specified, + // to use for filtering duplicate getblocks requests. + var beginHash *wire.ShaHash + if len(locator) > 0 { + beginHash = locator[0] + } + + // Filter duplicate getblocks requests. + p.prevGetBlocksMtx.Lock() + isDuplicate := p.prevGetBlocksStop != nil && p.prevGetBlocksBegin != nil && + beginHash != nil && stopHash.IsEqual(p.prevGetBlocksStop) && + beginHash.IsEqual(p.prevGetBlocksBegin) + p.prevGetBlocksMtx.Unlock() + + if isDuplicate { + log.Tracef("Filtering duplicate [getblocks] with begin "+ + "hash %v, stop hash %v", beginHash, stopHash) + return nil + } + + // Construct the getblocks request and queue it to be sent. + msg := wire.NewMsgGetBlocks(stopHash) + for _, hash := range locator { + err := msg.AddBlockLocatorHash(hash) + if err != nil { + return err + } + } + p.QueueMessage(msg, nil) + + // Update the previous getblocks request information for filtering + // duplicates. + p.prevGetBlocksMtx.Lock() + p.prevGetBlocksBegin = beginHash + p.prevGetBlocksStop = stopHash + p.prevGetBlocksMtx.Unlock() + return nil +} + +// PushGetHeadersMsg sends a getblocks message for the provided block locator +// and stop hash. It will ignore back-to-back duplicate requests. +// +// This function is safe for concurrent access. +func (p *Peer) PushGetHeadersMsg(locator blockchain.BlockLocator, stopHash *wire.ShaHash) error { + // Extract the begin hash from the block locator, if one was specified, + // to use for filtering duplicate getheaders requests. + var beginHash *wire.ShaHash + if len(locator) > 0 { + beginHash = locator[0] + } + + // Filter duplicate getheaders requests. + p.prevGetHdrsMtx.Lock() + isDuplicate := p.prevGetHdrsStop != nil && p.prevGetHdrsBegin != nil && + beginHash != nil && stopHash.IsEqual(p.prevGetHdrsStop) && + beginHash.IsEqual(p.prevGetHdrsBegin) + p.prevGetHdrsMtx.Unlock() + + if isDuplicate { + log.Tracef("Filtering duplicate [getheaders] with begin "+ + "hash %v", beginHash) + return nil + } + + // Construct the getheaders request and queue it to be sent. + msg := wire.NewMsgGetHeaders() + msg.HashStop = *stopHash + for _, hash := range locator { + err := msg.AddBlockLocatorHash(hash) + if err != nil { + return err + } + } + p.QueueMessage(msg, nil) + + // Update the previous getheaders request information for filtering + // duplicates. + p.prevGetHdrsMtx.Lock() + p.prevGetHdrsBegin = beginHash + p.prevGetHdrsStop = stopHash + p.prevGetHdrsMtx.Unlock() + return nil +} + +// PushRejectMsg sends a reject message for the provided command, reject code, +// reject reason, and hash. The hash will only be used when the command is a tx +// or block and should be nil in other cases. The wait parameter will cause the +// function to block until the reject message has actually been sent. +// +// This function is safe for concurrent access. +func (p *Peer) PushRejectMsg(command string, code wire.RejectCode, reason string, hash *wire.ShaHash, wait bool) { + // Don't bother sending the reject message if the protocol version + // is too low. + if p.VersionKnown() && p.ProtocolVersion() < wire.RejectVersion { + return + } + + msg := wire.NewMsgReject(command, code, reason) + if command == wire.CmdTx || command == wire.CmdBlock { + if hash == nil { + log.Warnf("Sending a reject message for command "+ + "type %v which should have specified a hash "+ + "but does not", command) + hash = &zeroHash + } + msg.Hash = *hash + } + + // Send the message without waiting if the caller has not requested it. + if !wait { + p.QueueMessage(msg, nil) + return + } + + // Send the message and block until it has been sent before returning. + doneChan := make(chan struct{}, 1) + p.QueueMessage(msg, doneChan) + <-doneChan +} + +// handleVersionMsg is invoked when a peer receives a version bitcoin message +// and is used to negotiate the protocol version details as well as kick start +// the communications. +func (p *Peer) handleVersionMsg(msg *wire.MsgVersion) error { + // Detect self connections. + if !allowSelfConns && sentNonces.Exists(msg.Nonce) { + return errors.New("disconnecting peer connected to self") + } + + // Notify and disconnect clients that have a protocol version that is + // too old. + if msg.ProtocolVersion < int32(wire.MultipleAddressVersion) { + // Send a reject message indicating the protocol version is + // obsolete and wait for the message to be sent before + // disconnecting. + reason := fmt.Sprintf("protocol version must be %d or greater", + wire.MultipleAddressVersion) + p.PushRejectMsg(msg.Command(), wire.RejectObsolete, reason, + nil, true) + return errors.New(reason) + } + + // Limit to one version message per peer. + // No read lock is necessary because versionKnown is not written to in any + // other goroutine + if p.versionKnown { + // Send an reject message indicating the version message was + // incorrectly sent twice and wait for the message to be sent + // before disconnecting. + p.PushRejectMsg(msg.Command(), wire.RejectDuplicate, + "duplicate version message", nil, true) + return errors.New("only one version message per peer is allowed") + } + + // Updating a bunch of stats. + p.statsMtx.Lock() + p.lastBlock = msg.LastBlock + p.startingHeight = msg.LastBlock + // Set the peer's time offset. + p.timeOffset = msg.Timestamp.Unix() - time.Now().Unix() + p.statsMtx.Unlock() + + // Negotiate the protocol version. + p.flagsMtx.Lock() + p.protocolVersion = minUint32(p.protocolVersion, uint32(msg.ProtocolVersion)) + p.versionKnown = true + log.Debugf("Negotiated protocol version %d for peer %s", + p.protocolVersion, p) + // Set the peer's ID. + p.id = atomic.AddInt32(&nodeCount, 1) + // Set the supported services for the peer to what the remote peer + // advertised. + p.services = msg.Services + // Set the remote peer's user agent. + p.userAgent = msg.UserAgent + p.flagsMtx.Unlock() + + // Inbound connections. + if p.inbound { + // Set up a NetAddress for the peer to be used with AddrManager. + // We only do this inbound because outbound set this up + // at connection time and no point recomputing. + na, err := newNetAddress(p.conn.RemoteAddr(), p.services) + if err != nil { + return err + } + p.na = na + + // Send version. + err = p.pushVersionMsg() + if err != nil { + return err + } + } + + // Send verack. + p.QueueMessage(wire.NewMsgVerAck(), nil) + return nil +} + +// isValidBIP0111 is a helper function for the bloom filter commands to check +// BIP0111 compliance. +func (p *Peer) isValidBIP0111(cmd string) bool { + if p.Services()&wire.SFNodeBloom != wire.SFNodeBloom { + if p.ProtocolVersion() >= wire.BIP0111Version { + log.Debugf("%s sent an unsupported %s "+ + "request -- disconnecting", p, cmd) + p.Disconnect() + } else { + log.Debugf("Ignoring %s request from %s -- bloom "+ + "support is disabled", cmd, p) + } + return false + } + + return true +} + +// handlePingMsg is invoked when a peer receives a ping bitcoin message. For +// recent clients (protocol version > BIP0031Version), it replies with a pong +// message. For older clients, it does nothing and anything other than failure +// is considered a successful ping. +func (p *Peer) handlePingMsg(msg *wire.MsgPing) { + // Only reply with pong if the message is from a new enough client. + if p.ProtocolVersion() > wire.BIP0031Version { + // Include nonce from ping so pong can be identified. + p.QueueMessage(wire.NewMsgPong(msg.Nonce), nil) + } +} + +// handlePongMsg is invoked when a peer receives a pong bitcoin message. It +// updates the ping statistics as required for recent clients (protocol +// version > BIP0031Version). There is no effect for older clients or when a +// ping was not previously sent. +func (p *Peer) handlePongMsg(msg *wire.MsgPong) { + p.statsMtx.Lock() + defer p.statsMtx.Unlock() + + // Arguably we could use a buffered channel here sending data + // in a fifo manner whenever we send a ping, or a list keeping track of + // the times of each ping. For now we just make a best effort and + // only record stats if it was for the last ping sent. Any preceding + // and overlapping pings will be ignored. It is unlikely to occur + // without large usage of the ping rpc call since we ping infrequently + // enough that if they overlap we would have timed out the peer. + if p.ProtocolVersion() > wire.BIP0031Version && p.lastPingNonce != 0 && + msg.Nonce == p.lastPingNonce { + + p.lastPingMicros = time.Now().Sub(p.lastPingTime).Nanoseconds() + p.lastPingMicros /= 1000 // convert to usec. + p.lastPingNonce = 0 + } +} + +// readMessage reads the next bitcoin message from the peer with logging. +func (p *Peer) readMessage() (wire.Message, []byte, error) { + n, msg, buf, err := wire.ReadMessageN(p.conn, p.ProtocolVersion(), + p.cfg.ChainParams.Net) + atomic.AddUint64(&p.bytesReceived, uint64(n)) + if p.cfg.Listeners.OnRead != nil { + p.cfg.Listeners.OnRead(p, n, msg, err) + } + if err != nil { + return nil, nil, err + } + + // Use closures to log expensive operations so they are only run when + // the logging level requires it. + log.Debugf("%v", newLogClosure(func() string { + // Debug summary of message. + summary := messageSummary(msg) + if len(summary) > 0 { + summary = " (" + summary + ")" + } + return fmt.Sprintf("Received %v%s from %s", + msg.Command(), summary, p) + })) + log.Tracef("%v", newLogClosure(func() string { + return spew.Sdump(msg) + })) + log.Tracef("%v", newLogClosure(func() string { + return spew.Sdump(buf) + })) + + return msg, buf, nil +} + +// writeMessage sends a bitcoin message to the peer with logging. +func (p *Peer) writeMessage(msg wire.Message) error { + // Don't do anything if we're disconnecting. + if atomic.LoadInt32(&p.disconnect) != 0 { + return nil + } + if !p.VersionKnown() { + switch msg.(type) { + case *wire.MsgVersion: + // This is OK. + case *wire.MsgReject: + // This is OK. + default: + // Drop all messages other than version and reject if + // the handshake has not already been done. + return nil + } + } + + // Use closures to log expensive operations so they are only run when + // the logging level requires it. + log.Debugf("%v", newLogClosure(func() string { + // Debug summary of message. + summary := messageSummary(msg) + if len(summary) > 0 { + summary = " (" + summary + ")" + } + return fmt.Sprintf("Sending %v%s to %s", msg.Command(), + summary, p) + })) + log.Tracef("%v", newLogClosure(func() string { + return spew.Sdump(msg) + })) + log.Tracef("%v", newLogClosure(func() string { + var buf bytes.Buffer + err := wire.WriteMessage(&buf, msg, p.ProtocolVersion(), + p.cfg.ChainParams.Net) + if err != nil { + return err.Error() + } + return spew.Sdump(buf.Bytes()) + })) + + // Write the message to the peer. + n, err := wire.WriteMessageN(p.conn, msg, p.ProtocolVersion(), + p.cfg.ChainParams.Net) + atomic.AddUint64(&p.bytesSent, uint64(n)) + if p.cfg.Listeners.OnWrite != nil { + p.cfg.Listeners.OnWrite(p, n, msg, err) + } + return err +} + +// isAllowedByRegression returns whether or not the passed error is allowed by +// regression tests without disconnecting the peer. In particular, regression +// tests need to be allowed to send malformed messages without the peer being +// disconnected. +func (p *Peer) isAllowedByRegression(err error) bool { + // Don't allow the error if it's not specifically a malformed message + // error. + if _, ok := err.(*wire.MessageError); !ok { + return false + } + + // Don't allow the error if it's not coming from localhost or the + // hostname can't be determined for some reason. + host, _, err := net.SplitHostPort(p.addr) + if err != nil { + return false + } + + if host != "127.0.0.1" && host != "localhost" { + return false + } + + // Allowed if all checks passed. + return true +} + +// isRegTestNetwork returns whether or not the peer is running on the regression +// test network. +func (p *Peer) isRegTestNetwork() bool { + return p.cfg.ChainParams.Net == wire.TestNet +} + +// shouldHandleReadError returns whether or not the passed error, which is +// expected to have come from reading from the remote peer in the inHandler, +// should be logged and responded to with a reject message. +func (p *Peer) shouldHandleReadError(err error) bool { + // No logging or reject message when the peer is being forcibly + // disconnected. + if atomic.LoadInt32(&p.disconnect) != 0 { + return false + } + + // No logging or reject message when the remote peer has been + // disconnected. + if err == io.EOF { + return false + } + if opErr, ok := err.(*net.OpError); ok && !opErr.Temporary() { + return false + } + + return true +} + +// maybeAddDeadline potentially adds a deadline for the appropriate expected +// response for the passed wire protocol command to the pending responses map. +func (p *Peer) maybeAddDeadline(pendingResponses map[string]time.Time, msgCmd string) { + // Setup a deadline for each message being sent that expects a response. + // + // NOTE: Pings are intentionally ignored here since they are typically + // sent asynchronously and as a result of a long backlock of messages, + // such as is typical in the case of initial block download, the + // response won't be received in time. + deadline := time.Now().Add(stallResponseTimeout) + switch msgCmd { + case wire.CmdVersion: + // Expects a verack message. + pendingResponses[wire.CmdVerAck] = deadline + + case wire.CmdMemPool: + // Expects an inv message. + pendingResponses[wire.CmdInv] = deadline + + case wire.CmdGetBlocks: + // Expects an inv message. + pendingResponses[wire.CmdInv] = deadline + + case wire.CmdGetData: + // Expects a block, tx, or notfound message. + pendingResponses[wire.CmdBlock] = deadline + pendingResponses[wire.CmdTx] = deadline + pendingResponses[wire.CmdNotFound] = deadline + + case wire.CmdGetHeaders: + // Expects a headers message. Use a longer deadline since it + // can take a while for the remote peer to load all of the + // headers. + deadline = time.Now().Add(stallResponseTimeout * 3) + pendingResponses[wire.CmdHeaders] = deadline + } +} + +// stallHandler handles stall detection for the peer. This entails keeping +// track of expected responses and assigning them deadlines while accounting for +// the time spent in callbacks. It must be run as a goroutine. +func (p *Peer) stallHandler() { + // These variables are used to adjust the deadline times forward by the + // time it takes callbacks to execute. This is done because new + // messages aren't read until the previous one is finished processing + // (which includes callbacks), so the deadline for receiving a response + // for a given message must account for the processing time as well. + var handlerActive bool + var handlersStartTime time.Time + var deadlineOffset time.Duration + + // pendingResponses tracks the expected response deadline times. + pendingResponses := make(map[string]time.Time) + + // stallTicker is used to periodically check pending responses that have + // exceeded the expected deadline and disconnect the peer due to + // stalling. + stallTicker := time.NewTicker(stallTickInterval) + defer stallTicker.Stop() + + // ioStopped is used to detect when both the input and output handler + // goroutines are done. + var ioStopped bool +out: + for { + select { + case msg := <-p.stallControl: + switch msg.command { + case sccSendMessage: + // Add a deadline for the expected response + // message if needed. + p.maybeAddDeadline(pendingResponses, + msg.message.Command()) + + case sccReceiveMessage: + // Remove received messages from the expected + // response map. Since certain commands expect + // one of a group of responses, remove + // everything in the expected group accordingly. + switch msgCmd := msg.message.Command(); msgCmd { + case wire.CmdBlock: + fallthrough + case wire.CmdTx: + fallthrough + case wire.CmdNotFound: + delete(pendingResponses, wire.CmdBlock) + delete(pendingResponses, wire.CmdTx) + delete(pendingResponses, wire.CmdNotFound) + + default: + delete(pendingResponses, msgCmd) + } + + case sccHandlerStart: + // Warn on unbalanced callback signalling. + if handlerActive { + log.Warn("Received handler start " + + "control command while a " + + "handler is already active") + continue + } + + handlerActive = true + handlersStartTime = time.Now() + + case sccHandlerDone: + // Warn on unbalanced callback signalling. + if !handlerActive { + log.Warn("Received handler done " + + "control command when a " + + "handler is not already active") + continue + } + + // Extend active deadlines by the time it took + // to execute the callback. + duration := time.Now().Sub(handlersStartTime) + deadlineOffset += duration + handlerActive = false + + default: + log.Warnf("Unsupported message command %v", + msg.command) + } + + case <-stallTicker.C: + // Calculate the offset to apply to the deadline based + // on how long the handlers have taken to execute since + // the last tick. + now := time.Now() + offset := deadlineOffset + if handlerActive { + offset += now.Sub(handlersStartTime) + } + + // Disconnect the peer if any of the pending responses + // don't arrive by their adjusted deadline. + for command, deadline := range pendingResponses { + if now.Before(deadline.Add(offset)) { + continue + } + + log.Debugf("Peer %s appears to be stalled or "+ + "misbehaving, %s timeout -- "+ + "disconnecting", p, command) + p.Disconnect() + break + } + + // Reset the deadline offset for the next tick. + deadlineOffset = 0 + + case <-p.inQuit: + // The stall handler can exit once both the input and + // output handler goroutines are done. + if ioStopped { + break out + } + ioStopped = true + + case <-p.outQuit: + // The stall handler can exit once both the input and + // output handler goroutines are done. + if ioStopped { + break out + } + ioStopped = true + } + } + + // Drain any wait channels before going away so there is nothing left + // waiting on this goroutine. +cleanup: + for { + select { + case <-p.stallControl: + default: + break cleanup + } + } + log.Tracef("Peer stall handler done for %s", p) +} + +// inHandler handles all incoming messages for the peer. It must be run as a +// goroutine. +func (p *Peer) inHandler() { + // Peers must complete the initial version negotiation within a shorter + // timeframe than a general idle timeout. The timer is then reset below + // to idleTimeout for all future messages. + idleTimer := time.AfterFunc(negotiateTimeout, func() { + if p.VersionKnown() { + log.Warnf("Peer %s no answer for %s -- disconnecting", + p, idleTimeout) + } else { + log.Debugf("Peer %s no valid version message for %s -- "+ + "disconnecting", p, negotiateTimeout) + } + p.Disconnect() + }) + +out: + for atomic.LoadInt32(&p.disconnect) == 0 { + // Read a message and stop the idle timer as soon as the read + // is done. The timer is reset below for the next iteration if + // needed. + rmsg, buf, err := p.readMessage() + idleTimer.Stop() + if err != nil { + // In order to allow regression tests with malformed + // messages, don't disconnect the peer when we're in + // regression test mode and the error is one of the + // allowed errors. + if p.isRegTestNetwork() && p.isAllowedByRegression(err) { + log.Errorf("Allowed regression test error "+ + "from %s: %v", p, err) + idleTimer.Reset(idleTimeout) + continue + } + + // Only log the error and send reject message if the + // local peer is not forcibly disconnecting and the + // remote peer has not disconnected. + if p.shouldHandleReadError(err) { + errMsg := fmt.Sprintf("Can't read message "+ + "from %s: %v", p, err) + log.Errorf(errMsg) + + // Push a reject message for the malformed + // message and wait for the message to be sent + // before disconnecting. + // + // NOTE: Ideally this would include the command + // in the header if at least that much of the + // message was valid, but that is not currently + // exposed by wire, so just used malformed for + // the command. + p.PushRejectMsg("malformed", + wire.RejectMalformed, errMsg, nil, true) + } + break out + } + p.statsMtx.Lock() + p.lastRecv = time.Now() + p.statsMtx.Unlock() + p.stallControl <- stallControlMsg{sccReceiveMessage, rmsg} + + // Ensure version message comes first. + if vmsg, ok := rmsg.(*wire.MsgVersion); !ok && !p.VersionKnown() { + errStr := "A version message must precede all others" + log.Errorf(errStr) + + // Push a reject message and wait for the message to be + // sent before disconnecting. + p.PushRejectMsg(vmsg.Command(), wire.RejectMalformed, + errStr, nil, true) + break out + } + + // Handle each supported message type. + p.stallControl <- stallControlMsg{sccHandlerStart, rmsg} + switch msg := rmsg.(type) { + case *wire.MsgVersion: + err := p.handleVersionMsg(msg) + if err != nil { + log.Debugf("New peer %v - error negotiating protocol: %v", + p, err) + p.Disconnect() + break out + } + if p.cfg.Listeners.OnVersion != nil { + p.cfg.Listeners.OnVersion(p, msg) + } + + case *wire.MsgVerAck: + p.flagsMtx.Lock() + versionSent := p.versionSent + p.flagsMtx.Unlock() + if !versionSent { + log.Infof("Received 'verack' from peer %v "+ + "before version was sent -- "+ + "disconnecting", p) + break out + } + + // No read lock is necessary because verAckReceived is + // not written to in any other goroutine. + if p.verAckReceived { + log.Infof("Already received 'verack' from "+ + "peer %v -- disconnecting", p) + break out + } + p.flagsMtx.Lock() + p.verAckReceived = true + p.flagsMtx.Unlock() + if p.cfg.Listeners.OnVerAck != nil { + p.cfg.Listeners.OnVerAck(p, msg) + } + + case *wire.MsgGetAddr: + if p.cfg.Listeners.OnGetAddr != nil { + p.cfg.Listeners.OnGetAddr(p, msg) + } + + case *wire.MsgAddr: + if p.cfg.Listeners.OnAddr != nil { + p.cfg.Listeners.OnAddr(p, msg) + } + + case *wire.MsgPing: + p.handlePingMsg(msg) + if p.cfg.Listeners.OnPing != nil { + p.cfg.Listeners.OnPing(p, msg) + } + + case *wire.MsgPong: + p.handlePongMsg(msg) + if p.cfg.Listeners.OnPong != nil { + p.cfg.Listeners.OnPong(p, msg) + } + + case *wire.MsgAlert: + if p.cfg.Listeners.OnAlert != nil { + p.cfg.Listeners.OnAlert(p, msg) + } + + case *wire.MsgMemPool: + if p.cfg.Listeners.OnMemPool != nil { + p.cfg.Listeners.OnMemPool(p, msg) + } + + case *wire.MsgTx: + if p.cfg.Listeners.OnTx != nil { + p.cfg.Listeners.OnTx(p, msg) + } + + case *wire.MsgBlock: + if p.cfg.Listeners.OnBlock != nil { + p.cfg.Listeners.OnBlock(p, msg, buf) + } + + case *wire.MsgInv: + if p.cfg.Listeners.OnInv != nil { + p.cfg.Listeners.OnInv(p, msg) + } + + case *wire.MsgHeaders: + if p.cfg.Listeners.OnHeaders != nil { + p.cfg.Listeners.OnHeaders(p, msg) + } + + case *wire.MsgNotFound: + if p.cfg.Listeners.OnNotFound != nil { + p.cfg.Listeners.OnNotFound(p, msg) + } + + case *wire.MsgGetData: + if p.cfg.Listeners.OnGetData != nil { + p.cfg.Listeners.OnGetData(p, msg) + } + + case *wire.MsgGetBlocks: + if p.cfg.Listeners.OnGetBlocks != nil { + p.cfg.Listeners.OnGetBlocks(p, msg) + } + + case *wire.MsgGetHeaders: + if p.cfg.Listeners.OnGetHeaders != nil { + p.cfg.Listeners.OnGetHeaders(p, msg) + } + + case *wire.MsgFilterAdd: + if p.isValidBIP0111(msg.Command()) && p.cfg.Listeners.OnFilterAdd != nil { + p.cfg.Listeners.OnFilterAdd(p, msg) + } + + case *wire.MsgFilterClear: + if p.isValidBIP0111(msg.Command()) && p.cfg.Listeners.OnFilterClear != nil { + p.cfg.Listeners.OnFilterClear(p, msg) + } + + case *wire.MsgFilterLoad: + if p.isValidBIP0111(msg.Command()) && p.cfg.Listeners.OnFilterLoad != nil { + p.cfg.Listeners.OnFilterLoad(p, msg) + } + + case *wire.MsgMerkleBlock: + if p.cfg.Listeners.OnMerkleBlock != nil { + p.cfg.Listeners.OnMerkleBlock(p, msg) + } + + case *wire.MsgReject: + if p.cfg.Listeners.OnReject != nil { + p.cfg.Listeners.OnReject(p, msg) + } + + case *wire.MsgSendHeaders: + p.flagsMtx.Lock() + p.sendHeadersPreferred = true + p.flagsMtx.Unlock() + + if p.cfg.Listeners.OnSendHeaders != nil { + p.cfg.Listeners.OnSendHeaders(p, msg) + } + + default: + log.Debugf("Received unhandled message of type %v "+ + "from %v", rmsg.Command(), p) + } + p.stallControl <- stallControlMsg{sccHandlerDone, rmsg} + + // A message was received so reset the idle timer. + idleTimer.Reset(idleTimeout) + } + + // Ensure the idle timer is stopped to avoid leaking the resource. + idleTimer.Stop() + + // Ensure connection is closed. + p.Disconnect() + + close(p.inQuit) + log.Tracef("Peer input handler done for %s", p) +} + +// queueHandler handles the queuing of outgoing data for the peer. This runs as +// a muxer for various sources of input so we can ensure that server and peer +// handlers will not block on us sending a message. That data is then passed on +// to outHandler to be actually written. +func (p *Peer) queueHandler() { + pendingMsgs := list.New() + invSendQueue := list.New() + trickleTicker := time.NewTicker(trickleTimeout) + defer trickleTicker.Stop() + + // We keep the waiting flag so that we know if we have a message queued + // to the outHandler or not. We could use the presence of a head of + // the list for this but then we have rather racy concerns about whether + // it has gotten it at cleanup time - and thus who sends on the + // message's done channel. To avoid such confusion we keep a different + // flag and pendingMsgs only contains messages that we have not yet + // passed to outHandler. + waiting := false + + // To avoid duplication below. + queuePacket := func(msg outMsg, list *list.List, waiting bool) bool { + if !waiting { + p.sendQueue <- msg + } else { + list.PushBack(msg) + } + // we are always waiting now. + return true + } +out: + for { + select { + case msg := <-p.outputQueue: + waiting = queuePacket(msg, pendingMsgs, waiting) + + // This channel is notified when a message has been sent across + // the network socket. + case <-p.sendDoneQueue: + // No longer waiting if there are no more messages + // in the pending messages queue. + next := pendingMsgs.Front() + if next == nil { + waiting = false + continue + } + + // Notify the outHandler about the next item to + // asynchronously send. + val := pendingMsgs.Remove(next) + p.sendQueue <- val.(outMsg) + + case iv := <-p.outputInvChan: + // No handshake? They'll find out soon enough. + if p.VersionKnown() { + invSendQueue.PushBack(iv) + } + + case <-trickleTicker.C: + // Don't send anything if we're disconnecting or there + // is no queued inventory. + // version is known if send queue has any entries. + if atomic.LoadInt32(&p.disconnect) != 0 || + invSendQueue.Len() == 0 { + continue + } + + // Create and send as many inv messages as needed to + // drain the inventory send queue. + invMsg := wire.NewMsgInvSizeHint(uint(invSendQueue.Len())) + for e := invSendQueue.Front(); e != nil; e = invSendQueue.Front() { + iv := invSendQueue.Remove(e).(*wire.InvVect) + + // Don't send inventory that became known after + // the initial check. + if p.knownInventory.Exists(iv) { + continue + } + + invMsg.AddInvVect(iv) + if len(invMsg.InvList) >= maxInvTrickleSize { + waiting = queuePacket( + outMsg{msg: invMsg}, + pendingMsgs, waiting) + invMsg = wire.NewMsgInvSizeHint(uint(invSendQueue.Len())) + } + + // Add the inventory that is being relayed to + // the known inventory for the peer. + p.AddKnownInventory(iv) + } + if len(invMsg.InvList) > 0 { + waiting = queuePacket(outMsg{msg: invMsg}, + pendingMsgs, waiting) + } + + case <-p.quit: + break out + } + } + + // Drain any wait channels before we go away so we don't leave something + // waiting for us. + for e := pendingMsgs.Front(); e != nil; e = pendingMsgs.Front() { + val := pendingMsgs.Remove(e) + msg := val.(outMsg) + if msg.doneChan != nil { + msg.doneChan <- struct{}{} + } + } +cleanup: + for { + select { + case msg := <-p.outputQueue: + if msg.doneChan != nil { + msg.doneChan <- struct{}{} + } + case <-p.outputInvChan: + // Just drain channel + // sendDoneQueue is buffered so doesn't need draining. + default: + break cleanup + } + } + close(p.queueQuit) + log.Tracef("Peer queue handler done for %s", p) +} + +// shouldLogWriteError returns whether or not the passed error, which is +// expected to have come from writing to the remote peer in the outHandler, +// should be logged. +func (p *Peer) shouldLogWriteError(err error) bool { + // No logging when the peer is being forcibly disconnected. + if atomic.LoadInt32(&p.disconnect) != 0 { + return false + } + + // No logging when the remote peer has been disconnected. + if err == io.EOF { + return false + } + if opErr, ok := err.(*net.OpError); ok && !opErr.Temporary() { + return false + } + + return true +} + +// outHandler handles all outgoing messages for the peer. It must be run as a +// goroutine. It uses a buffered channel to serialize output messages while +// allowing the sender to continue running asynchronously. +func (p *Peer) outHandler() { + // pingTicker is used to periodically send pings to the remote peer. + pingTicker := time.NewTicker(pingInterval) + defer pingTicker.Stop() + +out: + for { + select { + case msg := <-p.sendQueue: + switch m := msg.msg.(type) { + case *wire.MsgVersion: + // Set the flag which indicates the version has + // been sent. + p.flagsMtx.Lock() + p.versionSent = true + p.flagsMtx.Unlock() + + case *wire.MsgPing: + // Only expects a pong message in later protocol + // versions. Also set up statistics. + if p.ProtocolVersion() > wire.BIP0031Version { + p.statsMtx.Lock() + p.lastPingNonce = m.Nonce + p.lastPingTime = time.Now() + p.statsMtx.Unlock() + } + } + + p.stallControl <- stallControlMsg{sccSendMessage, msg.msg} + err := p.writeMessage(msg.msg) + if err != nil { + p.Disconnect() + if p.shouldLogWriteError(err) { + log.Errorf("Failed to send message to "+ + "%s: %v", p, err) + } + if msg.doneChan != nil { + msg.doneChan <- struct{}{} + } + continue + } + + // At this point, the message was successfully sent, so + // update the last send time, signal the sender of the + // message that it has been sent (if requested), and + // signal the send queue to the deliver the next queued + // message. + p.statsMtx.Lock() + p.lastSend = time.Now() + p.statsMtx.Unlock() + if msg.doneChan != nil { + msg.doneChan <- struct{}{} + } + p.sendDoneQueue <- struct{}{} + + case <-pingTicker.C: + nonce, err := wire.RandomUint64() + if err != nil { + log.Errorf("Not sending ping to %s: %v", p, err) + continue + } + p.QueueMessage(wire.NewMsgPing(nonce), nil) + + case <-p.quit: + break out + } + } + + <-p.queueQuit + + // Drain any wait channels before we go away so we don't leave something + // waiting for us. We have waited on queueQuit and thus we can be sure + // that we will not miss anything sent on sendQueue. +cleanup: + for { + select { + case msg := <-p.sendQueue: + if msg.doneChan != nil { + msg.doneChan <- struct{}{} + } + // no need to send on sendDoneQueue since queueHandler + // has been waited on and already exited. + default: + break cleanup + } + } + close(p.outQuit) + log.Tracef("Peer output handler done for %s", p) +} + +// QueueMessage adds the passed bitcoin message to the peer send queue. +// +// This function is safe for concurrent access. +func (p *Peer) QueueMessage(msg wire.Message, doneChan chan<- struct{}) { + // Avoid risk of deadlock if goroutine already exited. The goroutine + // we will be sending to hangs around until it knows for a fact that + // it is marked as disconnected and *then* it drains the channels. + if !p.Connected() { + if doneChan != nil { + go func() { + doneChan <- struct{}{} + }() + } + return + } + p.outputQueue <- outMsg{msg: msg, doneChan: doneChan} +} + +// QueueInventory adds the passed inventory to the inventory send queue which +// might not be sent right away, rather it is trickled to the peer in batches. +// Inventory that the peer is already known to have is ignored. +// +// This function is safe for concurrent access. +func (p *Peer) QueueInventory(invVect *wire.InvVect) { + // Don't add the inventory to the send queue if the peer is already + // known to have it. + if p.knownInventory.Exists(invVect) { + return + } + + // Avoid risk of deadlock if goroutine already exited. The goroutine + // we will be sending to hangs around until it knows for a fact that + // it is marked as disconnected and *then* it drains the channels. + if !p.Connected() { + return + } + + p.outputInvChan <- invVect +} + +// Connect uses the given conn to connect to the peer. Calling this function when +// the peer is already connected will have no effect. +func (p *Peer) Connect(conn net.Conn) error { + // Already connected? + if atomic.LoadInt32(&p.connected) != 0 { + return nil + } + + if p.inbound { + p.addr = conn.RemoteAddr().String() + } + p.conn = conn + p.timeConnected = time.Now() + + atomic.AddInt32(&p.connected, 1) + return p.start() +} + +// Connected returns whether or not the peer is currently connected. +// +// This function is safe for concurrent access. +func (p *Peer) Connected() bool { + return atomic.LoadInt32(&p.connected) != 0 && + atomic.LoadInt32(&p.disconnect) == 0 +} + +// Disconnect disconnects the peer by closing the connection. Calling this +// function when the peer is already disconnected or in the process of +// disconnecting will have no effect. +func (p *Peer) Disconnect() { + if atomic.AddInt32(&p.disconnect, 1) != 1 { + return + } + + log.Tracef("Disconnecting %s", p) + if atomic.LoadInt32(&p.connected) != 0 { + p.conn.Close() + } + close(p.quit) +} + +// Start begins processing input and output messages. It also sends the initial +// version message for outbound connections to start the negotiation process. +func (p *Peer) start() error { + log.Tracef("Starting peer %s", p) + + // Send an initial version message if this is an outbound connection. + if !p.inbound { + if err := p.pushVersionMsg(); err != nil { + log.Errorf("Can't send outbound version message %v", err) + p.Disconnect() + return err + } + } + + // Start processing input and output. + go p.stallHandler() + go p.inHandler() + go p.queueHandler() + go p.outHandler() + + return nil +} + +// WaitForDisconnect waits until the peer has completely disconnected and all +// resources are cleaned up. This will happen if either the local or remote +// side has been disconnected or the peer is forcibly disconnected via +// Disconnect. +func (p *Peer) WaitForDisconnect() { + <-p.quit +} + +// newPeerBase returns a new base bitcoin peer based on the inbound flag. This +// is used by the NewInboundPeer and NewOutboundPeer functions to perform base +// setup needed by both types of peers. +func newPeerBase(cfg *Config, inbound bool) *Peer { + // Default to the max supported protocol version. Override to the + // version specified by the caller if configured. + protocolVersion := uint32(MaxProtocolVersion) + if cfg.ProtocolVersion != 0 { + protocolVersion = cfg.ProtocolVersion + } + + // Set the chain parameters to testnet if the caller did not specify any. + if cfg.ChainParams == nil { + cfg.ChainParams = &chaincfg.TestNet3Params + } + + p := Peer{ + inbound: inbound, + knownInventory: newMruInventoryMap(maxKnownInventory), + stallControl: make(chan stallControlMsg, 1), // nonblocking sync + outputQueue: make(chan outMsg, outputBufferSize), + sendQueue: make(chan outMsg, 1), // nonblocking sync + sendDoneQueue: make(chan struct{}, 1), // nonblocking sync + outputInvChan: make(chan *wire.InvVect, outputBufferSize), + inQuit: make(chan struct{}), + queueQuit: make(chan struct{}), + outQuit: make(chan struct{}), + quit: make(chan struct{}), + cfg: *cfg, // Copy so caller can't mutate. + services: cfg.Services, + protocolVersion: protocolVersion, + } + return &p +} + +// NewInboundPeer returns a new inbound bitcoin peer. Use Start to begin +// processing incoming and outgoing messages. +func NewInboundPeer(cfg *Config) *Peer { + return newPeerBase(cfg, true) +} + +// NewOutboundPeer returns a new outbound bitcoin peer. +func NewOutboundPeer(cfg *Config, addr string) (*Peer, error) { + p := newPeerBase(cfg, false) + p.addr = addr + + host, portStr, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + + port, err := strconv.ParseUint(portStr, 10, 16) + if err != nil { + return nil, err + } + + if cfg.HostToNetAddress != nil { + na, err := cfg.HostToNetAddress(host, uint16(port), cfg.Services) + if err != nil { + return nil, err + } + p.na = na + } else { + p.na = wire.NewNetAddressIPPort(net.ParseIP(host), uint16(port), + cfg.Services) + } + + return p, nil +} + +func init() { + rand.Seed(time.Now().UnixNano()) +} diff --git a/vendor/github.com/btcsuite/btcd/peer/peer_test.go b/vendor/github.com/btcsuite/btcd/peer/peer_test.go new file mode 100644 index 0000000000000000000000000000000000000000..cddcdfaf612f9f326c76a3aa4aa1690579616f9c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/peer/peer_test.go @@ -0,0 +1,666 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package peer_test + +import ( + "errors" + "io" + "net" + "strconv" + "testing" + "time" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/peer" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/go-socks/socks" +) + +// conn mocks a network connection by implementing the net.Conn interface. It +// is used to test peer connection without actually opening a network +// connection. +type conn struct { + io.Reader + io.Writer + io.Closer + + // local network, address for the connection. + lnet, laddr string + + // remote network, address for the connection. + rnet, raddr string + + // mocks socks proxy if true + proxy bool +} + +// LocalAddr returns the local address for the connection. +func (c conn) LocalAddr() net.Addr { + return &addr{c.lnet, c.laddr} +} + +// Remote returns the remote address for the connection. +func (c conn) RemoteAddr() net.Addr { + if !c.proxy { + return &addr{c.rnet, c.raddr} + } + host, strPort, _ := net.SplitHostPort(c.raddr) + port, _ := strconv.Atoi(strPort) + return &socks.ProxiedAddr{ + Net: c.rnet, + Host: host, + Port: port, + } +} + +// Close handles closing the connection. +func (c conn) Close() error { + return nil +} + +func (c conn) SetDeadline(t time.Time) error { return nil } +func (c conn) SetReadDeadline(t time.Time) error { return nil } +func (c conn) SetWriteDeadline(t time.Time) error { return nil } + +// addr mocks a network address +type addr struct { + net, address string +} + +func (m addr) Network() string { return m.net } +func (m addr) String() string { return m.address } + +// pipe turns two mock connections into a full-duplex connection similar to +// net.Pipe to allow pipe's with (fake) addresses. +func pipe(c1, c2 *conn) (*conn, *conn) { + r1, w1 := io.Pipe() + r2, w2 := io.Pipe() + + c1.Writer = w1 + c2.Reader = r1 + c1.Reader = r2 + c2.Writer = w2 + + return c1, c2 +} + +// peerStats holds the expected peer stats used for testing peer. +type peerStats struct { + wantUserAgent string + wantServices wire.ServiceFlag + wantProtocolVersion uint32 + wantConnected bool + wantVersionKnown bool + wantVerAckReceived bool + wantLastBlock int32 + wantStartingHeight int32 + wantLastPingTime time.Time + wantLastPingNonce uint64 + wantLastPingMicros int64 + wantTimeOffset int64 + wantBytesSent uint64 + wantBytesReceived uint64 +} + +// testPeer tests the given peer's flags and stats +func testPeer(t *testing.T, p *peer.Peer, s peerStats) { + if p.UserAgent() != s.wantUserAgent { + t.Errorf("testPeer: wrong UserAgent - got %v, want %v", p.UserAgent(), s.wantUserAgent) + return + } + + if p.Services() != s.wantServices { + t.Errorf("testPeer: wrong Services - got %v, want %v", p.Services(), s.wantServices) + return + } + + if !p.LastPingTime().Equal(s.wantLastPingTime) { + t.Errorf("testPeer: wrong LastPingTime - got %v, want %v", p.LastPingTime(), s.wantLastPingTime) + return + } + + if p.LastPingNonce() != s.wantLastPingNonce { + t.Errorf("testPeer: wrong LastPingNonce - got %v, want %v", p.LastPingNonce(), s.wantLastPingNonce) + return + } + + if p.LastPingMicros() != s.wantLastPingMicros { + t.Errorf("testPeer: wrong LastPingMicros - got %v, want %v", p.LastPingMicros(), s.wantLastPingMicros) + return + } + + if p.VerAckReceived() != s.wantVerAckReceived { + t.Errorf("testPeer: wrong VerAckReceived - got %v, want %v", p.VerAckReceived(), s.wantVerAckReceived) + return + } + + if p.VersionKnown() != s.wantVersionKnown { + t.Errorf("testPeer: wrong VersionKnown - got %v, want %v", p.VersionKnown(), s.wantVersionKnown) + return + } + + if p.ProtocolVersion() != s.wantProtocolVersion { + t.Errorf("testPeer: wrong ProtocolVersion - got %v, want %v", p.ProtocolVersion(), s.wantProtocolVersion) + return + } + + if p.LastBlock() != s.wantLastBlock { + t.Errorf("testPeer: wrong LastBlock - got %v, want %v", p.LastBlock(), s.wantLastBlock) + return + } + + // Allow for a deviation of 1s, as the second may tick when the message is + // in transit and the protocol doesn't support any further precision. + if p.TimeOffset() != s.wantTimeOffset && p.TimeOffset() != s.wantTimeOffset-1 { + t.Errorf("testPeer: wrong TimeOffset - got %v, want %v or %v", p.TimeOffset(), + s.wantTimeOffset, s.wantTimeOffset-1) + return + } + + if p.BytesSent() != s.wantBytesSent { + t.Errorf("testPeer: wrong BytesSent - got %v, want %v", p.BytesSent(), s.wantBytesSent) + return + } + + if p.BytesReceived() != s.wantBytesReceived { + t.Errorf("testPeer: wrong BytesReceived - got %v, want %v", p.BytesReceived(), s.wantBytesReceived) + return + } + + if p.StartingHeight() != s.wantStartingHeight { + t.Errorf("testPeer: wrong StartingHeight - got %v, want %v", p.StartingHeight(), s.wantStartingHeight) + return + } + + if p.Connected() != s.wantConnected { + t.Errorf("testPeer: wrong Connected - got %v, want %v", p.Connected(), s.wantConnected) + return + } + + stats := p.StatsSnapshot() + + if p.ID() != stats.ID { + t.Errorf("testPeer: wrong ID - got %v, want %v", p.ID(), stats.ID) + return + } + + if p.Addr() != stats.Addr { + t.Errorf("testPeer: wrong Addr - got %v, want %v", p.Addr(), stats.Addr) + return + } + + if p.LastSend() != stats.LastSend { + t.Errorf("testPeer: wrong LastSend - got %v, want %v", p.LastSend(), stats.LastSend) + return + } + + if p.LastRecv() != stats.LastRecv { + t.Errorf("testPeer: wrong LastRecv - got %v, want %v", p.LastRecv(), stats.LastRecv) + return + } +} + +// TestPeerConnection tests connection between inbound and outbound peers. +func TestPeerConnection(t *testing.T) { + verack := make(chan struct{}, 1) + peerCfg := &peer.Config{ + Listeners: peer.MessageListeners{ + OnWrite: func(p *peer.Peer, bytesWritten int, msg wire.Message, err error) { + switch msg.(type) { + case *wire.MsgVerAck: + verack <- struct{}{} + } + }, + }, + UserAgentName: "peer", + UserAgentVersion: "1.0", + ChainParams: &chaincfg.MainNetParams, + Services: 0, + } + wantStats := peerStats{ + wantUserAgent: wire.DefaultUserAgent + "peer:1.0/", + wantServices: 0, + wantProtocolVersion: peer.MaxProtocolVersion, + wantConnected: true, + wantVersionKnown: true, + wantVerAckReceived: true, + wantLastPingTime: time.Time{}, + wantLastPingNonce: uint64(0), + wantLastPingMicros: int64(0), + wantTimeOffset: int64(0), + wantBytesSent: 158, // 134 version + 24 verack + wantBytesReceived: 158, + } + tests := []struct { + name string + setup func() (*peer.Peer, *peer.Peer, error) + }{ + { + "basic handshake", + func() (*peer.Peer, *peer.Peer, error) { + inConn, outConn := pipe( + &conn{raddr: "10.0.0.1:8333"}, + &conn{raddr: "10.0.0.2:8333"}, + ) + inPeer := peer.NewInboundPeer(peerCfg) + if err := inPeer.Connect(inConn); err != nil { + return nil, nil, err + } + outPeer, err := peer.NewOutboundPeer(peerCfg, "10.0.0.2:8333") + if err != nil { + return nil, nil, err + } + if err := outPeer.Connect(outConn); err != nil { + return nil, nil, err + } + + for i := 0; i < 2; i++ { + select { + case <-verack: + case <-time.After(time.Second * 1): + return nil, nil, errors.New("verack timeout") + } + } + return inPeer, outPeer, nil + }, + }, + { + "socks proxy", + func() (*peer.Peer, *peer.Peer, error) { + inConn, outConn := pipe( + &conn{raddr: "10.0.0.1:8333", proxy: true}, + &conn{raddr: "10.0.0.2:8333"}, + ) + inPeer := peer.NewInboundPeer(peerCfg) + if err := inPeer.Connect(inConn); err != nil { + return nil, nil, err + } + outPeer, err := peer.NewOutboundPeer(peerCfg, "10.0.0.2:8333") + if err != nil { + return nil, nil, err + } + if err := outPeer.Connect(outConn); err != nil { + return nil, nil, err + } + for i := 0; i < 2; i++ { + select { + case <-verack: + case <-time.After(time.Second * 1): + return nil, nil, errors.New("verack timeout") + } + } + return inPeer, outPeer, nil + }, + }, + } + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + inPeer, outPeer, err := test.setup() + if err != nil { + t.Errorf("TestPeerConnection setup #%d: unexpected err %v\n", i, err) + return + } + testPeer(t, inPeer, wantStats) + testPeer(t, outPeer, wantStats) + + inPeer.Disconnect() + outPeer.Disconnect() + } +} + +// TestPeerListeners tests that the peer listeners are called as expected. +func TestPeerListeners(t *testing.T) { + verack := make(chan struct{}, 1) + ok := make(chan wire.Message, 20) + peerCfg := &peer.Config{ + Listeners: peer.MessageListeners{ + OnGetAddr: func(p *peer.Peer, msg *wire.MsgGetAddr) { + ok <- msg + }, + OnAddr: func(p *peer.Peer, msg *wire.MsgAddr) { + ok <- msg + }, + OnPing: func(p *peer.Peer, msg *wire.MsgPing) { + ok <- msg + }, + OnPong: func(p *peer.Peer, msg *wire.MsgPong) { + ok <- msg + }, + OnAlert: func(p *peer.Peer, msg *wire.MsgAlert) { + ok <- msg + }, + OnMemPool: func(p *peer.Peer, msg *wire.MsgMemPool) { + ok <- msg + }, + OnTx: func(p *peer.Peer, msg *wire.MsgTx) { + ok <- msg + }, + OnBlock: func(p *peer.Peer, msg *wire.MsgBlock, buf []byte) { + ok <- msg + }, + OnInv: func(p *peer.Peer, msg *wire.MsgInv) { + ok <- msg + }, + OnHeaders: func(p *peer.Peer, msg *wire.MsgHeaders) { + ok <- msg + }, + OnNotFound: func(p *peer.Peer, msg *wire.MsgNotFound) { + ok <- msg + }, + OnGetData: func(p *peer.Peer, msg *wire.MsgGetData) { + ok <- msg + }, + OnGetBlocks: func(p *peer.Peer, msg *wire.MsgGetBlocks) { + ok <- msg + }, + OnGetHeaders: func(p *peer.Peer, msg *wire.MsgGetHeaders) { + ok <- msg + }, + OnFilterAdd: func(p *peer.Peer, msg *wire.MsgFilterAdd) { + ok <- msg + }, + OnFilterClear: func(p *peer.Peer, msg *wire.MsgFilterClear) { + ok <- msg + }, + OnFilterLoad: func(p *peer.Peer, msg *wire.MsgFilterLoad) { + ok <- msg + }, + OnMerkleBlock: func(p *peer.Peer, msg *wire.MsgMerkleBlock) { + ok <- msg + }, + OnVersion: func(p *peer.Peer, msg *wire.MsgVersion) { + ok <- msg + }, + OnVerAck: func(p *peer.Peer, msg *wire.MsgVerAck) { + verack <- struct{}{} + }, + OnReject: func(p *peer.Peer, msg *wire.MsgReject) { + ok <- msg + }, + OnSendHeaders: func(p *peer.Peer, msg *wire.MsgSendHeaders) { + ok <- msg + }, + }, + UserAgentName: "peer", + UserAgentVersion: "1.0", + ChainParams: &chaincfg.MainNetParams, + Services: wire.SFNodeBloom, + } + inConn, outConn := pipe( + &conn{raddr: "10.0.0.1:8333"}, + &conn{raddr: "10.0.0.2:8333"}, + ) + inPeer := peer.NewInboundPeer(peerCfg) + if err := inPeer.Connect(inConn); err != nil { + t.Errorf("TestPeerListeners: unexpected err %v\n", err) + return + } + peerCfg.Listeners = peer.MessageListeners{ + OnVerAck: func(p *peer.Peer, msg *wire.MsgVerAck) { + verack <- struct{}{} + }, + } + outPeer, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:8333") + if err != nil { + t.Errorf("NewOutboundPeer: unexpected err %v\n", err) + return + } + if err := outPeer.Connect(outConn); err != nil { + t.Errorf("TestPeerListeners: unexpected err %v\n", err) + return + } + for i := 0; i < 2; i++ { + select { + case <-verack: + case <-time.After(time.Second * 1): + t.Errorf("TestPeerListeners: verack timeout\n") + return + } + } + + tests := []struct { + listener string + msg wire.Message + }{ + { + "OnGetAddr", + wire.NewMsgGetAddr(), + }, + { + "OnAddr", + wire.NewMsgAddr(), + }, + { + "OnPing", + wire.NewMsgPing(42), + }, + { + "OnPong", + wire.NewMsgPong(42), + }, + { + "OnAlert", + wire.NewMsgAlert([]byte("payload"), []byte("signature")), + }, + { + "OnMemPool", + wire.NewMsgMemPool(), + }, + { + "OnTx", + wire.NewMsgTx(), + }, + { + "OnBlock", + wire.NewMsgBlock(wire.NewBlockHeader(&wire.ShaHash{}, &wire.ShaHash{}, 1, 1)), + }, + { + "OnInv", + wire.NewMsgInv(), + }, + { + "OnHeaders", + wire.NewMsgHeaders(), + }, + { + "OnNotFound", + wire.NewMsgNotFound(), + }, + { + "OnGetData", + wire.NewMsgGetData(), + }, + { + "OnGetBlocks", + wire.NewMsgGetBlocks(&wire.ShaHash{}), + }, + { + "OnGetHeaders", + wire.NewMsgGetHeaders(), + }, + { + "OnFilterAdd", + wire.NewMsgFilterAdd([]byte{0x01}), + }, + { + "OnFilterClear", + wire.NewMsgFilterClear(), + }, + { + "OnFilterLoad", + wire.NewMsgFilterLoad([]byte{0x01}, 10, 0, wire.BloomUpdateNone), + }, + { + "OnMerkleBlock", + wire.NewMsgMerkleBlock(wire.NewBlockHeader(&wire.ShaHash{}, &wire.ShaHash{}, 1, 1)), + }, + // only one version message is allowed + // only one verack message is allowed + { + "OnReject", + wire.NewMsgReject("block", wire.RejectDuplicate, "dupe block"), + }, + { + "OnSendHeaders", + wire.NewMsgSendHeaders(), + }, + } + t.Logf("Running %d tests", len(tests)) + for _, test := range tests { + // Queue the test message + outPeer.QueueMessage(test.msg, nil) + select { + case <-ok: + case <-time.After(time.Second * 1): + t.Errorf("TestPeerListeners: %s timeout", test.listener) + return + } + } + inPeer.Disconnect() + outPeer.Disconnect() +} + +// TestOutboundPeer tests that the outbound peer works as expected. +func TestOutboundPeer(t *testing.T) { + // Use a mock NewestBlock func to test errs + var errBlockNotFound = errors.New("newest block not found") + var mockNewestSha = func() (*wire.ShaHash, int32, error) { + return nil, 0, errBlockNotFound + } + + peerCfg := &peer.Config{ + NewestBlock: mockNewestSha, + UserAgentName: "peer", + UserAgentVersion: "1.0", + ChainParams: &chaincfg.MainNetParams, + Services: 0, + } + + r, w := io.Pipe() + c := &conn{raddr: "10.0.0.1:8333", Writer: w, Reader: r} + + p, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:8333") + if err != nil { + t.Errorf("NewOutboundPeer: unexpected err - %v\n", err) + return + } + + wantErr := errBlockNotFound + if err := p.Connect(c); err != wantErr { + t.Errorf("Connect: expected err %v, got %v\n", wantErr, err) + return + } + + // Test already connected. + if err := p.Connect(c); err != nil { + t.Errorf("Connect: unexpected err %v\n", err) + return + } + + // Test Queue Inv + fakeBlockHash := &wire.ShaHash{0: 0x00, 1: 0x01} + fakeInv := wire.NewInvVect(wire.InvTypeBlock, fakeBlockHash) + p.QueueInventory(fakeInv) + p.AddKnownInventory(fakeInv) + p.QueueInventory(fakeInv) + + // Test Queue Message + fakeMsg := wire.NewMsgVerAck() + p.QueueMessage(fakeMsg, nil) + done := make(chan struct{}) + p.QueueMessage(fakeMsg, done) + <-done + p.Disconnect() + + // Test NewestBlock + var newestBlock = func() (*wire.ShaHash, int32, error) { + hashStr := "14a0810ac680a3eb3f82edc878cea25ec41d6b790744e5daeef" + hash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + return nil, 0, err + } + return hash, 234439, nil + } + peerCfg.NewestBlock = newestBlock + r1, w1 := io.Pipe() + c1 := &conn{raddr: "10.0.0.1:8333", Writer: w1, Reader: r1} + p1, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:8333") + if err != nil { + t.Errorf("NewOutboundPeer: unexpected err - %v\n", err) + return + } + if err := p1.Connect(c1); err != nil { + t.Errorf("Connect: unexpected err %v\n", err) + return + } + + // Test update latest block + latestBlockSha, err := wire.NewShaHashFromStr("1a63f9cdff1752e6375c8c76e543a71d239e1a2e5c6db1aa679") + if err != nil { + t.Errorf("NewShaHashFromStr: unexpected err %v\n", err) + return + } + p1.UpdateLastAnnouncedBlock(latestBlockSha) + p1.UpdateLastBlockHeight(234440) + if p1.LastAnnouncedBlock() != latestBlockSha { + t.Errorf("LastAnnouncedBlock: wrong block - got %v, want %v", + p1.LastAnnouncedBlock(), latestBlockSha) + return + } + + // Test Queue Inv after connection + p1.QueueInventory(fakeInv) + p1.Disconnect() + + // Test regression + peerCfg.ChainParams = &chaincfg.RegressionNetParams + peerCfg.Services = wire.SFNodeBloom + r2, w2 := io.Pipe() + c2 := &conn{raddr: "10.0.0.1:8333", Writer: w2, Reader: r2} + p2, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:8333") + if err != nil { + t.Errorf("NewOutboundPeer: unexpected err - %v\n", err) + return + } + if err := p2.Connect(c2); err != nil { + t.Errorf("Connect: unexpected err %v\n", err) + return + } + + // Test PushXXX + var addrs []*wire.NetAddress + for i := 0; i < 5; i++ { + na := wire.NetAddress{} + addrs = append(addrs, &na) + } + if _, err := p2.PushAddrMsg(addrs); err != nil { + t.Errorf("PushAddrMsg: unexpected err %v\n", err) + return + } + if err := p2.PushGetBlocksMsg(nil, &wire.ShaHash{}); err != nil { + t.Errorf("PushGetBlocksMsg: unexpected err %v\n", err) + return + } + if err := p2.PushGetHeadersMsg(nil, &wire.ShaHash{}); err != nil { + t.Errorf("PushGetHeadersMsg: unexpected err %v\n", err) + return + } + p2.PushRejectMsg("block", wire.RejectMalformed, "malformed", nil, true) + p2.PushRejectMsg("block", wire.RejectInvalid, "invalid", nil, false) + + // Test Queue Messages + p2.QueueMessage(wire.NewMsgGetAddr(), nil) + p2.QueueMessage(wire.NewMsgPing(1), nil) + p2.QueueMessage(wire.NewMsgMemPool(), nil) + p2.QueueMessage(wire.NewMsgGetData(), nil) + p2.QueueMessage(wire.NewMsgGetHeaders(), nil) + + p2.Disconnect() +} + +func init() { + // Allow self connection when running the tests. + peer.TstAllowSelfConns() +} diff --git a/vendor/github.com/btcsuite/btcd/policy.go b/vendor/github.com/btcsuite/btcd/policy.go new file mode 100644 index 0000000000000000000000000000000000000000..64d0a1208d15caed092b5ddbf4a56824aba6cbb7 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/policy.go @@ -0,0 +1,430 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +const ( + // maxStandardTxSize is the maximum size allowed for transactions that + // are considered standard and will therefore be relayed and considered + // for mining. + maxStandardTxSize = 100000 + + // maxStandardSigScriptSize is the maximum size allowed for a + // transaction input signature script to be considered standard. This + // value allows for a 15-of-15 CHECKMULTISIG pay-to-script-hash with + // compressed keys. + // + // The form of the overall script is: OP_0 <15 signatures> OP_PUSHDATA2 + // <2 bytes len> [OP_15 <15 pubkeys> OP_15 OP_CHECKMULTISIG] + // + // For the p2sh script portion, each of the 15 compressed pubkeys are + // 33 bytes (plus one for the OP_DATA_33 opcode), and the thus it totals + // to (15*34)+3 = 513 bytes. Next, each of the 15 signatures is a max + // of 73 bytes (plus one for the OP_DATA_73 opcode). Also, there is one + // extra byte for the initial extra OP_0 push and 3 bytes for the + // OP_PUSHDATA2 needed to specify the 513 bytes for the script push. + // That brings the total to 1+(15*74)+3+513 = 1627. This value also + // adds a few extra bytes to provide a little buffer. + // (1 + 15*74 + 3) + (15*34 + 3) + 23 = 1650 + maxStandardSigScriptSize = 1650 + + // defaultMinRelayTxFee is the minimum fee in satoshi that is required + // for a transaction to be treated as free for relay and mining + // purposes. It is also used to help determine if a transaction is + // considered dust and as a base for calculating minimum required fees + // for larger transactions. This value is in Satoshi/1000 bytes. + defaultMinRelayTxFee = btcutil.Amount(1000) + + // maxStandardMultiSigKeys is the maximum number of public keys allowed + // in a multi-signature transaction output script for it to be + // considered standard. + maxStandardMultiSigKeys = 3 +) + +// calcMinRequiredTxRelayFee returns the minimum transaction fee required for a +// transaction with the passed serialized size to be accepted into the memory +// pool and relayed. +func calcMinRequiredTxRelayFee(serializedSize int64, minRelayTxFee btcutil.Amount) int64 { + // Calculate the minimum fee for a transaction to be allowed into the + // mempool and relayed by scaling the base fee (which is the minimum + // free transaction relay fee). minTxRelayFee is in Satoshi/kB so + // multiply by serializedSize (which is in bytes) and divide by 1000 to + // get minimum Satoshis. + minFee := (serializedSize * int64(minRelayTxFee)) / 1000 + + if minFee == 0 && minRelayTxFee > 0 { + minFee = int64(minRelayTxFee) + } + + // Set the minimum fee to the maximum possible value if the calculated + // fee is not in the valid range for monetary amounts. + if minFee < 0 || minFee > btcutil.MaxSatoshi { + minFee = btcutil.MaxSatoshi + } + + return minFee +} + +// calcPriority returns a transaction priority given a transaction and the sum +// of each of its input values multiplied by their age (# of confirmations). +// Thus, the final formula for the priority is: +// sum(inputValue * inputAge) / adjustedTxSize +func calcPriority(tx *wire.MsgTx, utxoView *blockchain.UtxoViewpoint, nextBlockHeight int32) float64 { + // In order to encourage spending multiple old unspent transaction + // outputs thereby reducing the total set, don't count the constant + // overhead for each input as well as enough bytes of the signature + // script to cover a pay-to-script-hash redemption with a compressed + // pubkey. This makes additional inputs free by boosting the priority + // of the transaction accordingly. No more incentive is given to avoid + // encouraging gaming future transactions through the use of junk + // outputs. This is the same logic used in the reference + // implementation. + // + // The constant overhead for a txin is 41 bytes since the previous + // outpoint is 36 bytes + 4 bytes for the sequence + 1 byte the + // signature script length. + // + // A compressed pubkey pay-to-script-hash redemption with a maximum len + // signature is of the form: + // [OP_DATA_73 <73-byte sig> + OP_DATA_35 + {OP_DATA_33 + // <33 byte compresed pubkey> + OP_CHECKSIG}] + // + // Thus 1 + 73 + 1 + 1 + 33 + 1 = 110 + overhead := 0 + for _, txIn := range tx.TxIn { + // Max inputs + size can't possibly overflow here. + overhead += 41 + minInt(110, len(txIn.SignatureScript)) + } + + serializedTxSize := tx.SerializeSize() + if overhead >= serializedTxSize { + return 0.0 + } + + inputValueAge := calcInputValueAge(tx, utxoView, nextBlockHeight) + return inputValueAge / float64(serializedTxSize-overhead) +} + +// calcInputValueAge is a helper function used to calculate the input age of +// a transaction. The input age for a txin is the number of confirmations +// since the referenced txout multiplied by its output value. The total input +// age is the sum of this value for each txin. Any inputs to the transaction +// which are currently in the mempool and hence not mined into a block yet, +// contribute no additional input age to the transaction. +func calcInputValueAge(tx *wire.MsgTx, utxoView *blockchain.UtxoViewpoint, nextBlockHeight int32) float64 { + var totalInputAge float64 + for _, txIn := range tx.TxIn { + // Don't attempt to accumulate the total input age if the + // referenced transaction output doesn't exist. + originHash := &txIn.PreviousOutPoint.Hash + originIndex := txIn.PreviousOutPoint.Index + txEntry := utxoView.LookupEntry(originHash) + if txEntry != nil && !txEntry.IsOutputSpent(originIndex) { + // Inputs with dependencies currently in the mempool + // have their block height set to a special constant. + // Their input age should be computed as zero since + // their parent hasn't made it into a block yet. + var inputAge int32 + originHeight := txEntry.BlockHeight() + if originHeight == mempoolHeight { + inputAge = 0 + } else { + inputAge = nextBlockHeight - originHeight + } + + // Sum the input value times age. + inputValue := txEntry.AmountByIndex(originIndex) + totalInputAge += float64(inputValue * int64(inputAge)) + } + } + + return totalInputAge +} + +// checkInputsStandard performs a series of checks on a transaction's inputs +// to ensure they are "standard". A standard transaction input is one that +// that consumes the expected number of elements from the stack and that number +// is the same as the output script pushes. This help prevent resource +// exhaustion attacks by "creative" use of scripts that are super expensive to +// process like OP_DUP OP_CHECKSIG OP_DROP repeated a large number of times +// followed by a final OP_TRUE. +func checkInputsStandard(tx *btcutil.Tx, utxoView *blockchain.UtxoViewpoint) error { + // NOTE: The reference implementation also does a coinbase check here, + // but coinbases have already been rejected prior to calling this + // function so no need to recheck. + + for i, txIn := range tx.MsgTx().TxIn { + // It is safe to elide existence and index checks here since + // they have already been checked prior to calling this + // function. + prevOut := txIn.PreviousOutPoint + entry := utxoView.LookupEntry(&prevOut.Hash) + originPkScript := entry.PkScriptByIndex(prevOut.Index) + + // Calculate stats for the script pair. + scriptInfo, err := txscript.CalcScriptInfo(txIn.SignatureScript, + originPkScript, true) + if err != nil { + str := fmt.Sprintf("transaction input #%d script parse "+ + "failure: %v", i, err) + return txRuleError(wire.RejectNonstandard, str) + } + + // A negative value for expected inputs indicates the script is + // non-standard in some way. + if scriptInfo.ExpectedInputs < 0 { + str := fmt.Sprintf("transaction input #%d expects %d "+ + "inputs", i, scriptInfo.ExpectedInputs) + return txRuleError(wire.RejectNonstandard, str) + } + + // The script pair is non-standard if the number of available + // inputs does not match the number of expected inputs. + if scriptInfo.NumInputs != scriptInfo.ExpectedInputs { + str := fmt.Sprintf("transaction input #%d expects %d "+ + "inputs, but referenced output script provides "+ + "%d", i, scriptInfo.ExpectedInputs, + scriptInfo.NumInputs) + return txRuleError(wire.RejectNonstandard, str) + } + } + + return nil +} + +// checkPkScriptStandard performs a series of checks on a transaction output +// script (public key script) to ensure it is a "standard" public key script. +// A standard public key script is one that is a recognized form, and for +// multi-signature scripts, only contains from 1 to maxStandardMultiSigKeys +// public keys. +func checkPkScriptStandard(pkScript []byte, scriptClass txscript.ScriptClass) error { + switch scriptClass { + case txscript.MultiSigTy: + numPubKeys, numSigs, err := txscript.CalcMultiSigStats(pkScript) + if err != nil { + str := fmt.Sprintf("multi-signature script parse "+ + "failure: %v", err) + return txRuleError(wire.RejectNonstandard, str) + } + + // A standard multi-signature public key script must contain + // from 1 to maxStandardMultiSigKeys public keys. + if numPubKeys < 1 { + str := "multi-signature script with no pubkeys" + return txRuleError(wire.RejectNonstandard, str) + } + if numPubKeys > maxStandardMultiSigKeys { + str := fmt.Sprintf("multi-signature script with %d "+ + "public keys which is more than the allowed "+ + "max of %d", numPubKeys, maxStandardMultiSigKeys) + return txRuleError(wire.RejectNonstandard, str) + } + + // A standard multi-signature public key script must have at + // least 1 signature and no more signatures than available + // public keys. + if numSigs < 1 { + return txRuleError(wire.RejectNonstandard, + "multi-signature script with no signatures") + } + if numSigs > numPubKeys { + str := fmt.Sprintf("multi-signature script with %d "+ + "signatures which is more than the available "+ + "%d public keys", numSigs, numPubKeys) + return txRuleError(wire.RejectNonstandard, str) + } + + case txscript.NonStandardTy: + return txRuleError(wire.RejectNonstandard, + "non-standard script form") + } + + return nil +} + +// isDust returns whether or not the passed transaction output amount is +// considered dust or not based on the passed minimum transaction relay fee. +// Dust is defined in terms of the minimum transaction relay fee. In +// particular, if the cost to the network to spend coins is more than 1/3 of the +// minimum transaction relay fee, it is considered dust. +func isDust(txOut *wire.TxOut, minRelayTxFee btcutil.Amount) bool { + // Unspendable outputs are considered dust. + if txscript.IsUnspendable(txOut.PkScript) { + return true + } + + // The total serialized size consists of the output and the associated + // input script to redeem it. Since there is no input script + // to redeem it yet, use the minimum size of a typical input script. + // + // Pay-to-pubkey-hash bytes breakdown: + // + // Output to hash (34 bytes): + // 8 value, 1 script len, 25 script [1 OP_DUP, 1 OP_HASH_160, + // 1 OP_DATA_20, 20 hash, 1 OP_EQUALVERIFY, 1 OP_CHECKSIG] + // + // Input with compressed pubkey (148 bytes): + // 36 prev outpoint, 1 script len, 107 script [1 OP_DATA_72, 72 sig, + // 1 OP_DATA_33, 33 compressed pubkey], 4 sequence + // + // Input with uncompressed pubkey (180 bytes): + // 36 prev outpoint, 1 script len, 139 script [1 OP_DATA_72, 72 sig, + // 1 OP_DATA_65, 65 compressed pubkey], 4 sequence + // + // Pay-to-pubkey bytes breakdown: + // + // Output to compressed pubkey (44 bytes): + // 8 value, 1 script len, 35 script [1 OP_DATA_33, + // 33 compressed pubkey, 1 OP_CHECKSIG] + // + // Output to uncompressed pubkey (76 bytes): + // 8 value, 1 script len, 67 script [1 OP_DATA_65, 65 pubkey, + // 1 OP_CHECKSIG] + // + // Input (114 bytes): + // 36 prev outpoint, 1 script len, 73 script [1 OP_DATA_72, + // 72 sig], 4 sequence + // + // Theoretically this could examine the script type of the output script + // and use a different size for the typical input script size for + // pay-to-pubkey vs pay-to-pubkey-hash inputs per the above breakdowns, + // but the only combinination which is less than the value chosen is + // a pay-to-pubkey script with a compressed pubkey, which is not very + // common. + // + // The most common scripts are pay-to-pubkey-hash, and as per the above + // breakdown, the minimum size of a p2pkh input script is 148 bytes. So + // that figure is used. + totalSize := txOut.SerializeSize() + 148 + + // The output is considered dust if the cost to the network to spend the + // coins is more than 1/3 of the minimum free transaction relay fee. + // minFreeTxRelayFee is in Satoshi/KB, so multiply by 1000 to + // convert to bytes. + // + // Using the typical values for a pay-to-pubkey-hash transaction from + // the breakdown above and the default minimum free transaction relay + // fee of 1000, this equates to values less than 546 satoshi being + // considered dust. + // + // The following is equivalent to (value/totalSize) * (1/3) * 1000 + // without needing to do floating point math. + return txOut.Value*1000/(3*int64(totalSize)) < int64(minRelayTxFee) +} + +// checkTransactionStandard performs a series of checks on a transaction to +// ensure it is a "standard" transaction. A standard transaction is one that +// conforms to several additional limiting cases over what is considered a +// "sane" transaction such as having a version in the supported range, being +// finalized, conforming to more stringent size constraints, having scripts +// of recognized forms, and not containing "dust" outputs (those that are +// so small it costs more to process them than they are worth). +func checkTransactionStandard(tx *btcutil.Tx, height int32, timeSource blockchain.MedianTimeSource, minRelayTxFee btcutil.Amount) error { + // The transaction must be a currently supported version. + msgTx := tx.MsgTx() + if msgTx.Version > wire.TxVersion || msgTx.Version < 1 { + str := fmt.Sprintf("transaction version %d is not in the "+ + "valid range of %d-%d", msgTx.Version, 1, + wire.TxVersion) + return txRuleError(wire.RejectNonstandard, str) + } + + // The transaction must be finalized to be standard and therefore + // considered for inclusion in a block. + adjustedTime := timeSource.AdjustedTime() + if !blockchain.IsFinalizedTransaction(tx, height, adjustedTime) { + return txRuleError(wire.RejectNonstandard, + "transaction is not finalized") + } + + // Since extremely large transactions with a lot of inputs can cost + // almost as much to process as the sender fees, limit the maximum + // size of a transaction. This also helps mitigate CPU exhaustion + // attacks. + serializedLen := msgTx.SerializeSize() + if serializedLen > maxStandardTxSize { + str := fmt.Sprintf("transaction size of %v is larger than max "+ + "allowed size of %v", serializedLen, maxStandardTxSize) + return txRuleError(wire.RejectNonstandard, str) + } + + for i, txIn := range msgTx.TxIn { + // Each transaction input signature script must not exceed the + // maximum size allowed for a standard transaction. See + // the comment on maxStandardSigScriptSize for more details. + sigScriptLen := len(txIn.SignatureScript) + if sigScriptLen > maxStandardSigScriptSize { + str := fmt.Sprintf("transaction input %d: signature "+ + "script size of %d bytes is large than max "+ + "allowed size of %d bytes", i, sigScriptLen, + maxStandardSigScriptSize) + return txRuleError(wire.RejectNonstandard, str) + } + + // Each transaction input signature script must only contain + // opcodes which push data onto the stack. + if !txscript.IsPushOnlyScript(txIn.SignatureScript) { + str := fmt.Sprintf("transaction input %d: signature "+ + "script is not push only", i) + return txRuleError(wire.RejectNonstandard, str) + } + } + + // None of the output public key scripts can be a non-standard script or + // be "dust" (except when the script is a null data script). + numNullDataOutputs := 0 + for i, txOut := range msgTx.TxOut { + scriptClass := txscript.GetScriptClass(txOut.PkScript) + err := checkPkScriptStandard(txOut.PkScript, scriptClass) + if err != nil { + // Attempt to extract a reject code from the error so + // it can be retained. When not possible, fall back to + // a non standard error. + rejectCode := wire.RejectNonstandard + if rejCode, found := extractRejectCode(err); found { + rejectCode = rejCode + } + str := fmt.Sprintf("transaction output %d: %v", i, err) + return txRuleError(rejectCode, str) + } + + // Accumulate the number of outputs which only carry data. For + // all other script types, ensure the output value is not + // "dust". + if scriptClass == txscript.NullDataTy { + numNullDataOutputs++ + } else if isDust(txOut, minRelayTxFee) { + str := fmt.Sprintf("transaction output %d: payment "+ + "of %d is dust", i, txOut.Value) + return txRuleError(wire.RejectDust, str) + } + } + + // A standard transaction must not have more than one output script that + // only carries data. + if numNullDataOutputs > 1 { + str := "more than one transaction output in a nulldata script" + return txRuleError(wire.RejectNonstandard, str) + } + + return nil +} + +// minInt is a helper function to return the minimum of two ints. This avoids +// a math import and the need to cast to floats. +func minInt(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/vendor/github.com/btcsuite/btcd/policy_test.go b/vendor/github.com/btcsuite/btcd/policy_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fb8bc988949e0f154d931006f6535ed38f185670 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/policy_test.go @@ -0,0 +1,511 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "testing" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// TestCalcMinRequiredTxRelayFee tests the calcMinRequiredTxRelayFee API. +func TestCalcMinRequiredTxRelayFee(t *testing.T) { + tests := []struct { + name string // test description. + size int64 // Transaction size in bytes. + relayFee btcutil.Amount // minimum relay transaction fee. + want int64 // Expected fee. + }{ + { + // Ensure combination of size and fee that are less than 1000 + // produce a non-zero fee. + "250 bytes with relay fee of 3", + 250, + 3, + 3, + }, + { + "100 bytes with default minimum relay fee", + 100, + defaultMinRelayTxFee, + 100, + }, + { + "max standard tx size with default minimum relay fee", + maxStandardTxSize, + defaultMinRelayTxFee, + 100000, + }, + { + "max standard tx size with max satoshi relay fee", + maxStandardTxSize, + btcutil.MaxSatoshi, + btcutil.MaxSatoshi, + }, + { + "1500 bytes with 5000 relay fee", + 1500, + 5000, + 7500, + }, + { + "1500 bytes with 3000 relay fee", + 1500, + 3000, + 4500, + }, + { + "782 bytes with 5000 relay fee", + 782, + 5000, + 3910, + }, + { + "782 bytes with 3000 relay fee", + 782, + 3000, + 2346, + }, + { + "782 bytes with 2550 relay fee", + 782, + 2550, + 1994, + }, + } + + for _, test := range tests { + got := calcMinRequiredTxRelayFee(test.size, test.relayFee) + if got != test.want { + t.Errorf("TestCalcMinRequiredTxRelayFee test '%s' "+ + "failed: got %v want %v", test.name, got, + test.want) + continue + } + } +} + +// TestCheckPkScriptStandard tests the checkPkScriptStandard API. +func TestCheckPkScriptStandard(t *testing.T) { + var pubKeys [][]byte + for i := 0; i < 4; i++ { + pk, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Fatalf("TestCheckPkScriptStandard NewPrivateKey failed: %v", + err) + return + } + pubKeys = append(pubKeys, pk.PubKey().SerializeCompressed()) + } + + tests := []struct { + name string // test description. + script *txscript.ScriptBuilder + isStandard bool + }{ + { + "key1 and key2", + txscript.NewScriptBuilder().AddOp(txscript.OP_2). + AddData(pubKeys[0]).AddData(pubKeys[1]). + AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG), + true, + }, + { + "key1 or key2", + txscript.NewScriptBuilder().AddOp(txscript.OP_1). + AddData(pubKeys[0]).AddData(pubKeys[1]). + AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG), + true, + }, + { + "escrow", + txscript.NewScriptBuilder().AddOp(txscript.OP_2). + AddData(pubKeys[0]).AddData(pubKeys[1]). + AddData(pubKeys[2]). + AddOp(txscript.OP_3).AddOp(txscript.OP_CHECKMULTISIG), + true, + }, + { + "one of four", + txscript.NewScriptBuilder().AddOp(txscript.OP_1). + AddData(pubKeys[0]).AddData(pubKeys[1]). + AddData(pubKeys[2]).AddData(pubKeys[3]). + AddOp(txscript.OP_4).AddOp(txscript.OP_CHECKMULTISIG), + false, + }, + { + "malformed1", + txscript.NewScriptBuilder().AddOp(txscript.OP_3). + AddData(pubKeys[0]).AddData(pubKeys[1]). + AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG), + false, + }, + { + "malformed2", + txscript.NewScriptBuilder().AddOp(txscript.OP_2). + AddData(pubKeys[0]).AddData(pubKeys[1]). + AddOp(txscript.OP_3).AddOp(txscript.OP_CHECKMULTISIG), + false, + }, + { + "malformed3", + txscript.NewScriptBuilder().AddOp(txscript.OP_0). + AddData(pubKeys[0]).AddData(pubKeys[1]). + AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG), + false, + }, + { + "malformed4", + txscript.NewScriptBuilder().AddOp(txscript.OP_1). + AddData(pubKeys[0]).AddData(pubKeys[1]). + AddOp(txscript.OP_0).AddOp(txscript.OP_CHECKMULTISIG), + false, + }, + { + "malformed5", + txscript.NewScriptBuilder().AddOp(txscript.OP_1). + AddData(pubKeys[0]).AddData(pubKeys[1]). + AddOp(txscript.OP_CHECKMULTISIG), + false, + }, + { + "malformed6", + txscript.NewScriptBuilder().AddOp(txscript.OP_1). + AddData(pubKeys[0]).AddData(pubKeys[1]), + false, + }, + } + + for _, test := range tests { + script, err := test.script.Script() + if err != nil { + t.Fatalf("TestCheckPkScriptStandard test '%s' "+ + "failed: %v", test.name, err) + continue + } + scriptClass := txscript.GetScriptClass(script) + got := checkPkScriptStandard(script, scriptClass) + if (test.isStandard && got != nil) || + (!test.isStandard && got == nil) { + + t.Fatalf("TestCheckPkScriptStandard test '%s' failed", + test.name) + return + } + } +} + +// TestDust tests the isDust API. +func TestDust(t *testing.T) { + pkScript := []byte{0x76, 0xa9, 0x21, 0x03, 0x2f, 0x7e, 0x43, + 0x0a, 0xa4, 0xc9, 0xd1, 0x59, 0x43, 0x7e, 0x84, 0xb9, + 0x75, 0xdc, 0x76, 0xd9, 0x00, 0x3b, 0xf0, 0x92, 0x2c, + 0xf3, 0xaa, 0x45, 0x28, 0x46, 0x4b, 0xab, 0x78, 0x0d, + 0xba, 0x5e, 0x88, 0xac} + + tests := []struct { + name string // test description + txOut wire.TxOut + relayFee btcutil.Amount // minimum relay transaction fee. + isDust bool + }{ + { + // Any value is allowed with a zero relay fee. + "zero value with zero relay fee", + wire.TxOut{Value: 0, PkScript: pkScript}, + 0, + false, + }, + { + // Zero value is dust with any relay fee" + "zero value with very small tx fee", + wire.TxOut{Value: 0, PkScript: pkScript}, + 1, + true, + }, + { + "38 byte public key script with value 584", + wire.TxOut{Value: 584, PkScript: pkScript}, + 1000, + true, + }, + { + "38 byte public key script with value 585", + wire.TxOut{Value: 585, PkScript: pkScript}, + 1000, + false, + }, + { + // Maximum allowed value is never dust. + "max satoshi amount is never dust", + wire.TxOut{Value: btcutil.MaxSatoshi, PkScript: pkScript}, + btcutil.MaxSatoshi, + false, + }, + { + // Maximum int64 value causes overflow. + "maximum int64 value", + wire.TxOut{Value: 1<<63 - 1, PkScript: pkScript}, + 1<<63 - 1, + true, + }, + { + // Unspendable pkScript due to an invalid public key + // script. + "unspendable pkScript", + wire.TxOut{Value: 5000, PkScript: []byte{0x01}}, + 0, // no relay fee + true, + }, + } + for _, test := range tests { + res := isDust(&test.txOut, test.relayFee) + if res != test.isDust { + t.Fatalf("Dust test '%s' failed: want %v got %v", + test.name, test.isDust, res) + continue + } + } +} + +// TestCheckTransactionStandard tests the checkTransactionStandard API. +func TestCheckTransactionStandard(t *testing.T) { + // Create some dummy, but otherwise standard, data for transactions. + prevOutHash, err := wire.NewShaHashFromStr("01") + if err != nil { + t.Fatalf("NewShaHashFromStr: unexpected error: %v", err) + } + dummyPrevOut := wire.OutPoint{Hash: *prevOutHash, Index: 1} + dummySigScript := bytes.Repeat([]byte{0x00}, 65) + dummyTxIn := wire.TxIn{ + PreviousOutPoint: dummyPrevOut, + SignatureScript: dummySigScript, + Sequence: wire.MaxTxInSequenceNum, + } + addrHash := [20]byte{0x01} + addr, err := btcutil.NewAddressPubKeyHash(addrHash[:], + &chaincfg.TestNet3Params) + if err != nil { + t.Fatalf("NewAddressPubKeyHash: unexpected error: %v", err) + } + dummyPkScript, err := txscript.PayToAddrScript(addr) + if err != nil { + t.Fatalf("PayToAddrScript: unexpected error: %v", err) + } + dummyTxOut := wire.TxOut{ + Value: 100000000, // 1 BTC + PkScript: dummyPkScript, + } + + tests := []struct { + name string + tx wire.MsgTx + height int32 + isStandard bool + code wire.RejectCode + }{ + { + name: "Typical pay-to-pubkey-hash transaction", + tx: wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{&dummyTxIn}, + TxOut: []*wire.TxOut{&dummyTxOut}, + LockTime: 0, + }, + height: 300000, + isStandard: true, + }, + { + name: "Transaction version too high", + tx: wire.MsgTx{ + Version: wire.TxVersion + 1, + TxIn: []*wire.TxIn{&dummyTxIn}, + TxOut: []*wire.TxOut{&dummyTxOut}, + LockTime: 0, + }, + height: 300000, + isStandard: false, + code: wire.RejectNonstandard, + }, + { + name: "Transaction is not finalized", + tx: wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{{ + PreviousOutPoint: dummyPrevOut, + SignatureScript: dummySigScript, + Sequence: 0, + }}, + TxOut: []*wire.TxOut{&dummyTxOut}, + LockTime: 300001, + }, + height: 300000, + isStandard: false, + code: wire.RejectNonstandard, + }, + { + name: "Transaction size is too large", + tx: wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{&dummyTxIn}, + TxOut: []*wire.TxOut{{ + Value: 0, + PkScript: bytes.Repeat([]byte{0x00}, + maxStandardTxSize+1), + }}, + LockTime: 0, + }, + height: 300000, + isStandard: false, + code: wire.RejectNonstandard, + }, + { + name: "Signature script size is too large", + tx: wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{{ + PreviousOutPoint: dummyPrevOut, + SignatureScript: bytes.Repeat([]byte{0x00}, + maxStandardSigScriptSize+1), + Sequence: wire.MaxTxInSequenceNum, + }}, + TxOut: []*wire.TxOut{&dummyTxOut}, + LockTime: 0, + }, + height: 300000, + isStandard: false, + code: wire.RejectNonstandard, + }, + { + name: "Signature script that does more than push data", + tx: wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{{ + PreviousOutPoint: dummyPrevOut, + SignatureScript: []byte{ + txscript.OP_CHECKSIGVERIFY}, + Sequence: wire.MaxTxInSequenceNum, + }}, + TxOut: []*wire.TxOut{&dummyTxOut}, + LockTime: 0, + }, + height: 300000, + isStandard: false, + code: wire.RejectNonstandard, + }, + { + name: "Valid but non standard public key script", + tx: wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{&dummyTxIn}, + TxOut: []*wire.TxOut{{ + Value: 100000000, + PkScript: []byte{txscript.OP_TRUE}, + }}, + LockTime: 0, + }, + height: 300000, + isStandard: false, + code: wire.RejectNonstandard, + }, + { + name: "More than one nulldata output", + tx: wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{&dummyTxIn}, + TxOut: []*wire.TxOut{{ + Value: 0, + PkScript: []byte{txscript.OP_RETURN}, + }, { + Value: 0, + PkScript: []byte{txscript.OP_RETURN}, + }}, + LockTime: 0, + }, + height: 300000, + isStandard: false, + code: wire.RejectNonstandard, + }, + { + name: "Dust output", + tx: wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{&dummyTxIn}, + TxOut: []*wire.TxOut{{ + Value: 0, + PkScript: dummyPkScript, + }}, + LockTime: 0, + }, + height: 300000, + isStandard: false, + code: wire.RejectDust, + }, + { + name: "One nulldata output with 0 amount (standard)", + tx: wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{&dummyTxIn}, + TxOut: []*wire.TxOut{{ + Value: 0, + PkScript: []byte{txscript.OP_RETURN}, + }}, + LockTime: 0, + }, + height: 300000, + isStandard: true, + }, + } + + timeSource := blockchain.NewMedianTime() + for _, test := range tests { + // Ensure standardness is as expected. + err := checkTransactionStandard(btcutil.NewTx(&test.tx), + test.height, timeSource, defaultMinRelayTxFee) + if err == nil && test.isStandard { + // Test passes since function returned standard for a + // transaction which is intended to be standard. + continue + } + if err == nil && !test.isStandard { + t.Errorf("checkTransactionStandard (%s): standard when "+ + "it should not be", test.name) + continue + } + if err != nil && test.isStandard { + t.Errorf("checkTransactionStandard (%s): nonstandard "+ + "when it should not be: %v", test.name, err) + continue + } + + // Ensure error type is a TxRuleError inside of a RuleError. + rerr, ok := err.(RuleError) + if !ok { + t.Errorf("checkTransactionStandard (%s): unexpected "+ + "error type - got %T", test.name, err) + continue + } + txrerr, ok := rerr.Err.(TxRuleError) + if !ok { + t.Errorf("checkTransactionStandard (%s): unexpected "+ + "error type - got %T", test.name, rerr.Err) + continue + } + + // Ensure the reject code is the expected one. + if txrerr.RejectCode != test.code { + t.Errorf("checkTransactionStandard (%s): unexpected "+ + "error code - got %v, want %v", test.name, + txrerr.RejectCode, test.code) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/release/notes.sample b/vendor/github.com/btcsuite/btcd/release/notes.sample new file mode 100644 index 0000000000000000000000000000000000000000..86a79a61f52e38955395840028058a36a82b3ee8 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/release/notes.sample @@ -0,0 +1,6 @@ +- Each release note is preceded by a dash +- Each release note must not exceed 74 characters per line +- Release notes that require a longer explanation than will fit on a + single line should be wrapped with the text indented as in this line +- No periods at the end of each release note +- Other minor cleanup and bug fixes diff --git a/vendor/github.com/btcsuite/btcd/release/prep_release.sh b/vendor/github.com/btcsuite/btcd/release/prep_release.sh new file mode 100644 index 0000000000000000000000000000000000000000..c64824a644cfea0f2e9fae5bd7315d50dd1f8b19 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/release/prep_release.sh @@ -0,0 +1,205 @@ +#!/bin/sh +# +# Copyright (c) 2013 Conformal Systems LLC <info@conformal.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# +# Prepares for a release: +# - Bumps version according to specified level (major, minor, or patch) +# - Updates all version files and package control files with new version +# - Performs some basic validation on specified release notes +# - Updates project changes file with release notes +# + +PROJECT=btcd +PROJECT_UC=$(echo $PROJECT | tr '[:lower:]' '[:upper:]') +SCRIPT=$(basename $0) +VERFILE=../version.go +VERFILES="$VERFILE ../cmd/btcctl/version.go" +PROJ_CHANGES=../CHANGES + +# verify params +if [ $# -lt 2 ]; then + echo "usage: $SCRIPT {major | minor | patch} release-notes-file" + exit 1 +fi + +CUR_DIR=$(pwd) +cd "$(dirname $0)" + +# verify version files exist +for verfile in $VERFILES; do + if [ ! -f "$verfile" ]; then + echo "$SCRIPT: error: $verfile does not exist" 1>&2 + exit 1 + fi +done + +# verify changes file exists +if [ ! -f "$PROJ_CHANGES" ]; then + echo "$SCRIPT: error: $PROJ_CHANGES does not exist" 1>&2 + exit 1 +fi + +RTYPE="$1" +RELEASE_NOTES="$2" +if [ $(echo $RELEASE_NOTES | cut -c1) != "/" ]; then + RELEASE_NOTES="$CUR_DIR/$RELEASE_NOTES" +fi + +# verify valid release type +if [ "$RTYPE" != "major" -a "$RTYPE" != "minor" -a "$RTYPE" != "patch" ]; then + echo "$SCRIPT: error: release type must be major, minor, or patch" + exit 1 +fi + +# verify release notes +if [ ! -e "$RELEASE_NOTES" ]; then + echo "$SCRIPT: error: specified release notes file does not exist" + exit 1 +fi + +if [ ! -s "$RELEASE_NOTES" ]; then + echo "$SCRIPT: error: specified release notes file is empty" + exit 1 +fi + +# verify release notes format +while IFS='' read line; do + if [ -z "$line" ]; then + echo "$SCRIPT: error: release notes must not have blank lines" + exit 1 + fi + if [ ${#line} -gt 74 ]; then + echo -n "$SCRIPT: error: release notes must not contain lines " + echo "with more than 74 characters" + exit 1 + fi + if expr "$line" : ".*\.$" >/dev/null 2>&1 ; then + echo -n "$SCRIPT: error: release notes must not contain lines " + echo "that end in a period" + exit 1 + fi + if ! expr "$line" : "\-" >/dev/null 2>&1; then + if ! expr "$line" : " " >/dev/null 2>&1; then + echo -n "$SCRIPT: error: release notes must not contain lines " + echo "that do not begin with a dash and are not indented" + exit 1 + fi + fi +done <"$RELEASE_NOTES" + +# verify git is available +if ! type git >/dev/null 2>&1; then + echo -n "$SCRIPT: error: Unable to find 'git' in the system path." + exit 1 +fi + +# verify the git repository is on the master branch +BRANCH=$(git branch | grep '\*' | cut -c3-) +if [ "$BRANCH" != "master" ]; then + echo "$SCRIPT: error: git repository must be on the master branch." + exit 1 +fi + +# verify there are no uncommitted modifications prior to release modifications +NUM_MODIFIED=$(git diff 2>/dev/null | wc -l | sed 's/^[ \t]*//') +NUM_STAGED=$(git diff --cached 2>/dev/null | wc -l | sed 's/^[ \t]*//') +if [ "$NUM_MODIFIED" != "0" -o "$NUM_STAGED" != "0" ]; then + echo -n "$SCRIPT: error: the working directory contains uncommitted " + echo "modifications" + exit 1 +fi + +# get version +PAT_PREFIX="(^[[:space:]]+app" +PAT_SUFFIX='[[:space:]]+uint[[:space:]]+=[[:space:]]+)[0-9]+$' +MAJOR=$(egrep "${PAT_PREFIX}Major${PAT_SUFFIX}" $VERFILE | awk '{print $4}') +MINOR=$(egrep "${PAT_PREFIX}Minor${PAT_SUFFIX}" $VERFILE | awk '{print $4}') +PATCH=$(egrep "${PAT_PREFIX}Patch${PAT_SUFFIX}" $VERFILE | awk '{print $4}') +if [ -z "$MAJOR" -o -z "$MINOR" -o -z "$PATCH" ]; then + echo "$SCRIPT: error: unable to get version from $VERFILE" 1>&2 + exit 1 +fi + +# bump version according to level +if [ "$RTYPE" = "major" ]; then + MAJOR=$(expr $MAJOR + 1) + MINOR=0 + PATCH=0 +elif [ "$RTYPE" = "minor" ]; then + MINOR=$(expr $MINOR + 1) + PATCH=0 +elif [ "$RTYPE" = "patch" ]; then + PATCH=$(expr $PATCH + 1) +fi +PROJ_VER="$MAJOR.$MINOR.$PATCH" + +# update project changes with release notes +DATE=$(date "+%a %b %d %Y") +awk -v D="$DATE" -v VER="$PROJ_VER" ' +/=======/ && first_line==0 { + first_line=1 + print $0 + next +} +/=======/ && first_line==1 { + print $0 + print "" + print "Changes in "VER" ("D")" + exit +} +{ print $0 } +' <"$PROJ_CHANGES" >"${PROJ_CHANGES}.tmp" +cat "$RELEASE_NOTES" | sed 's/^/ /' >>"${PROJ_CHANGES}.tmp" +awk ' +/=======/ && first_line==0 { + first_line=1 + next +} +/=======/ && first_line==1 { + second_line=1 + next +} +second_line==1 { print $0 } +' <"$PROJ_CHANGES" >>"${PROJ_CHANGES}.tmp" + +# update version filef with new version +for verfile in $VERFILES; do + sed -E " + s/${PAT_PREFIX}Major${PAT_SUFFIX}/\1${MAJOR}/; + s/${PAT_PREFIX}Minor${PAT_SUFFIX}/\1${MINOR}/; + s/${PAT_PREFIX}Patch${PAT_SUFFIX}/\1${PATCH}/; + " <"$verfile" >"${verfile}.tmp" +done + + +# Apply changes +mv "${PROJ_CHANGES}.tmp" "$PROJ_CHANGES" +for verfile in $VERFILES; do + mv "${verfile}.tmp" "$verfile" +done + +echo "All files have been prepared for release." +echo "Use the following commands to review the changes for accuracy:" +echo " git status" +echo " git diff" +echo "" +echo "If everything is accurate, use the following commands to commit, tag," +echo "and push the changes" +echo " git commit -am \"Prepare for release ${PROJ_VER}.\"" +echo -n " git tag -a \"${PROJECT_UC}_${MAJOR}_${MINOR}_${PATCH}\" -m " +echo "\"Release ${PROJ_VER}\"" +echo " git push" +echo " git push --tags" diff --git a/vendor/github.com/btcsuite/btcd/rpcserver.go b/vendor/github.com/btcsuite/btcd/rpcserver.go new file mode 100644 index 0000000000000000000000000000000000000000..2d1564c172d6e37f095f1b70b1fb201182112510 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/rpcserver.go @@ -0,0 +1,4246 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "crypto/subtle" + "crypto/tls" + "encoding/base64" + "encoding/binary" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "math/big" + "math/rand" + "net" + "net/http" + "os" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/mining" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/fastsha256" + "github.com/btcsuite/websocket" +) + +const ( + // rpcAuthTimeoutSeconds is the number of seconds a connection to the + // RPC server is allowed to stay open without authenticating before it + // is closed. + rpcAuthTimeoutSeconds = 10 + + // uint256Size is the number of bytes needed to represent an unsigned + // 256-bit integer. + uint256Size = 32 + + // getworkDataLen is the length of the data field of the getwork RPC. + // It consists of the serialized block header plus the internal sha256 + // padding. The internal sha256 padding consists of a single 1 bit + // followed by enough zeros to pad the message out to 56 bytes followed + // by length of the message in bits encoded as a big-endian uint64 + // (8 bytes). Thus, the resulting length is a multiple of the sha256 + // block size (64 bytes). + getworkDataLen = (1 + ((wire.MaxBlockHeaderPayload + 8) / + fastsha256.BlockSize)) * fastsha256.BlockSize + + // hash1Len is the length of the hash1 field of the getwork RPC. It + // consists of a zero hash plus the internal sha256 padding. See + // the getworkDataLen comment for details about the internal sha256 + // padding format. + hash1Len = (1 + ((wire.HashSize + 8) / fastsha256.BlockSize)) * + fastsha256.BlockSize + + // gbtNonceRange is two 32-bit big-endian hexadecimal integers which + // represent the valid ranges of nonces returned by the getblocktemplate + // RPC. + gbtNonceRange = "00000000ffffffff" + + // gbtRegenerateSeconds is the number of seconds that must pass before + // a new template is generated when the previous block hash has not + // changed and there have been changes to the available transactions + // in the memory pool. + gbtRegenerateSeconds = 60 + + // maxProtocolVersion is the max protocol version the server supports. + maxProtocolVersion = 70002 +) + +var ( + // gbtMutableFields are the manipulations the server allows to be made + // to block templates generated by the getblocktemplate RPC. It is + // declared here to avoid the overhead of creating the slice on every + // invocation for constant data. + gbtMutableFields = []string{ + "time", "transactions/add", "prevblock", "coinbase/append", + } + + // gbtCoinbaseAux describes additional data that miners should include + // in the coinbase signature script. It is declared here to avoid the + // overhead of creating a new object on every invocation for constant + // data. + gbtCoinbaseAux = &btcjson.GetBlockTemplateResultAux{ + Flags: hex.EncodeToString(builderScript(txscript. + NewScriptBuilder().AddData([]byte(coinbaseFlags)))), + } + + // gbtCapabilities describes additional capabilities returned with a + // block template generated by the getblocktemplate RPC. It is + // declared here to avoid the overhead of creating the slice on every + // invocation for constant data. + gbtCapabilities = []string{"proposal"} +) + +// Errors +var ( + // ErrRPCUnimplemented is an error returned to RPC clients when the + // provided command is recognized, but not implemented. + ErrRPCUnimplemented = &btcjson.RPCError{ + Code: btcjson.ErrRPCUnimplemented, + Message: "Command unimplemented", + } + + // ErrRPCNoWallet is an error returned to RPC clients when the provided + // command is recognized as a wallet command. + ErrRPCNoWallet = &btcjson.RPCError{ + Code: btcjson.ErrRPCNoWallet, + Message: "This implementation does not implement wallet commands", + } +) + +type commandHandler func(*rpcServer, interface{}, <-chan struct{}) (interface{}, error) + +// rpcHandlers maps RPC command strings to appropriate handler functions. +// This is set by init because help references rpcHandlers and thus causes +// a dependency loop. +var rpcHandlers map[string]commandHandler +var rpcHandlersBeforeInit = map[string]commandHandler{ + "addnode": handleAddNode, + "createrawtransaction": handleCreateRawTransaction, + "debuglevel": handleDebugLevel, + "decoderawtransaction": handleDecodeRawTransaction, + "decodescript": handleDecodeScript, + "generate": handleGenerate, + "getaddednodeinfo": handleGetAddedNodeInfo, + "getbestblock": handleGetBestBlock, + "getbestblockhash": handleGetBestBlockHash, + "getblock": handleGetBlock, + "getblockcount": handleGetBlockCount, + "getblockhash": handleGetBlockHash, + "getblockheader": handleGetBlockHeader, + "getblocktemplate": handleGetBlockTemplate, + "getconnectioncount": handleGetConnectionCount, + "getcurrentnet": handleGetCurrentNet, + "getdifficulty": handleGetDifficulty, + "getgenerate": handleGetGenerate, + "gethashespersec": handleGetHashesPerSec, + "getinfo": handleGetInfo, + "getmempoolinfo": handleGetMempoolInfo, + "getmininginfo": handleGetMiningInfo, + "getnettotals": handleGetNetTotals, + "getnetworkhashps": handleGetNetworkHashPS, + "getpeerinfo": handleGetPeerInfo, + "getrawmempool": handleGetRawMempool, + "getrawtransaction": handleGetRawTransaction, + "gettxout": handleGetTxOut, + "getwork": handleGetWork, + "help": handleHelp, + "node": handleNode, + "ping": handlePing, + "searchrawtransactions": handleSearchRawTransactions, + "sendrawtransaction": handleSendRawTransaction, + "setgenerate": handleSetGenerate, + "stop": handleStop, + "submitblock": handleSubmitBlock, + "validateaddress": handleValidateAddress, + "verifychain": handleVerifyChain, + "verifymessage": handleVerifyMessage, +} + +// list of commands that we recognize, but for which btcd has no support because +// it lacks support for wallet functionality. For these commands the user +// should ask a connected instance of btcwallet. +var rpcAskWallet = map[string]struct{}{ + "addmultisigaddress": {}, + "backupwallet": {}, + "createencryptedwallet": {}, + "createmultisig": {}, + "dumpprivkey": {}, + "dumpwallet": {}, + "encryptwallet": {}, + "getaccount": {}, + "getaccountaddress": {}, + "getaddressesbyaccount": {}, + "getbalance": {}, + "getnewaddress": {}, + "getrawchangeaddress": {}, + "getreceivedbyaccount": {}, + "getreceivedbyaddress": {}, + "gettransaction": {}, + "gettxoutsetinfo": {}, + "getunconfirmedbalance": {}, + "getwalletinfo": {}, + "importprivkey": {}, + "importwallet": {}, + "keypoolrefill": {}, + "listaccounts": {}, + "listaddressgroupings": {}, + "listlockunspent": {}, + "listreceivedbyaccount": {}, + "listreceivedbyaddress": {}, + "listsinceblock": {}, + "listtransactions": {}, + "listunspent": {}, + "lockunspent": {}, + "move": {}, + "sendfrom": {}, + "sendmany": {}, + "sendtoaddress": {}, + "setaccount": {}, + "settxfee": {}, + "signmessage": {}, + "signrawtransaction": {}, + "walletlock": {}, + "walletpassphrase": {}, + "walletpassphrasechange": {}, +} + +// Commands that are currently unimplemented, but should ultimately be. +var rpcUnimplemented = map[string]struct{}{ + "estimatefee": {}, + "estimatepriority": {}, + "getblockchaininfo": {}, + "getchaintips": {}, + "getnetworkinfo": {}, +} + +// Commands that are available to a limited user +var rpcLimited = map[string]struct{}{ + // Websockets commands + "notifyblocks": {}, + "notifynewtransactions": {}, + "notifyreceived": {}, + "notifyspent": {}, + "rescan": {}, + "session": {}, + + // Websockets AND HTTP/S commands + "help": {}, + + // HTTP/S-only commands + "createrawtransaction": {}, + "decoderawtransaction": {}, + "decodescript": {}, + "getbestblock": {}, + "getbestblockhash": {}, + "getblock": {}, + "getblockcount": {}, + "getblockhash": {}, + "getcurrentnet": {}, + "getdifficulty": {}, + "getinfo": {}, + "getnettotals": {}, + "getnetworkhashps": {}, + "getrawmempool": {}, + "getrawtransaction": {}, + "gettxout": {}, + "searchrawtransactions": {}, + "sendrawtransaction": {}, + "submitblock": {}, + "validateaddress": {}, + "verifymessage": {}, +} + +// builderScript is a convenience function which is used for hard-coded scripts +// built with the script builder. Any errors are converted to a panic since it +// is only, and must only, be used with hard-coded, and therefore, known good, +// scripts. +func builderScript(builder *txscript.ScriptBuilder) []byte { + script, err := builder.Script() + if err != nil { + panic(err) + } + return script +} + +// internalRPCError is a convenience function to convert an internal error to +// an RPC error with the appropriate code set. It also logs the error to the +// RPC server subsystem since internal errors really should not occur. The +// context parameter is only used in the log message and may be empty if it's +// not needed. +func internalRPCError(errStr, context string) *btcjson.RPCError { + logStr := errStr + if context != "" { + logStr = context + ": " + errStr + } + rpcsLog.Error(logStr) + return btcjson.NewRPCError(btcjson.ErrRPCInternal.Code, errStr) +} + +// rpcDecodeHexError is a convenience function for returning a nicely formatted +// RPC error which indicates the provided hex string failed to decode. +func rpcDecodeHexError(gotHex string) *btcjson.RPCError { + return btcjson.NewRPCError(btcjson.ErrRPCDecodeHexString, + fmt.Sprintf("Argument must be hexadecimal string (not %q)", + gotHex)) +} + +// rpcNoTxInfoError is a convenience function for returning a nicely formatted +// RPC error which indiactes there is no information available for the provided +// transaction hash. +func rpcNoTxInfoError(txHash *wire.ShaHash) *btcjson.RPCError { + return btcjson.NewRPCError(btcjson.ErrRPCNoTxInfo, + fmt.Sprintf("No information available about transaction %v", + txHash)) +} + +// workStateBlockInfo houses information about how to reconstruct a block given +// its template and signature script. +type workStateBlockInfo struct { + msgBlock *wire.MsgBlock + signatureScript []byte +} + +// workState houses state that is used in between multiple RPC invocations to +// getwork. +type workState struct { + sync.Mutex + lastTxUpdate time.Time + lastGenerated time.Time + prevHash *wire.ShaHash + msgBlock *wire.MsgBlock + extraNonce uint64 + blockInfo map[wire.ShaHash]*workStateBlockInfo +} + +// newWorkState returns a new instance of a workState with all internal fields +// initialized and ready to use. +func newWorkState() *workState { + return &workState{ + blockInfo: make(map[wire.ShaHash]*workStateBlockInfo), + } +} + +// gbtWorkState houses state that is used in between multiple RPC invocations to +// getblocktemplate. +type gbtWorkState struct { + sync.Mutex + lastTxUpdate time.Time + lastGenerated time.Time + prevHash *wire.ShaHash + minTimestamp time.Time + template *BlockTemplate + notifyMap map[wire.ShaHash]map[int64]chan struct{} + timeSource blockchain.MedianTimeSource +} + +// newGbtWorkState returns a new instance of a gbtWorkState with all internal +// fields initialized and ready to use. +func newGbtWorkState(timeSource blockchain.MedianTimeSource) *gbtWorkState { + return &gbtWorkState{ + notifyMap: make(map[wire.ShaHash]map[int64]chan struct{}), + timeSource: timeSource, + } +} + +// handleUnimplemented is the handler for commands that should ultimately be +// supported but are not yet implemented. +func handleUnimplemented(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + return nil, ErrRPCUnimplemented +} + +// handleAskWallet is the handler for commands that are recognized as valid, but +// are unable to answer correctly since it involves wallet state. +// These commands will be implemented in btcwallet. +func handleAskWallet(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + return nil, ErrRPCNoWallet +} + +// handleAddNode handles addnode commands. +func handleAddNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.AddNodeCmd) + + addr := normalizeAddress(c.Addr, activeNetParams.DefaultPort) + var err error + switch c.SubCmd { + case "add": + err = s.server.ConnectNode(addr, true) + case "remove": + err = s.server.RemoveNodeByAddr(addr) + case "onetry": + err = s.server.ConnectNode(addr, false) + default: + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: "invalid subcommand for addnode", + } + } + + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: err.Error(), + } + } + + // no data returned unless an error. + return nil, nil +} + +// handleNode handles node commands. +func handleNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.NodeCmd) + + var addr string + var nodeID uint64 + var errN, err error + switch c.SubCmd { + case "disconnect": + // If we have a valid uint disconnect by node id. Otherwise, + // attempt to disconnect by address, returning an error if a + // valid IP address is not supplied. + if nodeID, errN = strconv.ParseUint(c.Target, 10, 32); errN == nil { + err = s.server.DisconnectNodeByID(int32(nodeID)) + } else { + if _, _, errP := net.SplitHostPort(c.Target); errP == nil || net.ParseIP(c.Target) != nil { + addr = normalizeAddress(c.Target, activeNetParams.DefaultPort) + err = s.server.DisconnectNodeByAddr(addr) + } else { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: "invalid address or node ID", + } + } + } + if err != nil && peerExists(s.server.Peers(), addr, int32(nodeID)) { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCMisc, + Message: "can't disconnect a permanent peer, use remove", + } + } + case "remove": + // If we have a valid uint disconnect by node id. Otherwise, + // attempt to disconnect by address, returning an error if a + // valid IP address is not supplied. + if nodeID, errN = strconv.ParseUint(c.Target, 10, 32); errN == nil { + err = s.server.RemoveNodeByID(int32(nodeID)) + } else { + if _, _, errP := net.SplitHostPort(c.Target); errP == nil || net.ParseIP(c.Target) != nil { + addr = normalizeAddress(c.Target, activeNetParams.DefaultPort) + err = s.server.RemoveNodeByAddr(addr) + } else { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: "invalid address or node ID", + } + } + } + if err != nil && peerExists(s.server.Peers(), addr, int32(nodeID)) { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCMisc, + Message: "can't remove a temporary peer, use disconnect", + } + } + case "connect": + addr = normalizeAddress(c.Target, activeNetParams.DefaultPort) + + // Default to temporary connections. + subCmd := "temp" + if c.ConnectSubCmd != nil { + subCmd = *c.ConnectSubCmd + } + + switch subCmd { + case "perm", "temp": + err = s.server.ConnectNode(addr, subCmd == "perm") + default: + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: "invalid subcommand for node connect", + } + } + default: + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: "invalid subcommand for node", + } + } + + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: err.Error(), + } + } + + // no data returned unless an error. + return nil, nil +} + +// peerExists determines if a certain peer is currently connected given +// information about all currently connected peers. Peer existence is +// determined using either a target address or node id. +func peerExists(peers []*serverPeer, addr string, nodeID int32) bool { + for _, p := range peers { + if p.ID() == nodeID || p.Addr() == addr { + return true + } + } + return false +} + +// messageToHex serializes a message to the wire protocol encoding using the +// latest protocol version and returns a hex-encoded string of the result. +func messageToHex(msg wire.Message) (string, error) { + var buf bytes.Buffer + if err := msg.BtcEncode(&buf, maxProtocolVersion); err != nil { + context := fmt.Sprintf("Failed to encode msg of type %T", msg) + return "", internalRPCError(err.Error(), context) + } + + return hex.EncodeToString(buf.Bytes()), nil +} + +// handleCreateRawTransaction handles createrawtransaction commands. +func handleCreateRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.CreateRawTransactionCmd) + + // Validate the locktime, if given. + if c.LockTime != nil && + (*c.LockTime < 0 || *c.LockTime > int64(wire.MaxTxInSequenceNum)) { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: "Locktime out of range", + } + } + + // Add all transaction inputs to a new transaction after performing + // some validity checks. + mtx := wire.NewMsgTx() + for _, input := range c.Inputs { + txHash, err := wire.NewShaHashFromStr(input.Txid) + if err != nil { + return nil, rpcDecodeHexError(input.Txid) + } + + prevOut := wire.NewOutPoint(txHash, uint32(input.Vout)) + txIn := wire.NewTxIn(prevOut, []byte{}) + if c.LockTime != nil && *c.LockTime != 0 { + txIn.Sequence = wire.MaxTxInSequenceNum - 1 + } + mtx.AddTxIn(txIn) + } + + // Add all transaction outputs to the transaction after performing + // some validity checks. + for encodedAddr, amount := range c.Amounts { + // Ensure amount is in the valid range for monetary amounts. + if amount <= 0 || amount > btcutil.MaxSatoshi { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCType, + Message: "Invalid amount", + } + } + + // Decode the provided address. + addr, err := btcutil.DecodeAddress(encodedAddr, + activeNetParams.Params) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidAddressOrKey, + Message: "Invalid address or key: " + err.Error(), + } + } + + // Ensure the address is one of the supported types and that + // the network encoded with the address matches the network the + // server is currently on. + switch addr.(type) { + case *btcutil.AddressPubKeyHash: + case *btcutil.AddressScriptHash: + default: + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidAddressOrKey, + Message: "Invalid address or key", + } + } + if !addr.IsForNet(s.server.chainParams) { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidAddressOrKey, + Message: "Invalid address: " + encodedAddr + + " is for the wrong network", + } + } + + // Create a new script which pays to the provided address. + pkScript, err := txscript.PayToAddrScript(addr) + if err != nil { + context := "Failed to generate pay-to-address script" + return nil, internalRPCError(err.Error(), context) + } + + // Convert the amount to satoshi. + satoshi, err := btcutil.NewAmount(amount) + if err != nil { + context := "Failed to convert amount" + return nil, internalRPCError(err.Error(), context) + } + + txOut := wire.NewTxOut(int64(satoshi), pkScript) + mtx.AddTxOut(txOut) + } + + // Set the Locktime, if given. + if c.LockTime != nil { + mtx.LockTime = uint32(*c.LockTime) + } + + // Return the serialized and hex-encoded transaction. Note that this + // is intentionally not directly returning because the first return + // value is a string and it would result in returning an empty string to + // the client instead of nothing (nil) in the case of an error. + mtxHex, err := messageToHex(mtx) + if err != nil { + return nil, err + } + return mtxHex, nil +} + +// handleDebugLevel handles debuglevel commands. +func handleDebugLevel(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.DebugLevelCmd) + + // Special show command to list supported subsystems. + if c.LevelSpec == "show" { + return fmt.Sprintf("Supported subsystems %v", + supportedSubsystems()), nil + } + + err := parseAndSetDebugLevels(c.LevelSpec) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParams.Code, + Message: err.Error(), + } + } + + return "Done.", nil +} + +// createVinList returns a slice of JSON objects for the inputs of the passed +// transaction. +func createVinList(mtx *wire.MsgTx) []btcjson.Vin { + // Coinbase transactions only have a single txin by definition. + vinList := make([]btcjson.Vin, len(mtx.TxIn)) + if blockchain.IsCoinBaseTx(mtx) { + txIn := mtx.TxIn[0] + vinList[0].Coinbase = hex.EncodeToString(txIn.SignatureScript) + vinList[0].Sequence = txIn.Sequence + return vinList + } + + for i, txIn := range mtx.TxIn { + // The disassembled string will contain [error] inline + // if the script doesn't fully parse, so ignore the + // error here. + disbuf, _ := txscript.DisasmString(txIn.SignatureScript) + + vinEntry := &vinList[i] + vinEntry.Txid = txIn.PreviousOutPoint.Hash.String() + vinEntry.Vout = txIn.PreviousOutPoint.Index + vinEntry.Sequence = txIn.Sequence + vinEntry.ScriptSig = &btcjson.ScriptSig{ + Asm: disbuf, + Hex: hex.EncodeToString(txIn.SignatureScript), + } + } + + return vinList +} + +// createVoutList returns a slice of JSON objects for the outputs of the passed +// transaction. +func createVoutList(mtx *wire.MsgTx, chainParams *chaincfg.Params, filterAddrMap map[string]struct{}) []btcjson.Vout { + voutList := make([]btcjson.Vout, 0, len(mtx.TxOut)) + for i, v := range mtx.TxOut { + // The disassembled string will contain [error] inline if the + // script doesn't fully parse, so ignore the error here. + disbuf, _ := txscript.DisasmString(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, _ := txscript.ExtractPkScriptAddrs( + v.PkScript, chainParams) + + // Encode the addresses while checking if the address passes the + // filter when needed. + passesFilter := len(filterAddrMap) == 0 + encodedAddrs := make([]string, len(addrs)) + for j, addr := range addrs { + encodedAddr := addr.EncodeAddress() + encodedAddrs[j] = encodedAddr + + // No need to check the map again if the filter already + // passes. + if passesFilter { + continue + } + if _, exists := filterAddrMap[encodedAddr]; exists { + passesFilter = true + } + } + + if !passesFilter { + continue + } + + var vout btcjson.Vout + vout.N = uint32(i) + vout.Value = btcutil.Amount(v.Value).ToBTC() + vout.ScriptPubKey.Addresses = encodedAddrs + vout.ScriptPubKey.Asm = disbuf + vout.ScriptPubKey.Hex = hex.EncodeToString(v.PkScript) + vout.ScriptPubKey.Type = scriptClass.String() + vout.ScriptPubKey.ReqSigs = int32(reqSigs) + + voutList = append(voutList, vout) + } + + return voutList +} + +// createTxRawResult converts the passed transaction and associated parameters +// to a raw transaction JSON object. +func createTxRawResult(chainParams *chaincfg.Params, mtx *wire.MsgTx, + txHash string, blkHeader *wire.BlockHeader, blkHash string, + blkHeight int32, chainHeight int32) (*btcjson.TxRawResult, error) { + + mtxHex, err := messageToHex(mtx) + if err != nil { + return nil, err + } + + txReply := &btcjson.TxRawResult{ + Hex: mtxHex, + Txid: txHash, + Vin: createVinList(mtx), + Vout: createVoutList(mtx, chainParams, nil), + Version: mtx.Version, + LockTime: mtx.LockTime, + } + + if blkHeader != nil { + // This is not a typo, they are identical in bitcoind as well. + txReply.Time = blkHeader.Timestamp.Unix() + txReply.Blocktime = blkHeader.Timestamp.Unix() + txReply.BlockHash = blkHash + txReply.Confirmations = uint64(1 + chainHeight - blkHeight) + } + + return txReply, nil +} + +// handleDecodeRawTransaction handles decoderawtransaction commands. +func handleDecodeRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.DecodeRawTransactionCmd) + + // Deserialize the transaction. + hexStr := c.HexTx + if len(hexStr)%2 != 0 { + hexStr = "0" + hexStr + } + serializedTx, err := hex.DecodeString(hexStr) + if err != nil { + return nil, rpcDecodeHexError(hexStr) + } + var mtx wire.MsgTx + err = mtx.Deserialize(bytes.NewReader(serializedTx)) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCDeserialization, + Message: "TX decode failed: " + err.Error(), + } + } + + // Create and return the result. + txReply := btcjson.TxRawDecodeResult{ + Txid: mtx.TxSha().String(), + Version: mtx.Version, + Locktime: mtx.LockTime, + Vin: createVinList(&mtx), + Vout: createVoutList(&mtx, s.server.chainParams, nil), + } + return txReply, nil +} + +// handleDecodeScript handles decodescript commands. +func handleDecodeScript(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.DecodeScriptCmd) + + // Convert the hex script to bytes. + hexStr := c.HexScript + if len(hexStr)%2 != 0 { + hexStr = "0" + hexStr + } + script, err := hex.DecodeString(hexStr) + if err != nil { + return nil, rpcDecodeHexError(hexStr) + } + + // The disassembled string will contain [error] inline if the script + // doesn't fully parse, so ignore the error here. + disbuf, _ := txscript.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. + scriptClass, addrs, reqSigs, _ := txscript.ExtractPkScriptAddrs(script, + s.server.chainParams) + 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, s.server.chainParams) + if err != nil { + context := "Failed to convert script to pay-to-script-hash" + return nil, internalRPCError(err.Error(), context) + } + + // Generate and return the reply. + reply := btcjson.DecodeScriptResult{ + Asm: disbuf, + ReqSigs: int32(reqSigs), + Type: scriptClass.String(), + Addresses: addresses, + P2sh: p2sh.EncodeAddress(), + } + return reply, nil +} + +// handleGenerate handles generate commands. +func handleGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + // Respond with an error if there are no addresses to pay the + // created blocks to. + if len(cfg.miningAddrs) == 0 { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInternal.Code, + Message: "No payment addresses specified " + + "via --miningaddr", + } + } + + c := cmd.(*btcjson.GenerateCmd) + + // Respond with an error if the client is requesting 0 blocks to be generated. + if c.NumBlocks == 0 { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInternal.Code, + Message: "Please request a nonzero number of blocks to generate.", + } + } + + // Create a reply + reply := make([]string, c.NumBlocks) + + blockHashes, err := s.server.cpuMiner.GenerateNBlocks(c.NumBlocks) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInternal.Code, + Message: err.Error(), + } + } + + // Mine the correct number of blocks, assigning the hex representation of the + // hash of each one to its place in the reply. + for i, hash := range blockHashes { + reply[i] = hash.String() + } + + return reply, nil +} + +// handleGetAddedNodeInfo handles getaddednodeinfo commands. +func handleGetAddedNodeInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.GetAddedNodeInfoCmd) + + // Retrieve a list of persistent (added) peers from the bitcoin server + // and filter the list of peers per the specified address (if any). + peers := s.server.AddedNodeInfo() + if c.Node != nil { + node := *c.Node + found := false + for i, peer := range peers { + if peer.Addr() == node { + peers = peers[i : i+1] + found = true + } + } + if !found { + return nil, &btcjson.RPCError{ + Code: -24, // TODO: ErrRPCClientNodeNotAdded + Message: "Node has not been added", + } + } + } + + // Without the dns flag, the result is just a slice of the addresses as + // strings. + if !c.DNS { + results := make([]string, 0, len(peers)) + for _, peer := range peers { + results = append(results, peer.Addr()) + } + return results, nil + } + + // With the dns flag, the result is an array of JSON objects which + // include the result of DNS lookups for each peer. + results := make([]*btcjson.GetAddedNodeInfoResult, 0, len(peers)) + for _, peer := range peers { + // Set the "address" of the peer which could be an ip address + // or a domain name. + var result btcjson.GetAddedNodeInfoResult + result.AddedNode = peer.Addr() + result.Connected = btcjson.Bool(peer.Connected()) + + // Split the address into host and port portions so we can do + // a DNS lookup against the host. When no port is specified in + // the address, just use the address as the host. + host, _, err := net.SplitHostPort(peer.Addr()) + if err != nil { + host = peer.Addr() + } + + // Do a DNS lookup for the address. If the lookup fails, just + // use the host. + var ipList []string + ips, err := btcdLookup(host) + if err == nil { + ipList = make([]string, 0, len(ips)) + for _, ip := range ips { + ipList = append(ipList, ip.String()) + } + } else { + ipList = make([]string, 1) + ipList[0] = host + } + + // Add the addresses and connection info to the result. + addrs := make([]btcjson.GetAddedNodeInfoResultAddr, 0, len(ipList)) + for _, ip := range ipList { + var addr btcjson.GetAddedNodeInfoResultAddr + addr.Address = ip + addr.Connected = "false" + if ip == host && peer.Connected() { + addr.Connected = directionString(peer.Inbound()) + } + addrs = append(addrs, addr) + } + result.Addresses = &addrs + results = append(results, &result) + } + return results, nil +} + +// handleGetBestBlock implements the getbestblock command. +func handleGetBestBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + // All other "get block" commands give either the height, the + // hash, or both but require the block SHA. This gets both for + // the best block. + best := s.chain.BestSnapshot() + result := &btcjson.GetBestBlockResult{ + Hash: best.Hash.String(), + Height: best.Height, + } + return result, nil +} + +// handleGetBestBlockHash implements the getbestblockhash command. +func handleGetBestBlockHash(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + best := s.chain.BestSnapshot() + return best.Hash.String(), nil +} + +// getDifficultyRatio returns the proof-of-work difficulty as a multiple of the +// minimum difficulty using the passed bits field from the header of a block. +func getDifficultyRatio(bits uint32) float64 { + // The minimum difficulty is the max possible proof-of-work limit bits + // converted back to a number. Note this is not the same as the proof of + // work limit directly because the block difficulty is encoded in a block + // with the compact form which loses precision. + max := blockchain.CompactToBig(activeNetParams.PowLimitBits) + target := blockchain.CompactToBig(bits) + + difficulty := new(big.Rat).SetFrac(max, target) + outString := difficulty.FloatString(8) + diff, err := strconv.ParseFloat(outString, 64) + if err != nil { + rpcsLog.Errorf("Cannot get difficulty: %v", err) + return 0 + } + return diff +} + +// handleGetBlock implements the getblock command. +func handleGetBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.GetBlockCmd) + + // Load the raw block bytes from the database. + hash, err := wire.NewShaHashFromStr(c.Hash) + if err != nil { + return nil, rpcDecodeHexError(c.Hash) + } + var blkBytes []byte + err = s.server.db.View(func(dbTx database.Tx) error { + var err error + blkBytes, err = dbTx.FetchBlock(hash) + return err + }) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCBlockNotFound, + Message: "Block not found", + } + } + + // When the verbose flag isn't set, simply return the serialized block + // as a hex-encoded string. + if c.Verbose != nil && !*c.Verbose { + return hex.EncodeToString(blkBytes), nil + } + + // The verbose flag is set, so generate the JSON object and return it. + + // Deserialize the block. + blk, err := btcutil.NewBlockFromBytes(blkBytes) + if err != nil { + context := "Failed to deserialize block" + return nil, internalRPCError(err.Error(), context) + } + + // Get the block height from chain. + blockHeight, err := s.chain.BlockHeightByHash(hash) + if err != nil { + context := "Failed to obtain block height" + return nil, internalRPCError(err.Error(), context) + } + blk.SetHeight(blockHeight) + best := s.chain.BestSnapshot() + + // Get next block hash unless there are none. + var nextHashString string + if blockHeight < best.Height { + nextHash, err := s.chain.BlockHashByHeight(blockHeight + 1) + if err != nil { + context := "No next block" + return nil, internalRPCError(err.Error(), context) + } + nextHashString = nextHash.String() + } + + blockHeader := &blk.MsgBlock().Header + blockReply := btcjson.GetBlockVerboseResult{ + Hash: c.Hash, + Version: blockHeader.Version, + MerkleRoot: blockHeader.MerkleRoot.String(), + PreviousHash: blockHeader.PrevBlock.String(), + Nonce: blockHeader.Nonce, + Time: blockHeader.Timestamp.Unix(), + Confirmations: uint64(1 + best.Height - blockHeight), + Height: int64(blockHeight), + Size: int32(len(blkBytes)), + Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), + Difficulty: getDifficultyRatio(blockHeader.Bits), + NextHash: nextHashString, + } + + if c.VerboseTx == nil || !*c.VerboseTx { + transactions := blk.Transactions() + txNames := make([]string, len(transactions)) + for i, tx := range transactions { + txNames[i] = tx.Sha().String() + } + + blockReply.Tx = txNames + } else { + txns := blk.Transactions() + rawTxns := make([]btcjson.TxRawResult, len(txns)) + for i, tx := range txns { + rawTxn, err := createTxRawResult(s.server.chainParams, + tx.MsgTx(), tx.Sha().String(), blockHeader, + hash.String(), blockHeight, best.Height) + if err != nil { + return nil, err + } + rawTxns[i] = *rawTxn + } + blockReply.RawTx = rawTxns + } + + return blockReply, nil +} + +// handleGetBlockCount implements the getblockcount command. +func handleGetBlockCount(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + best := s.chain.BestSnapshot() + return int64(best.Height), nil +} + +// handleGetBlockHash implements the getblockhash command. +func handleGetBlockHash(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.GetBlockHashCmd) + hash, err := s.chain.BlockHashByHeight(int32(c.Index)) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCOutOfRange, + Message: "Block number out of range", + } + } + + return hash.String(), nil +} + +// handleGetBlockHeader implements the getblockheader command. +func handleGetBlockHeader(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.GetBlockHeaderCmd) + + // Load the raw header bytes from the database. + hash, err := wire.NewShaHashFromStr(c.Hash) + if err != nil { + return nil, rpcDecodeHexError(c.Hash) + } + var headerBytes []byte + err = s.server.db.View(func(dbTx database.Tx) error { + var err error + headerBytes, err = dbTx.FetchBlockHeader(hash) + return err + }) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCBlockNotFound, + Message: "Block not found", + } + } + + // When the verbose flag isn't set, simply return the serialized block + // header as a hex-encoded string. + if c.Verbose != nil && !*c.Verbose { + return hex.EncodeToString(headerBytes), nil + } + + // The verbose flag is set, so generate the JSON object and return it. + + // Deserialize the header. + var blockHeader wire.BlockHeader + err = blockHeader.Deserialize(bytes.NewReader(headerBytes)) + if err != nil { + context := "Failed to deserialize block header" + return nil, internalRPCError(err.Error(), context) + } + + // Get the block height from chain. + blockHeight, err := s.chain.BlockHeightByHash(hash) + if err != nil { + context := "Failed to obtain block height" + return nil, internalRPCError(err.Error(), context) + } + best := s.chain.BestSnapshot() + + // Get next block hash unless there are none. + var nextHashString string + if blockHeight < best.Height { + nextHash, err := s.chain.BlockHashByHeight(blockHeight + 1) + if err != nil { + context := "No next block" + return nil, internalRPCError(err.Error(), context) + } + nextHashString = nextHash.String() + } + + blockHeaderReply := btcjson.GetBlockHeaderVerboseResult{ + Hash: c.Hash, + Confirmations: uint64(1 + best.Height - blockHeight), + Height: int32(blockHeight), + Version: blockHeader.Version, + MerkleRoot: blockHeader.MerkleRoot.String(), + NextHash: nextHashString, + PreviousHash: blockHeader.PrevBlock.String(), + Nonce: uint64(blockHeader.Nonce), + Time: blockHeader.Timestamp.Unix(), + Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), + Difficulty: getDifficultyRatio(blockHeader.Bits), + } + return blockHeaderReply, nil +} + +// encodeTemplateID encodes the passed details into an ID that can be used to +// uniquely identify a block template. +func encodeTemplateID(prevHash *wire.ShaHash, lastGenerated time.Time) string { + return fmt.Sprintf("%s-%d", prevHash.String(), lastGenerated.Unix()) +} + +// decodeTemplateID decodes an ID that is used to uniquely identify a block +// template. This is mainly used as a mechanism to track when to update clients +// that are using long polling for block templates. The ID consists of the +// previous block hash for the associated template and the time the associated +// template was generated. +func decodeTemplateID(templateID string) (*wire.ShaHash, int64, error) { + fields := strings.Split(templateID, "-") + if len(fields) != 2 { + return nil, 0, errors.New("invalid longpollid format") + } + + prevHash, err := wire.NewShaHashFromStr(fields[0]) + if err != nil { + return nil, 0, errors.New("invalid longpollid format") + } + lastGenerated, err := strconv.ParseInt(fields[1], 10, 64) + if err != nil { + return nil, 0, errors.New("invalid longpollid format") + } + + return prevHash, lastGenerated, nil +} + +// notifyLongPollers notifies any channels that have been registered to be +// notified when block templates are stale. +// +// This function MUST be called with the state locked. +func (state *gbtWorkState) notifyLongPollers(latestHash *wire.ShaHash, lastGenerated time.Time) { + // Notify anything that is waiting for a block template update from a + // hash which is not the hash of the tip of the best chain since their + // work is now invalid. + for hash, channels := range state.notifyMap { + if !hash.IsEqual(latestHash) { + for _, c := range channels { + close(c) + } + delete(state.notifyMap, hash) + } + } + + // Return now if the provided last generated timestamp has not been + // initialized. + if lastGenerated.IsZero() { + return + } + + // Return now if there is nothing registered for updates to the current + // best block hash. + channels, ok := state.notifyMap[*latestHash] + if !ok { + return + } + + // Notify anything that is waiting for a block template update from a + // block template generated before the most recently generated block + // template. + lastGeneratedUnix := lastGenerated.Unix() + for lastGen, c := range channels { + if lastGen < lastGeneratedUnix { + close(c) + delete(channels, lastGen) + } + } + + // Remove the entry altogether if there are no more registered + // channels. + if len(channels) == 0 { + delete(state.notifyMap, *latestHash) + } +} + +// NotifyBlockConnected uses the newly-connected block to notify any long poll +// clients with a new block template when their existing block template is +// stale due to the newly connected block. +func (state *gbtWorkState) NotifyBlockConnected(blockSha *wire.ShaHash) { + go func() { + state.Lock() + defer state.Unlock() + + state.notifyLongPollers(blockSha, state.lastTxUpdate) + }() +} + +// NotifyMempoolTx uses the new last updated time for the transaction memory +// pool to notify any long poll clients with a new block template when their +// existing block template is stale due to enough time passing and the contents +// of the memory pool changing. +func (state *gbtWorkState) NotifyMempoolTx(lastUpdated time.Time) { + go func() { + state.Lock() + defer state.Unlock() + + // No need to notify anything if no block templates have been generated + // yet. + if state.prevHash == nil || state.lastGenerated.IsZero() { + return + } + + if time.Now().After(state.lastGenerated.Add(time.Second * + gbtRegenerateSeconds)) { + + state.notifyLongPollers(state.prevHash, lastUpdated) + } + }() +} + +// templateUpdateChan returns a channel that will be closed once the block +// template associated with the passed previous hash and last generated time +// is stale. The function will return existing channels for duplicate +// parameters which allows multiple clients to wait for the same block template +// without requiring a different channel for each client. +// +// This function MUST be called with the state locked. +func (state *gbtWorkState) templateUpdateChan(prevHash *wire.ShaHash, lastGenerated int64) chan struct{} { + // Either get the current list of channels waiting for updates about + // changes to block template for the previous hash or create a new one. + channels, ok := state.notifyMap[*prevHash] + if !ok { + m := make(map[int64]chan struct{}) + state.notifyMap[*prevHash] = m + channels = m + } + + // Get the current channel associated with the time the block template + // was last generated or create a new one. + c, ok := channels[lastGenerated] + if !ok { + c = make(chan struct{}) + channels[lastGenerated] = c + } + + return c +} + +// updateBlockTemplate creates or updates a block template for the work state. +// A new block template will be generated when the current best block has +// changed or the transactions in the memory pool have been updated and it has +// been long enough since the last template was generated. Otherwise, the +// timestamp for the existing block template is updated (and possibly the +// difficulty on testnet per the consesus rules). Finally, if the +// useCoinbaseValue flag is false and the existing block template does not +// already contain a valid payment address, the block template will be updated +// with a randomly selected payment address from the list of configured +// addresses. +// +// This function MUST be called with the state locked. +func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bool) error { + lastTxUpdate := s.server.txMemPool.LastUpdated() + if lastTxUpdate.IsZero() { + lastTxUpdate = time.Now() + } + + // Generate a new block template when the current best block has + // changed or the transactions in the memory pool have been updated and + // it has been at least gbtRegenerateSecond since the last template was + // generated. + var msgBlock *wire.MsgBlock + var targetDifficulty string + latestHash, _ := s.server.blockManager.chainState.Best() + template := state.template + if template == nil || state.prevHash == nil || + !state.prevHash.IsEqual(latestHash) || + (state.lastTxUpdate != lastTxUpdate && + time.Now().After(state.lastGenerated.Add(time.Second* + gbtRegenerateSeconds))) { + + // Reset the previous best hash the block template was generated + // against so any errors below cause the next invocation to try + // again. + state.prevHash = nil + + // Choose a payment address at random if the caller requests a + // full coinbase as opposed to only the pertinent details needed + // to create their own coinbase. + var payAddr btcutil.Address + if !useCoinbaseValue { + payAddr = cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] + } + + // Create a new block template that has a coinbase which anyone + // can redeem. This is only acceptable because the returned + // block template doesn't include the coinbase, so the caller + // will ultimately create their own coinbase which pays to the + // appropriate address(es). + blkTemplate, err := NewBlockTemplate(s.policy, s.server, payAddr) + if err != nil { + return internalRPCError("Failed to create new block "+ + "template: "+err.Error(), "") + } + template = blkTemplate + msgBlock = template.Block + targetDifficulty = fmt.Sprintf("%064x", + blockchain.CompactToBig(msgBlock.Header.Bits)) + + // Find the minimum allowed timestamp for the block based on the + // median timestamp of the last several blocks per the chain + // consensus rules. + chainState := &s.server.blockManager.chainState + minTimestamp, err := minimumMedianTime(chainState) + if err != nil { + context := "Failed to get minimum median time" + return internalRPCError(err.Error(), context) + } + + // Update work state to ensure another block template isn't + // generated until needed. + state.template = template + state.lastGenerated = time.Now() + state.lastTxUpdate = lastTxUpdate + state.prevHash = latestHash + state.minTimestamp = minTimestamp + + rpcsLog.Debugf("Generated block template (timestamp %v, "+ + "target %s, merkle root %s)", + msgBlock.Header.Timestamp, targetDifficulty, + msgBlock.Header.MerkleRoot) + + // Notify any clients that are long polling about the new + // template. + state.notifyLongPollers(latestHash, lastTxUpdate) + } else { + // At this point, there is a saved block template and another + // request for a template was made, but either the available + // transactions haven't change or it hasn't been long enough to + // trigger a new block template to be generated. So, update the + // existing block template. + + // When the caller requires a full coinbase as opposed to only + // the pertinent details needed to create their own coinbase, + // add a payment address to the output of the coinbase of the + // template if it doesn't already have one. Since this requires + // mining addresses to be specified via the config, an error is + // returned if none have been specified. + if !useCoinbaseValue && !template.ValidPayAddress { + // Choose a payment address at random. + payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] + + // Update the block coinbase output of the template to + // pay to the randomly selected payment address. + pkScript, err := txscript.PayToAddrScript(payToAddr) + if err != nil { + context := "Failed to create pay-to-addr script" + return internalRPCError(err.Error(), context) + } + template.Block.Transactions[0].TxOut[0].PkScript = pkScript + template.ValidPayAddress = true + + // Update the merkle root. + block := btcutil.NewBlock(template.Block) + merkles := blockchain.BuildMerkleTreeStore(block.Transactions()) + template.Block.Header.MerkleRoot = *merkles[len(merkles)-1] + } + + // Set locals for convenience. + msgBlock = template.Block + targetDifficulty = fmt.Sprintf("%064x", + blockchain.CompactToBig(msgBlock.Header.Bits)) + + // Update the time of the block template to the current time + // while accounting for the median time of the past several + // blocks per the chain consensus rules. + UpdateBlockTime(msgBlock, s.server.blockManager) + msgBlock.Header.Nonce = 0 + + rpcsLog.Debugf("Updated block template (timestamp %v, "+ + "target %s)", msgBlock.Header.Timestamp, + targetDifficulty) + } + + return nil +} + +// blockTemplateResult returns the current block template associated with the +// state as a btcjson.GetBlockTemplateResult that is ready to be encoded to JSON +// and returned to the caller. +// +// This function MUST be called with the state locked. +func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld *bool) (*btcjson.GetBlockTemplateResult, error) { + // Ensure the timestamps are still in valid range for the template. + // This should really only ever happen if the local clock is changed + // after the template is generated, but it's important to avoid serving + // invalid block templates. + template := state.template + msgBlock := template.Block + header := &msgBlock.Header + adjustedTime := state.timeSource.AdjustedTime() + maxTime := adjustedTime.Add(time.Second * blockchain.MaxTimeOffsetSeconds) + if header.Timestamp.After(maxTime) { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCOutOfRange, + Message: fmt.Sprintf("The template time is after the "+ + "maximum allowed time for a block - template "+ + "time %v, maximum time %v", adjustedTime, + maxTime), + } + } + + // Convert each transaction in the block template to a template result + // transaction. The result does not include the coinbase, so notice + // the adjustments to the various lengths and indices. + numTx := len(msgBlock.Transactions) + transactions := make([]btcjson.GetBlockTemplateResultTx, 0, numTx-1) + txIndex := make(map[wire.ShaHash]int64, numTx) + for i, tx := range msgBlock.Transactions { + txHash := tx.TxSha() + txIndex[txHash] = int64(i) + + // Skip the coinbase transaction. + if i == 0 { + continue + } + + // Create an array of 1-based indices to transactions that come + // before this one in the transactions list which this one + // depends on. This is necessary since the created block must + // ensure proper ordering of the dependencies. A map is used + // before creating the final array to prevent duplicate entries + // when multiple inputs reference the same transaction. + dependsMap := make(map[int64]struct{}) + for _, txIn := range tx.TxIn { + if idx, ok := txIndex[txIn.PreviousOutPoint.Hash]; ok { + dependsMap[idx] = struct{}{} + } + } + depends := make([]int64, 0, len(dependsMap)) + for idx := range dependsMap { + depends = append(depends, idx) + } + + // Serialize the transaction for later conversion to hex. + txBuf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(txBuf); err != nil { + context := "Failed to serialize transaction" + return nil, internalRPCError(err.Error(), context) + } + + resultTx := btcjson.GetBlockTemplateResultTx{ + Data: hex.EncodeToString(txBuf.Bytes()), + Hash: txHash.String(), + Depends: depends, + Fee: template.Fees[i], + SigOps: template.SigOpCounts[i], + } + transactions = append(transactions, resultTx) + } + + // Generate the block template reply. Note that following mutations are + // implied by the included or omission of fields: + // Including MinTime -> time/decrement + // Omitting CoinbaseTxn -> coinbase, generation + targetDifficulty := fmt.Sprintf("%064x", blockchain.CompactToBig(header.Bits)) + templateID := encodeTemplateID(state.prevHash, state.lastGenerated) + reply := btcjson.GetBlockTemplateResult{ + Bits: strconv.FormatInt(int64(header.Bits), 16), + CurTime: header.Timestamp.Unix(), + Height: int64(template.Height), + PreviousHash: header.PrevBlock.String(), + SigOpLimit: blockchain.MaxSigOpsPerBlock, + SizeLimit: wire.MaxBlockPayload, + Transactions: transactions, + Version: header.Version, + LongPollID: templateID, + SubmitOld: submitOld, + Target: targetDifficulty, + MinTime: state.minTimestamp.Unix(), + MaxTime: maxTime.Unix(), + Mutable: gbtMutableFields, + NonceRange: gbtNonceRange, + Capabilities: gbtCapabilities, + } + if useCoinbaseValue { + reply.CoinbaseAux = gbtCoinbaseAux + reply.CoinbaseValue = &msgBlock.Transactions[0].TxOut[0].Value + } else { + // Ensure the template has a valid payment address associated + // with it when a full coinbase is requested. + if !template.ValidPayAddress { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInternal.Code, + Message: "A coinbase transaction has been " + + "requested, but the server has not " + + "been configured with any payment " + + "addresses via --miningaddr", + } + } + + // Serialize the transaction for conversion to hex. + tx := msgBlock.Transactions[0] + txBuf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(txBuf); err != nil { + context := "Failed to serialize transaction" + return nil, internalRPCError(err.Error(), context) + } + + resultTx := btcjson.GetBlockTemplateResultTx{ + Data: hex.EncodeToString(txBuf.Bytes()), + Hash: tx.TxSha().String(), + Depends: []int64{}, + Fee: template.Fees[0], + SigOps: template.SigOpCounts[0], + } + + reply.CoinbaseTxn = &resultTx + } + + return &reply, nil +} + +// handleGetBlockTemplateLongPoll is a helper for handleGetBlockTemplateRequest +// which deals with handling long polling for block templates. When a caller +// sends a request with a long poll ID that was previously returned, a response +// is not sent until the caller should stop working on the previous block +// template in favor of the new one. In particular, this is the case when the +// old block template is no longer valid due to a solution already being found +// and added to the block chain, or new transactions have shown up and some time +// has passed without finding a solution. +// +// See https://en.bitcoin.it/wiki/BIP_0022 for more details. +func handleGetBlockTemplateLongPoll(s *rpcServer, longPollID string, useCoinbaseValue bool, closeChan <-chan struct{}) (interface{}, error) { + state := s.gbtWorkState + state.Lock() + // The state unlock is intentionally not deferred here since it needs to + // be manually unlocked before waiting for a notification about block + // template changes. + + if err := state.updateBlockTemplate(s, useCoinbaseValue); err != nil { + state.Unlock() + return nil, err + } + + // Just return the current block template if the long poll ID provided by + // the caller is invalid. + prevHash, lastGenerated, err := decodeTemplateID(longPollID) + if err != nil { + result, err := state.blockTemplateResult(useCoinbaseValue, nil) + if err != nil { + state.Unlock() + return nil, err + } + + state.Unlock() + return result, nil + } + + // Return the block template now if the specific block template + // identified by the long poll ID no longer matches the current block + // template as this means the provided template is stale. + prevTemplateHash := &state.template.Block.Header.PrevBlock + if !prevHash.IsEqual(prevTemplateHash) || + lastGenerated != state.lastGenerated.Unix() { + + // Include whether or not it is valid to submit work against the + // old block template depending on whether or not a solution has + // already been found and added to the block chain. + submitOld := prevHash.IsEqual(prevTemplateHash) + result, err := state.blockTemplateResult(useCoinbaseValue, + &submitOld) + if err != nil { + state.Unlock() + return nil, err + } + + state.Unlock() + return result, nil + } + + // Register the previous hash and last generated time for notifications + // Get a channel that will be notified when the template associated with + // the provided ID is stale and a new block template should be returned to + // the caller. + longPollChan := state.templateUpdateChan(prevHash, lastGenerated) + state.Unlock() + + select { + // When the client closes before it's time to send a reply, just return + // now so the goroutine doesn't hang around. + case <-closeChan: + return nil, ErrClientQuit + + // Wait until signal received to send the reply. + case <-longPollChan: + // Fallthrough + } + + // Get the lastest block template + state.Lock() + defer state.Unlock() + + if err := state.updateBlockTemplate(s, useCoinbaseValue); err != nil { + return nil, err + } + + // Include whether or not it is valid to submit work against the old + // block template depending on whether or not a solution has already + // been found and added to the block chain. + submitOld := prevHash.IsEqual(&state.template.Block.Header.PrevBlock) + result, err := state.blockTemplateResult(useCoinbaseValue, &submitOld) + if err != nil { + return nil, err + } + + return result, nil +} + +// handleGetBlockTemplateRequest is a helper for handleGetBlockTemplate which +// deals with generating and returning block templates to the caller. It +// handles both long poll requests as specified by BIP 0022 as well as regular +// requests. In addition, it detects the capabilities reported by the caller +// in regards to whether or not it supports creating its own coinbase (the +// coinbasetxn and coinbasevalue capabilities) and modifies the returned block +// template accordingly. +func handleGetBlockTemplateRequest(s *rpcServer, request *btcjson.TemplateRequest, closeChan <-chan struct{}) (interface{}, error) { + // Extract the relevant passed capabilities and restrict the result to + // either a coinbase value or a coinbase transaction object depending on + // the request. Default to only providing a coinbase value. + useCoinbaseValue := true + if request != nil { + var hasCoinbaseValue, hasCoinbaseTxn bool + for _, capability := range request.Capabilities { + switch capability { + case "coinbasetxn": + hasCoinbaseTxn = true + case "coinbasevalue": + hasCoinbaseValue = true + } + } + + if hasCoinbaseTxn && !hasCoinbaseValue { + useCoinbaseValue = false + } + } + + // When a coinbase transaction has been requested, respond with an error + // if there are no addresses to pay the created block template to. + if !useCoinbaseValue && len(cfg.miningAddrs) == 0 { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInternal.Code, + Message: "A coinbase transaction has been requested, " + + "but the server has not been configured with " + + "any payment addresses via --miningaddr", + } + } + + // Return an error if there are no peers connected since there is no + // way to relay a found block or receive transactions to work on. + // However, allow this state when running in the regression test or + // simulation test mode. + if !(cfg.RegressionTest || cfg.SimNet) && s.server.ConnectedCount() == 0 { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCClientNotConnected, + Message: "Bitcoin is not connected", + } + } + + // No point in generating or accepting work before the chain is synced. + _, currentHeight := s.server.blockManager.chainState.Best() + if currentHeight != 0 && !s.server.blockManager.IsCurrent() { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCClientInInitialDownload, + Message: "Bitcoin is downloading blocks...", + } + } + + // When a long poll ID was provided, this is a long poll request by the + // client to be notified when block template referenced by the ID should + // be replaced with a new one. + if request != nil && request.LongPollID != "" { + return handleGetBlockTemplateLongPoll(s, request.LongPollID, + useCoinbaseValue, closeChan) + } + + // Protect concurrent access when updating block templates. + state := s.gbtWorkState + state.Lock() + defer state.Unlock() + + // Get and return a block template. A new block template will be + // generated when the current best block has changed or the transactions + // in the memory pool have been updated and it has been at least five + // seconds since the last template was generated. Otherwise, the + // timestamp for the existing block template is updated (and possibly + // the difficulty on testnet per the consesus rules). + if err := state.updateBlockTemplate(s, useCoinbaseValue); err != nil { + return nil, err + } + return state.blockTemplateResult(useCoinbaseValue, nil) +} + +// chainErrToGBTErrString converts an error returned from btcchain to a string +// which matches the reasons and format described in BIP0022 for rejection +// reasons. +func chainErrToGBTErrString(err error) string { + // When the passed error is not a RuleError, just return a generic + // rejected string with the error text. + ruleErr, ok := err.(blockchain.RuleError) + if !ok { + return "rejected: " + err.Error() + } + + switch ruleErr.ErrorCode { + case blockchain.ErrDuplicateBlock: + return "duplicate" + case blockchain.ErrBlockTooBig: + return "bad-block-size" + case blockchain.ErrBlockVersionTooOld: + return "bad-version" + case blockchain.ErrInvalidTime: + return "bad-time" + case blockchain.ErrTimeTooOld: + return "time-too-old" + case blockchain.ErrTimeTooNew: + return "time-too-new" + case blockchain.ErrDifficultyTooLow: + return "bad-diffbits" + case blockchain.ErrUnexpectedDifficulty: + return "bad-diffbits" + case blockchain.ErrHighHash: + return "high-hash" + case blockchain.ErrBadMerkleRoot: + return "bad-txnmrklroot" + case blockchain.ErrBadCheckpoint: + return "bad-checkpoint" + case blockchain.ErrForkTooOld: + return "fork-too-old" + case blockchain.ErrCheckpointTimeTooOld: + return "checkpoint-time-too-old" + case blockchain.ErrNoTransactions: + return "bad-txns-none" + case blockchain.ErrTooManyTransactions: + return "bad-txns-toomany" + case blockchain.ErrNoTxInputs: + return "bad-txns-noinputs" + case blockchain.ErrNoTxOutputs: + return "bad-txns-nooutputs" + case blockchain.ErrTxTooBig: + return "bad-txns-size" + case blockchain.ErrBadTxOutValue: + return "bad-txns-outputvalue" + case blockchain.ErrDuplicateTxInputs: + return "bad-txns-dupinputs" + case blockchain.ErrBadTxInput: + return "bad-txns-badinput" + case blockchain.ErrMissingTx: + return "bad-txns-missinginput" + case blockchain.ErrUnfinalizedTx: + return "bad-txns-unfinalizedtx" + case blockchain.ErrDuplicateTx: + return "bad-txns-duplicate" + case blockchain.ErrOverwriteTx: + return "bad-txns-overwrite" + case blockchain.ErrImmatureSpend: + return "bad-txns-maturity" + case blockchain.ErrDoubleSpend: + return "bad-txns-dblspend" + case blockchain.ErrSpendTooHigh: + return "bad-txns-highspend" + case blockchain.ErrBadFees: + return "bad-txns-fees" + case blockchain.ErrTooManySigOps: + return "high-sigops" + case blockchain.ErrFirstTxNotCoinbase: + return "bad-txns-nocoinbase" + case blockchain.ErrMultipleCoinbases: + return "bad-txns-multicoinbase" + case blockchain.ErrBadCoinbaseScriptLen: + return "bad-cb-length" + case blockchain.ErrBadCoinbaseValue: + return "bad-cb-value" + case blockchain.ErrMissingCoinbaseHeight: + return "bad-cb-height" + case blockchain.ErrBadCoinbaseHeight: + return "bad-cb-height" + case blockchain.ErrScriptMalformed: + return "bad-script-malformed" + case blockchain.ErrScriptValidation: + return "bad-script-validate" + } + + return "rejected: " + err.Error() +} + +// handleGetBlockTemplateProposal is a helper for handleGetBlockTemplate which +// deals with block proposals. +// +// See https://en.bitcoin.it/wiki/BIP_0023 for more details. +func handleGetBlockTemplateProposal(s *rpcServer, request *btcjson.TemplateRequest) (interface{}, error) { + hexData := request.Data + if hexData == "" { + return false, &btcjson.RPCError{ + Code: btcjson.ErrRPCType, + Message: fmt.Sprintf("Data must contain the " + + "hex-encoded serialized block that is being " + + "proposed"), + } + } + + // Ensure the provided data is sane and deserialize the proposed block. + if len(hexData)%2 != 0 { + hexData = "0" + hexData + } + dataBytes, err := hex.DecodeString(hexData) + if err != nil { + return false, &btcjson.RPCError{ + Code: btcjson.ErrRPCDeserialization, + Message: fmt.Sprintf("Data must be "+ + "hexadecimal string (not %q)", hexData), + } + } + var msgBlock wire.MsgBlock + if err := msgBlock.Deserialize(bytes.NewReader(dataBytes)); err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCDeserialization, + Message: "Block decode failed: " + err.Error(), + } + } + block := btcutil.NewBlock(&msgBlock) + + // Ensure the block is building from the expected previous block. + expectedPrevHash, _ := s.server.blockManager.chainState.Best() + prevHash := &block.MsgBlock().Header.PrevBlock + if expectedPrevHash == nil || !expectedPrevHash.IsEqual(prevHash) { + return "bad-prevblk", nil + } + + flags := blockchain.BFDryRun | blockchain.BFNoPoWCheck + isOrphan, err := s.server.blockManager.ProcessBlock(block, flags) + if err != nil { + if _, ok := err.(blockchain.RuleError); !ok { + err := rpcsLog.Errorf("Failed to process block "+ + "proposal: %v", err) + return nil, &btcjson.RPCError{ + Code: -25, // TODO: ErrRpcVerify + Message: err.Error(), + } + } + + rpcsLog.Infof("Rejected block proposal: %v", err) + return chainErrToGBTErrString(err), nil + } + if isOrphan { + return "orphan", nil + } + + return nil, nil +} + +// handleGetBlockTemplate implements the getblocktemplate command. +// +// See https://en.bitcoin.it/wiki/BIP_0022 and +// https://en.bitcoin.it/wiki/BIP_0023 for more details. +func handleGetBlockTemplate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.GetBlockTemplateCmd) + request := c.Request + + // Set the default mode and override it if supplied. + mode := "template" + if request != nil && request.Mode != "" { + mode = request.Mode + } + + switch mode { + case "template": + return handleGetBlockTemplateRequest(s, request, closeChan) + case "proposal": + return handleGetBlockTemplateProposal(s, request) + } + + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: "Invalid mode", + } +} + +// handleGetConnectionCount implements the getconnectioncount command. +func handleGetConnectionCount(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + return s.server.ConnectedCount(), nil +} + +// handleGetCurrentNet implements the getcurrentnet command. +func handleGetCurrentNet(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + return s.server.chainParams.Net, nil +} + +// handleGetDifficulty implements the getdifficulty command. +func handleGetDifficulty(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + best := s.chain.BestSnapshot() + return getDifficultyRatio(best.Bits), nil +} + +// handleGetGenerate implements the getgenerate command. +func handleGetGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + return s.server.cpuMiner.IsMining(), nil +} + +// handleGetHashesPerSec implements the gethashespersec command. +func handleGetHashesPerSec(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + return int64(s.server.cpuMiner.HashesPerSecond()), nil +} + +// handleGetInfo implements the getinfo command. We only return the fields +// that are not related to wallet functionality. +func handleGetInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + best := s.chain.BestSnapshot() + ret := &btcjson.InfoChainResult{ + Version: int32(1000000*appMajor + 10000*appMinor + 100*appPatch), + ProtocolVersion: int32(maxProtocolVersion), + Blocks: best.Height, + TimeOffset: int64(s.server.timeSource.Offset().Seconds()), + Connections: s.server.ConnectedCount(), + Proxy: cfg.Proxy, + Difficulty: getDifficultyRatio(best.Bits), + TestNet: cfg.TestNet3, + RelayFee: cfg.minRelayTxFee.ToBTC(), + } + + return ret, nil +} + +// handleGetMempoolInfo implements the getmempoolinfo command. +func handleGetMempoolInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + mempoolTxns := s.server.txMemPool.TxDescs() + + var numBytes int64 + for _, txD := range mempoolTxns { + numBytes += int64(txD.Tx.MsgTx().SerializeSize()) + } + + ret := &btcjson.GetMempoolInfoResult{ + Size: int64(len(mempoolTxns)), + Bytes: numBytes, + } + + return ret, nil +} + +// handleGetMiningInfo implements the getmininginfo command. We only return the +// fields that are not related to wallet functionality. +func handleGetMiningInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + // Create a default getnetworkhashps command to use defaults and make + // use of the existing getnetworkhashps handler. + gnhpsCmd := btcjson.NewGetNetworkHashPSCmd(nil, nil) + networkHashesPerSecIface, err := handleGetNetworkHashPS(s, gnhpsCmd, + closeChan) + if err != nil { + return nil, err + } + networkHashesPerSec, ok := networkHashesPerSecIface.(int64) + if !ok { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInternal.Code, + Message: "networkHashesPerSec is not an int64", + } + } + + best := s.chain.BestSnapshot() + result := btcjson.GetMiningInfoResult{ + Blocks: int64(best.Height), + CurrentBlockSize: best.BlockSize, + CurrentBlockTx: best.NumTxns, + Difficulty: getDifficultyRatio(best.Bits), + Generate: s.server.cpuMiner.IsMining(), + GenProcLimit: s.server.cpuMiner.NumWorkers(), + HashesPerSec: int64(s.server.cpuMiner.HashesPerSecond()), + NetworkHashPS: networkHashesPerSec, + PooledTx: uint64(s.server.txMemPool.Count()), + TestNet: cfg.TestNet3, + } + return &result, nil +} + +// handleGetNetTotals implements the getnettotals command. +func handleGetNetTotals(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + totalBytesRecv, totalBytesSent := s.server.NetTotals() + reply := &btcjson.GetNetTotalsResult{ + TotalBytesRecv: totalBytesRecv, + TotalBytesSent: totalBytesSent, + TimeMillis: time.Now().UTC().UnixNano() / int64(time.Millisecond), + } + return reply, nil +} + +// handleGetNetworkHashPS implements the getnetworkhashps command. +func handleGetNetworkHashPS(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + // Note: All valid error return paths should return an int64. + // Literal zeros are inferred as int, and won't coerce to int64 + // because the return value is an interface{}. + + c := cmd.(*btcjson.GetNetworkHashPSCmd) + + // When the passed height is too high or zero, just return 0 now + // since we can't reasonably calculate the number of network hashes + // per second from invalid values. When it's negative, use the current + // best block height. + best := s.chain.BestSnapshot() + endHeight := int32(-1) + if c.Height != nil { + endHeight = int32(*c.Height) + } + if endHeight > best.Height || endHeight == 0 { + return int64(0), nil + } + if endHeight < 0 { + endHeight = best.Height + } + + // Calculate the starting block height based on the passed number of + // blocks. When the passed value is negative, use the last block the + // difficulty changed as the starting height. Also make sure the + // starting height is not before the beginning of the chain. + numBlocks := int32(120) + if c.Blocks != nil { + numBlocks = int32(*c.Blocks) + } + var startHeight int32 + if numBlocks <= 0 { + startHeight = endHeight - ((endHeight % blockchain.BlocksPerRetarget) + 1) + } else { + startHeight = endHeight - numBlocks + } + if startHeight < 0 { + startHeight = 0 + } + rpcsLog.Debugf("Calculating network hashes per second from %d to %d", + startHeight, endHeight) + + // Find the min and max block timestamps as well as calculate the total + // amount of work that happened between the start and end blocks. + var minTimestamp, maxTimestamp time.Time + totalWork := big.NewInt(0) + for curHeight := startHeight; curHeight <= endHeight; curHeight++ { + hash, err := s.chain.BlockHashByHeight(curHeight) + if err != nil { + context := "Failed to fetch block hash" + return nil, internalRPCError(err.Error(), context) + } + + // Load the raw header bytes. + var headerBytes []byte + err = s.server.db.View(func(dbTx database.Tx) error { + var err error + headerBytes, err = dbTx.FetchBlockHeader(hash) + return err + }) + if err != nil { + context := "Failed to fetch block header" + return nil, internalRPCError(err.Error(), context) + } + + // Deserialize the header. + var header wire.BlockHeader + err = header.Deserialize(bytes.NewReader(headerBytes)) + if err != nil { + context := "Failed to deserialize block header" + return nil, internalRPCError(err.Error(), context) + } + + if curHeight == startHeight { + minTimestamp = header.Timestamp + maxTimestamp = minTimestamp + } else { + totalWork.Add(totalWork, blockchain.CalcWork(header.Bits)) + + if minTimestamp.After(header.Timestamp) { + minTimestamp = header.Timestamp + } + if maxTimestamp.Before(header.Timestamp) { + maxTimestamp = header.Timestamp + } + } + } + + // Calculate the difference in seconds between the min and max block + // timestamps and avoid division by zero in the case where there is no + // time difference. + timeDiff := int64(maxTimestamp.Sub(minTimestamp) / time.Second) + if timeDiff == 0 { + return int64(0), nil + } + + hashesPerSec := new(big.Int).Div(totalWork, big.NewInt(timeDiff)) + return hashesPerSec.Int64(), nil +} + +// handleGetPeerInfo implements the getpeerinfo command. +func handleGetPeerInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + peers := s.server.Peers() + syncPeer := s.server.blockManager.SyncPeer() + infos := make([]*btcjson.GetPeerInfoResult, 0, len(peers)) + for _, p := range peers { + statsSnap := p.StatsSnapshot() + info := &btcjson.GetPeerInfoResult{ + ID: statsSnap.ID, + Addr: statsSnap.Addr, + Services: fmt.Sprintf("%08d", uint64(statsSnap.Services)), + LastSend: statsSnap.LastSend.Unix(), + LastRecv: statsSnap.LastRecv.Unix(), + BytesSent: statsSnap.BytesSent, + BytesRecv: statsSnap.BytesRecv, + ConnTime: statsSnap.ConnTime.Unix(), + PingTime: float64(statsSnap.LastPingMicros), + TimeOffset: statsSnap.TimeOffset, + Version: statsSnap.Version, + SubVer: statsSnap.UserAgent, + Inbound: statsSnap.Inbound, + StartingHeight: statsSnap.StartingHeight, + CurrentHeight: statsSnap.LastBlock, + BanScore: int32(p.banScore.Int()), + SyncNode: p == syncPeer, + } + if p.LastPingNonce() != 0 { + wait := float64(time.Now().Sub(statsSnap.LastPingTime).Nanoseconds()) + // We actually want microseconds. + info.PingWait = wait / 1000 + } + infos = append(infos, info) + } + return infos, nil +} + +// handleGetRawMempool implements the getrawmempool command. +func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.GetRawMempoolCmd) + mp := s.server.txMemPool + descs := mp.TxDescs() + + if c.Verbose != nil && *c.Verbose { + result := make(map[string]*btcjson.GetRawMempoolVerboseResult, + len(descs)) + + best := s.chain.BestSnapshot() + + mp.RLock() + defer mp.RUnlock() + for _, desc := range descs { + // Calculate the current priority based on the inputs to + // the transaction. Use zero if one or more of the + // input transactions can't be found for some reason. + tx := desc.Tx + var currentPriority float64 + utxos, err := mp.fetchInputUtxos(tx) + if err == nil { + currentPriority = calcPriority(tx.MsgTx(), + utxos, best.Height+1) + } + + mpd := &btcjson.GetRawMempoolVerboseResult{ + Size: int32(tx.MsgTx().SerializeSize()), + Fee: btcutil.Amount(desc.Fee).ToBTC(), + Time: desc.Added.Unix(), + Height: int64(desc.Height), + StartingPriority: desc.StartingPriority, + CurrentPriority: currentPriority, + Depends: make([]string, 0), + } + for _, txIn := range tx.MsgTx().TxIn { + hash := &txIn.PreviousOutPoint.Hash + if s.server.txMemPool.haveTransaction(hash) { + mpd.Depends = append(mpd.Depends, + hash.String()) + } + } + + result[tx.Sha().String()] = mpd + } + + return result, nil + } + + // The response is simply an array of the transaction hashes if the + // verbose flag is not set. + hashStrings := make([]string, len(descs)) + for i := range hashStrings { + hashStrings[i] = descs[i].Tx.Sha().String() + } + + return hashStrings, nil +} + +// handleGetRawTransaction implements the getrawtransaction command. +func handleGetRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.GetRawTransactionCmd) + + // Convert the provided transaction hash hex to a ShaHash. + txHash, err := wire.NewShaHashFromStr(c.Txid) + if err != nil { + return nil, rpcDecodeHexError(c.Txid) + } + + verbose := false + if c.Verbose != nil { + verbose = *c.Verbose != 0 + } + + // Try to fetch the transaction from the memory pool and if that fails, + // try the block database. + var mtx *wire.MsgTx + var blkHash *wire.ShaHash + var blkHeight int32 + tx, err := s.server.txMemPool.FetchTransaction(txHash) + if err != nil { + txIndex := s.server.txIndex + if txIndex == nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCNoTxInfo, + Message: "The transaction index must be " + + "enabled to query the blockchain " + + "(specify --txindex)", + } + } + + // Look up the location of the transaction. + blockRegion, err := txIndex.TxBlockRegion(txHash) + if err != nil { + context := "Failed to retrieve transaction location" + return nil, internalRPCError(err.Error(), context) + } + if blockRegion == nil { + return nil, rpcNoTxInfoError(txHash) + } + + // Load the raw transaction bytes from the database. + var txBytes []byte + err = s.server.db.View(func(dbTx database.Tx) error { + var err error + txBytes, err = dbTx.FetchBlockRegion(blockRegion) + return err + }) + if err != nil { + return nil, rpcNoTxInfoError(txHash) + } + + // When the verbose flag isn't set, simply return the serialized + // transaction as a hex-encoded string. This is done here to + // avoid deserializing it only to reserialize it again later. + if !verbose { + return hex.EncodeToString(txBytes), nil + } + + // Grab the block height. + blkHash = blockRegion.Hash + blkHeight, err = s.chain.BlockHeightByHash(blkHash) + if err != nil { + context := "Failed to retrieve block height" + return nil, internalRPCError(err.Error(), context) + } + + // Deserialize the transaction + var msgTx wire.MsgTx + err = msgTx.Deserialize(bytes.NewReader(txBytes)) + if err != nil { + context := "Failed to deserialize transaction" + return nil, internalRPCError(err.Error(), context) + } + mtx = &msgTx + } else { + // When the verbose flag isn't set, simply return the + // network-serialized transaction as a hex-encoded string. + if !verbose { + // Note that this is intentionally not directly + // returning because the first return value is a + // string and it would result in returning an empty + // string to the client instead of nothing (nil) in the + // case of an error. + mtxHex, err := messageToHex(tx.MsgTx()) + if err != nil { + return nil, err + } + return mtxHex, nil + } + + mtx = tx.MsgTx() + } + + // The verbose flag is set, so generate the JSON object and return it. + var blkHeader *wire.BlockHeader + var blkHashStr string + var chainHeight int32 + if blkHash != nil { + // Load the raw header bytes. + var headerBytes []byte + err := s.server.db.View(func(dbTx database.Tx) error { + var err error + headerBytes, err = dbTx.FetchBlockHeader(blkHash) + return err + }) + if err != nil { + context := "Failed to fetch block header" + return nil, internalRPCError(err.Error(), context) + } + + // Deserialize the header. + var header wire.BlockHeader + err = header.Deserialize(bytes.NewReader(headerBytes)) + if err != nil { + context := "Failed to deserialize block header" + return nil, internalRPCError(err.Error(), context) + } + + blkHeader = &header + blkHashStr = blkHash.String() + chainHeight = s.chain.BestSnapshot().Height + } + + rawTxn, err := createTxRawResult(s.server.chainParams, mtx, + txHash.String(), blkHeader, blkHashStr, blkHeight, chainHeight) + if err != nil { + return nil, err + } + return *rawTxn, nil +} + +// bigToLEUint256 returns the passed big integer as an unsigned 256-bit integer +// encoded as little-endian bytes. Numbers which are larger than the max +// unsigned 256-bit integer are truncated. +func bigToLEUint256(n *big.Int) [uint256Size]byte { + // Pad or truncate the big-endian big int to correct number of bytes. + nBytes := n.Bytes() + nlen := len(nBytes) + pad := 0 + start := 0 + if nlen <= uint256Size { + pad = uint256Size - nlen + } else { + start = nlen - uint256Size + } + var buf [uint256Size]byte + copy(buf[pad:], nBytes[start:]) + + // Reverse the bytes to little endian and return them. + for i := 0; i < uint256Size/2; i++ { + buf[i], buf[uint256Size-1-i] = buf[uint256Size-1-i], buf[i] + } + return buf +} + +// reverseUint32Array treats the passed bytes as a series of uint32s and +// reverses the byte order of each uint32. The passed byte slice must be a +// multiple of 4 for a correct result. The passed bytes slice is modified. +func reverseUint32Array(b []byte) { + blen := len(b) + for i := 0; i < blen; i += 4 { + b[i], b[i+3] = b[i+3], b[i] + b[i+1], b[i+2] = b[i+2], b[i+1] + } +} + +// handleGetTxOut handles gettxout commands. +func handleGetTxOut(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.GetTxOutCmd) + + // Convert the provided transaction hash hex to a ShaHash. + txHash, err := wire.NewShaHashFromStr(c.Txid) + if err != nil { + return nil, rpcDecodeHexError(c.Txid) + } + + // If requested and the tx is available in the mempool try to fetch it + // from there, otherwise attempt to fetch from the block database. + var bestBlockSha string + var confirmations int32 + var txVersion int32 + var value int64 + var pkScript []byte + var isCoinbase bool + includeMempool := true + if c.IncludeMempool != nil { + includeMempool = *c.IncludeMempool + } + // TODO: This is racy. It should attempt to fetch it directly and check + // the error. + if includeMempool && s.server.txMemPool.HaveTransaction(txHash) { + tx, err := s.server.txMemPool.FetchTransaction(txHash) + if err != nil { + return nil, rpcNoTxInfoError(txHash) + } + + mtx := tx.MsgTx() + if c.Vout > uint32(len(mtx.TxOut)-1) { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidTxVout, + Message: "Ouput index number (vout) does not " + + "exist for transaction.", + } + } + + txOut := mtx.TxOut[c.Vout] + if txOut == nil { + errStr := fmt.Sprintf("Output index: %d for txid: %s "+ + "does not exist", c.Vout, txHash) + return nil, internalRPCError(errStr, "") + } + + best := s.chain.BestSnapshot() + bestBlockSha = best.Hash.String() + confirmations = 0 + txVersion = mtx.Version + value = txOut.Value + pkScript = txOut.PkScript + isCoinbase = blockchain.IsCoinBaseTx(mtx) + } else { + entry, err := s.chain.FetchUtxoEntry(txHash) + if err != nil { + return nil, rpcNoTxInfoError(txHash) + } + + // To match the behavior of the reference client, return nil + // (JSON null) if the transaction output is spent by another + // transaction already in the main chain. Mined transactions + // that are spent by a mempool transaction are not affected by + // this. + if entry == nil || entry.IsOutputSpent(c.Vout) { + return nil, nil + } + + best := s.chain.BestSnapshot() + bestBlockSha = best.Hash.String() + confirmations = 1 + best.Height - entry.BlockHeight() + txVersion = entry.Version() + value = entry.AmountByIndex(c.Vout) + pkScript = entry.PkScriptByIndex(c.Vout) + isCoinbase = entry.IsCoinBase() + } + + // Disassemble script into single line printable format. + // The disassembled string will contain [error] inline if the script + // doesn't fully parse, so ignore the error here. + disbuf, _ := txscript.DisasmString(pkScript) + + // Get further info about the script. + // 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, _ := txscript.ExtractPkScriptAddrs(pkScript, + s.server.chainParams) + addresses := make([]string, len(addrs)) + for i, addr := range addrs { + addresses[i] = addr.EncodeAddress() + } + + txOutReply := &btcjson.GetTxOutResult{ + BestBlock: bestBlockSha, + Confirmations: int64(confirmations), + Value: btcutil.Amount(value).ToBTC(), + Version: txVersion, + ScriptPubKey: btcjson.ScriptPubKeyResult{ + Asm: disbuf, + Hex: hex.EncodeToString(pkScript), + ReqSigs: int32(reqSigs), + Type: scriptClass.String(), + Addresses: addresses, + }, + Coinbase: isCoinbase, + } + return txOutReply, nil +} + +// handleGetWorkRequest is a helper for handleGetWork which deals with +// generating and returning work to the caller. +// +// This function MUST be called with the RPC workstate locked. +func handleGetWorkRequest(s *rpcServer) (interface{}, error) { + state := s.workState + + // Generate a new block template when the current best block has + // changed or the transactions in the memory pool have been updated + // and it has been at least one minute since the last template was + // generated. + lastTxUpdate := s.server.txMemPool.LastUpdated() + latestHash, latestHeight := s.server.blockManager.chainState.Best() + msgBlock := state.msgBlock + if msgBlock == nil || state.prevHash == nil || + !state.prevHash.IsEqual(latestHash) || + (state.lastTxUpdate != lastTxUpdate && + time.Now().After(state.lastGenerated.Add(time.Minute))) { + + // Reset the extra nonce and clear all cached template + // variations if the best block changed. + if state.prevHash != nil && !state.prevHash.IsEqual(latestHash) { + state.extraNonce = 0 + state.blockInfo = make(map[wire.ShaHash]*workStateBlockInfo) + } + + // Reset the previous best hash the block template was generated + // against so any errors below cause the next invocation to try + // again. + state.prevHash = nil + + // Choose a payment address at random. + payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] + template, err := NewBlockTemplate(s.policy, s.server, payToAddr) + if err != nil { + context := "Failed to create new block template" + return nil, internalRPCError(err.Error(), context) + } + msgBlock = template.Block + + // Update work state to ensure another block template isn't + // generated until needed. + state.msgBlock = msgBlock + state.lastGenerated = time.Now() + state.lastTxUpdate = lastTxUpdate + state.prevHash = latestHash + + rpcsLog.Debugf("Generated block template (timestamp %v, extra "+ + "nonce %d, target %064x, merkle root %s, signature "+ + "script %x)", msgBlock.Header.Timestamp, + state.extraNonce, + blockchain.CompactToBig(msgBlock.Header.Bits), + msgBlock.Header.MerkleRoot, + msgBlock.Transactions[0].TxIn[0].SignatureScript) + } else { + // At this point, there is a saved block template and a new + // request for work was made, but either the available + // transactions haven't change or it hasn't been long enough to + // trigger a new block template to be generated. So, update the + // existing block template and track the variations so each + // variation can be regenerated if a caller finds an answer and + // makes a submission against it. + + // Update the time of the block template to the current time + // while accounting for the median time of the past several + // blocks per the chain consensus rules. + UpdateBlockTime(msgBlock, s.server.blockManager) + + // Increment the extra nonce and update the block template + // with the new value by regenerating the coinbase script and + // setting the merkle root to the new value. + state.extraNonce++ + err := UpdateExtraNonce(msgBlock, latestHeight+1, state.extraNonce) + if err != nil { + errStr := fmt.Sprintf("Failed to update extra nonce: "+ + "%v", err) + return nil, internalRPCError(errStr, "") + } + + rpcsLog.Debugf("Updated block template (timestamp %v, extra "+ + "nonce %d, target %064x, merkle root %s, signature "+ + "script %x)", msgBlock.Header.Timestamp, + state.extraNonce, + blockchain.CompactToBig(msgBlock.Header.Bits), + msgBlock.Header.MerkleRoot, + msgBlock.Transactions[0].TxIn[0].SignatureScript) + } + + // In order to efficiently store the variations of block templates that + // have been provided to callers, save a pointer to the block as well as + // the modified signature script keyed by the merkle root. This + // information, along with the data that is included in a work + // submission, is used to rebuild the block before checking the + // submitted solution. + coinbaseTx := msgBlock.Transactions[0] + state.blockInfo[msgBlock.Header.MerkleRoot] = &workStateBlockInfo{ + msgBlock: msgBlock, + signatureScript: coinbaseTx.TxIn[0].SignatureScript, + } + + // Serialize the block header into a buffer large enough to hold the + // the block header and the internal sha256 padding that is added and + // retuned as part of the data below. + data := make([]byte, 0, getworkDataLen) + buf := bytes.NewBuffer(data) + err := msgBlock.Header.Serialize(buf) + if err != nil { + errStr := fmt.Sprintf("Failed to serialize data: %v", err) + return nil, internalRPCError(errStr, "") + } + + // Calculate the midstate for the block header. The midstate here is + // the internal state of the sha256 algorithm for the first chunk of the + // block header (sha256 operates on 64-byte chunks) which is before the + // nonce. This allows sophisticated callers to avoid hashing the first + // chunk over and over while iterating the nonce range. + data = data[:buf.Len()] + midstate := fastsha256.MidState256(data) + + // Expand the data slice to include the full data buffer and apply the + // internal sha256 padding which consists of a single 1 bit followed + // by enough zeros to pad the message out to 56 bytes followed by the + // length of the message in bits encoded as a big-endian uint64 + // (8 bytes). Thus, the resulting length is a multiple of the sha256 + // block size (64 bytes). This makes the data ready for sophisticated + // caller to make use of only the second chunk along with the midstate + // for the first chunk. + data = data[:getworkDataLen] + data[wire.MaxBlockHeaderPayload] = 0x80 + binary.BigEndian.PutUint64(data[len(data)-8:], + wire.MaxBlockHeaderPayload*8) + + // Create the hash1 field which is a zero hash along with the internal + // sha256 padding as described above. This field is really quite + // useless, but it is required for compatibility with the reference + // implementation. + var hash1 [hash1Len]byte + hash1[wire.HashSize] = 0x80 + binary.BigEndian.PutUint64(hash1[len(hash1)-8:], wire.HashSize*8) + + // The final result reverses each of the fields to little endian. + // In particular, the data, hash1, and midstate fields are treated as + // arrays of uint32s (per the internal sha256 hashing state) which are + // in big endian, and thus each 4 bytes is byte swapped. The target is + // also in big endian, but it is treated as a uint256 and byte swapped + // to little endian accordingly. + // + // The fact the fields are reversed in this way is rather odd and likey + // an artifact of some legacy internal state in the reference + // implementation, but it is required for compatibility. + reverseUint32Array(data) + reverseUint32Array(hash1[:]) + reverseUint32Array(midstate[:]) + target := bigToLEUint256(blockchain.CompactToBig(msgBlock.Header.Bits)) + reply := &btcjson.GetWorkResult{ + Data: hex.EncodeToString(data), + Hash1: hex.EncodeToString(hash1[:]), + Midstate: hex.EncodeToString(midstate[:]), + Target: hex.EncodeToString(target[:]), + } + return reply, nil +} + +// handleGetWorkSubmission is a helper for handleGetWork which deals with +// the calling submitting work to be verified and processed. +// +// This function MUST be called with the RPC workstate locked. +func handleGetWorkSubmission(s *rpcServer, hexData string) (interface{}, error) { + // Ensure the provided data is sane. + if len(hexData)%2 != 0 { + hexData = "0" + hexData + } + data, err := hex.DecodeString(hexData) + if err != nil { + return false, rpcDecodeHexError(hexData) + } + if len(data) != getworkDataLen { + return false, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: fmt.Sprintf("Argument must be "+ + "%d bytes (not %d)", getworkDataLen, + len(data)), + } + } + + // Reverse the data as if it were an array of 32-bit unsigned integers. + // The fact the getwork request and submission data is reversed in this + // way is rather odd and likey an artifact of some legacy internal state + // in the reference implementation, but it is required for + // compatibility. + reverseUint32Array(data) + + // Deserialize the block header from the data. + var submittedHeader wire.BlockHeader + bhBuf := bytes.NewReader(data[0:wire.MaxBlockHeaderPayload]) + err = submittedHeader.Deserialize(bhBuf) + if err != nil { + return false, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: fmt.Sprintf("Argument does not "+ + "contain a valid block header: %v", err), + } + } + + // Look up the full block for the provided data based on the + // merkle root. Return false to indicate the solve failed if + // it's not available. + state := s.workState + blockInfo, ok := state.blockInfo[submittedHeader.MerkleRoot] + if !ok { + rpcsLog.Debugf("Block submitted via getwork has no matching "+ + "template for merkle root %s", + submittedHeader.MerkleRoot) + return false, nil + } + + // Reconstruct the block using the submitted header stored block info. + msgBlock := blockInfo.msgBlock + block := btcutil.NewBlock(msgBlock) + msgBlock.Header.Timestamp = submittedHeader.Timestamp + msgBlock.Header.Nonce = submittedHeader.Nonce + msgBlock.Transactions[0].TxIn[0].SignatureScript = blockInfo.signatureScript + merkles := blockchain.BuildMerkleTreeStore(block.Transactions()) + msgBlock.Header.MerkleRoot = *merkles[len(merkles)-1] + + // Ensure the submitted block hash is less than the target difficulty. + err = blockchain.CheckProofOfWork(block, activeNetParams.PowLimit) + if err != nil { + // Anything other than a rule violation is an unexpected error, + // so return that error as an internal error. + if _, ok := err.(blockchain.RuleError); !ok { + return false, internalRPCError("Unexpected error "+ + "while checking proof of work: "+err.Error(), + "") + } + + rpcsLog.Debugf("Block submitted via getwork does not meet "+ + "the required proof of work: %v", err) + return false, nil + } + + latestHash, _ := s.server.blockManager.chainState.Best() + if !msgBlock.Header.PrevBlock.IsEqual(latestHash) { + rpcsLog.Debugf("Block submitted via getwork with previous "+ + "block %s is stale", msgBlock.Header.PrevBlock) + return false, nil + } + + // Process this block using the same rules as blocks coming from other + // nodes. This will in turn relay it to the network like normal. + isOrphan, err := s.server.blockManager.ProcessBlock(block, blockchain.BFNone) + if err != nil || isOrphan { + // Anything other than a rule violation is an unexpected error, + // so return that error as an internal error. + if _, ok := err.(blockchain.RuleError); !ok { + return false, internalRPCError("Unexpected error "+ + "while processing block: "+err.Error(), "") + } + + rpcsLog.Infof("Block submitted via getwork rejected: %v", err) + return false, nil + } + + // The block was accepted. + rpcsLog.Infof("Block submitted via getwork accepted: %s", block.Sha()) + return true, nil +} + +// handleGetWork implements the getwork command. +func handleGetWork(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.GetWorkCmd) + + // Respond with an error if there are no addresses to pay the created + // blocks to. + if len(cfg.miningAddrs) == 0 { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInternal.Code, + Message: "No payment addresses specified via --miningaddr", + } + } + + // Return an error if there are no peers connected since there is no + // way to relay a found block or receive transactions to work on. + // However, allow this state when running in the regression test or + // simulation test mode. + if !(cfg.RegressionTest || cfg.SimNet) && s.server.ConnectedCount() == 0 { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCClientNotConnected, + Message: "Bitcoin is not connected", + } + } + + // No point in generating or accepting work before the chain is synced. + _, currentHeight := s.server.blockManager.chainState.Best() + if currentHeight != 0 && !s.server.blockManager.IsCurrent() { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCClientInInitialDownload, + Message: "Bitcoin is downloading blocks...", + } + } + + // Protect concurrent access from multiple RPC invocations for work + // requests and submission. + s.workState.Lock() + defer s.workState.Unlock() + + // When the caller provides data, it is a submission of a supposedly + // solved block that needs to be checked and submitted to the network + // if valid. + if c.Data != nil && *c.Data != "" { + return handleGetWorkSubmission(s, *c.Data) + } + + // No data was provided, so the caller is requesting work. + return handleGetWorkRequest(s) +} + +// handleHelp implements the help command. +func handleHelp(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.HelpCmd) + + // Provide a usage overview of all commands when no specific command + // was specified. + var command string + if c.Command != nil { + command = *c.Command + } + if command == "" { + usage, err := s.helpCacher.rpcUsage(false) + if err != nil { + context := "Failed to generate RPC usage" + return nil, internalRPCError(err.Error(), context) + } + return usage, nil + } + + // Check that the command asked for is supported and implemented. Only + // search the main list of handlers since help should not be provided + // for commands that are unimplemented or related to wallet + // functionality. + if _, ok := rpcHandlers[command]; !ok { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: "Unknown command: " + command, + } + } + + // Get the help for the command. + help, err := s.helpCacher.rpcMethodHelp(command) + if err != nil { + context := "Failed to generate help" + return nil, internalRPCError(err.Error(), context) + } + return help, nil +} + +// handlePing implements the ping command. +func handlePing(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + // Ask server to ping \o_ + nonce, err := wire.RandomUint64() + if err != nil { + return nil, internalRPCError("Not sending ping - failed to "+ + "generate nonce: "+err.Error(), "") + } + s.server.BroadcastMessage(wire.NewMsgPing(nonce)) + + return nil, nil +} + +// retrievedTx represents a transaction that was either loaded from the +// transaction memory pool or from the database. When a transaction is loaded +// from the database, it is loaded with the raw serialized bytes while the +// mempool has the fully deserialized structure. This structure therefore will +// have one of the two fields set depending on where is was retrieved from. +// This is mainly done for efficiency to avoid extra serialization steps when +// possible. +type retrievedTx struct { + txBytes []byte + blkHash *wire.ShaHash // Only set when transaction is in a block. + tx *btcutil.Tx +} + +// fetchInputTxos fetches the outpoints from all transactions referenced by the +// inputs to the passed transaction by checking the transaction mempool first +// then the transaction index for those already mined into blocks. +func fetchInputTxos(s *rpcServer, tx *wire.MsgTx) (map[wire.OutPoint]wire.TxOut, error) { + mp := s.server.txMemPool + originOutputs := make(map[wire.OutPoint]wire.TxOut) + for txInIndex, txIn := range tx.TxIn { + // Attempt to fetch and use the referenced transaction from the + // memory pool. + origin := &txIn.PreviousOutPoint + originTx, err := mp.FetchTransaction(&origin.Hash) + if err == nil { + txOuts := originTx.MsgTx().TxOut + if origin.Index >= uint32(len(txOuts)) { + errStr := fmt.Sprintf("unable to find output "+ + "%v referenced from transaction %s:%d", + origin, tx.TxSha(), txInIndex) + return nil, internalRPCError(errStr, "") + } + + originOutputs[*origin] = *txOuts[origin.Index] + continue + } + + // Look up the location of the transaction. + blockRegion, err := s.server.txIndex.TxBlockRegion(&origin.Hash) + if err != nil { + context := "Failed to retrieve transaction location" + return nil, internalRPCError(err.Error(), context) + } + if blockRegion == nil { + return nil, rpcNoTxInfoError(&origin.Hash) + } + + // Load the raw transaction bytes from the database. + var txBytes []byte + err = s.server.db.View(func(dbTx database.Tx) error { + var err error + txBytes, err = dbTx.FetchBlockRegion(blockRegion) + return err + }) + if err != nil { + return nil, rpcNoTxInfoError(&origin.Hash) + } + + // Deserialize the transaction + var msgTx wire.MsgTx + err = msgTx.Deserialize(bytes.NewReader(txBytes)) + if err != nil { + context := "Failed to deserialize transaction" + return nil, internalRPCError(err.Error(), context) + } + + // Add the referenced output to the map. + if origin.Index >= uint32(len(msgTx.TxOut)) { + errStr := fmt.Sprintf("unable to find output %v "+ + "referenced from transaction %s:%d", origin, + tx.TxSha(), txInIndex) + return nil, internalRPCError(errStr, "") + } + originOutputs[*origin] = *msgTx.TxOut[origin.Index] + } + + return originOutputs, nil +} + +// createVinListPrevOut returns a slice of JSON objects for the inputs of the +// passed transaction. +func createVinListPrevOut(s *rpcServer, mtx *wire.MsgTx, chainParams *chaincfg.Params, vinExtra bool, filterAddrMap map[string]struct{}) ([]btcjson.VinPrevOut, error) { + // Coinbase transactions only have a single txin by definition. + if blockchain.IsCoinBaseTx(mtx) { + // Only include the transaction if the filter map is empty + // because a coinbase input has no addresses and so would never + // match a non-empty filter. + if len(filterAddrMap) != 0 { + return nil, nil + } + + txIn := mtx.TxIn[0] + vinList := make([]btcjson.VinPrevOut, 1) + vinList[0].Coinbase = hex.EncodeToString(txIn.SignatureScript) + vinList[0].Sequence = txIn.Sequence + return vinList, nil + } + + // Use a dynamically sized list to accomodate the address filter. + vinList := make([]btcjson.VinPrevOut, 0, len(mtx.TxIn)) + + // Lookup all of the referenced transaction outputs needed to populate + // the previous output information if requested. + var originOutputs map[wire.OutPoint]wire.TxOut + if vinExtra || len(filterAddrMap) > 0 { + var err error + originOutputs, err = fetchInputTxos(s, mtx) + if err != nil { + return nil, err + } + } + + for _, txIn := range mtx.TxIn { + // The disassembled string will contain [error] inline + // if the script doesn't fully parse, so ignore the + // error here. + disbuf, _ := txscript.DisasmString(txIn.SignatureScript) + + // Create the basic input entry without the additional optional + // previous output details which will be added later if + // requested and available. + prevOut := &txIn.PreviousOutPoint + vinEntry := btcjson.VinPrevOut{ + Txid: prevOut.Hash.String(), + Vout: prevOut.Index, + Sequence: txIn.Sequence, + ScriptSig: &btcjson.ScriptSig{ + Asm: disbuf, + Hex: hex.EncodeToString(txIn.SignatureScript), + }, + } + + // Add the entry to the list now if it already passed the filter + // since the previous output might not be available. + passesFilter := len(filterAddrMap) == 0 + if passesFilter { + vinList = append(vinList, vinEntry) + } + + // Only populate previous output information if requested and + // available. + if len(originOutputs) == 0 { + continue + } + originTxOut, ok := originOutputs[*prevOut] + if !ok { + continue + } + + // Ignore the error here since an error means the script + // couldn't parse and there is no additional information about + // it anyways. + _, addrs, _, _ := txscript.ExtractPkScriptAddrs( + originTxOut.PkScript, chainParams) + + // Encode the addresses while checking if the address passes the + // filter when needed. + encodedAddrs := make([]string, len(addrs)) + for j, addr := range addrs { + encodedAddr := addr.EncodeAddress() + encodedAddrs[j] = encodedAddr + + // No need to check the map again if the filter already + // passes. + if passesFilter { + continue + } + if _, exists := filterAddrMap[encodedAddr]; exists { + passesFilter = true + } + } + + // Ignore the entry if it doesn't pass the filter. + if !passesFilter { + continue + } + + // Add entry to the list if it wasn't already done above. + if len(filterAddrMap) != 0 { + vinList = append(vinList, vinEntry) + } + + // Update the entry with previous output information if + // requested. + if vinExtra { + vinListEntry := &vinList[len(vinList)-1] + vinListEntry.PrevOut = &btcjson.PrevOut{ + Addresses: encodedAddrs, + Value: btcutil.Amount(originTxOut.Value).ToBTC(), + } + } + } + + return vinList, nil +} + +// fetchMempoolTxnsForAddress queries the address index for all unconfirmed +// transactions that involve the provided address. The results will be limited +// by the number to skip and the number requested. +func fetchMempoolTxnsForAddress(s *rpcServer, addr btcutil.Address, numToSkip, numRequested uint32) ([]*btcutil.Tx, uint32) { + // There are no entries to return when there are less available than the + // number being skipped. + mpTxns := s.server.addrIndex.UnconfirmedTxnsForAddress(addr) + numAvailable := uint32(len(mpTxns)) + if numToSkip > numAvailable { + return nil, numAvailable + } + + // Filter the available entries based on the number to skip and number + // requested. + rangeEnd := numToSkip + numRequested + if rangeEnd > numAvailable { + rangeEnd = numAvailable + } + return mpTxns[numToSkip:rangeEnd], numToSkip +} + +// handleSearchRawTransactions implements the searchrawtransactions command. +func handleSearchRawTransactions(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + // Respond with an error if the address index is not enabled. + addrIndex := s.server.addrIndex + if addrIndex == nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCMisc, + Message: "Address index must be enabled (--addrindex)", + } + } + + // Override the flag for including extra previous output information in + // each input if needed. + c := cmd.(*btcjson.SearchRawTransactionsCmd) + vinExtra := false + if c.VinExtra != nil { + vinExtra = *c.VinExtra != 0 + } + + // Including the extra previous output information requires the + // transaction index. Currently the address index relies on the + // transaction index, so this check is redundant, but it's better to be + // safe in case the address index is ever changed to not rely on it. + if vinExtra && s.server.txIndex == nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCMisc, + Message: "Transaction index must be enabled (--txindex)", + } + } + + // Attempt to decode the supplied address. + addr, err := btcutil.DecodeAddress(c.Address, s.server.chainParams) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidAddressOrKey, + Message: "Invalid address or key: " + err.Error(), + } + } + + // Override the default number of requested entries if needed. Also, + // just return now if the number of requested entries is zero to avoid + // extra work. + numRequested := 100 + if c.Count != nil { + numRequested = *c.Count + if numRequested < 0 { + numRequested = 1 + } + } + if numRequested == 0 { + return nil, nil + } + + // Override the default number of entries to skip if needed. + var numToSkip int + if c.Skip != nil { + numToSkip = *c.Skip + if numToSkip < 0 { + numToSkip = 0 + } + } + + // Override the reverse flag if needed. + var reverse bool + if c.Reverse != nil { + reverse = *c.Reverse + } + + // Add transactions from mempool first if client asked for reverse + // order. Otherwise, they will be added last (as needed depending on + // the requested counts). + // + // NOTE: This code doesn't sort by dependency. This might be something + // to do in the future for the client's convenience, or leave it to the + // client. + numSkipped := uint32(0) + addressTxns := make([]retrievedTx, 0, numRequested) + if reverse { + // Transactions in the mempool are not in a block header yet, + // so the block header field in the retieved transaction struct + // is left nil. + mpTxns, mpSkipped := fetchMempoolTxnsForAddress(s, addr, + uint32(numToSkip), uint32(numRequested)) + numSkipped += mpSkipped + for _, tx := range mpTxns { + addressTxns = append(addressTxns, retrievedTx{tx: tx}) + } + } + + // Fetch transactions from the database in the desired order if more are + // needed. + if len(addressTxns) < numRequested { + err = s.server.db.View(func(dbTx database.Tx) error { + regions, dbSkipped, err := addrIndex.TxRegionsForAddress( + dbTx, addr, uint32(numToSkip)-numSkipped, + uint32(numRequested-len(addressTxns)), reverse) + if err != nil { + return err + } + + // Load the raw transaction bytes from the database. + serializedTxns, err := dbTx.FetchBlockRegions(regions) + if err != nil { + return err + } + + // Add the transaction and the hash of the block it is + // contained in to the list. Note that the transaction + // is left serialized here since the caller might have + // requested non-verbose output and hence there would be + // no point in deserializing it just to reserialize it + // later. + for i, serializedTx := range serializedTxns { + addressTxns = append(addressTxns, retrievedTx{ + txBytes: serializedTx, + blkHash: regions[i].Hash, + }) + } + numSkipped += dbSkipped + + return nil + }) + if err != nil { + context := "Failed to load address index entries" + return nil, internalRPCError(err.Error(), context) + } + + } + + // Add transactions from mempool last if client did not request reverse + // order and the number of results is still under the number requested. + if !reverse && len(addressTxns) < numRequested { + // Transactions in the mempool are not in a block header yet, + // so the block header field in the retieved transaction struct + // is left nil. + mpTxns, mpSkipped := fetchMempoolTxnsForAddress(s, addr, + uint32(numToSkip)-numSkipped, uint32(numRequested- + len(addressTxns))) + numSkipped += mpSkipped + for _, tx := range mpTxns { + addressTxns = append(addressTxns, retrievedTx{tx: tx}) + } + } + + // Address has never been used if neither source yielded any results. + if len(addressTxns) == 0 { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCNoTxInfo, + Message: "No information available about address", + } + } + + // Serialize all of the transactions to hex. + hexTxns := make([]string, len(addressTxns)) + for i := range addressTxns { + // Simply encode the raw bytes to hex when the retrieved + // transaction is already in serialized form. + rtx := &addressTxns[i] + if rtx.txBytes != nil { + hexTxns[i] = hex.EncodeToString(rtx.txBytes) + continue + } + + // Serialize the transaction first and convert to hex when the + // retrieved transaction is the deserialized structure. + hexTxns[i], err = messageToHex(rtx.tx.MsgTx()) + if err != nil { + return nil, err + } + } + + // When not in verbose mode, simply return a list of serialized txns. + if c.Verbose != nil && *c.Verbose == 0 { + return hexTxns, nil + } + + // Normalize the provided filter addresses (if any) to ensure there are + // no duplicates. + filterAddrMap := make(map[string]struct{}) + if c.FilterAddrs != nil && len(*c.FilterAddrs) > 0 { + for _, addr := range *c.FilterAddrs { + filterAddrMap[addr] = struct{}{} + } + } + + // The verbose flag is set, so generate the JSON object and return it. + best := s.chain.BestSnapshot() + chainParams := s.server.chainParams + srtList := make([]btcjson.SearchRawTransactionsResult, len(addressTxns)) + for i := range addressTxns { + // The deserialized transaction is needed, so deserialize the + // retrieved transaction if it's in serialized form (which will + // be the case when it was lookup up from the database). + // Otherwise, use the existing deserialized transaction. + rtx := &addressTxns[i] + var mtx *wire.MsgTx + if rtx.tx == nil { + // Deserialize the transaction. + mtx = new(wire.MsgTx) + err := mtx.Deserialize(bytes.NewReader(rtx.txBytes)) + if err != nil { + context := "Failed to deserialize transaction" + return nil, internalRPCError(err.Error(), + context) + } + } else { + mtx = rtx.tx.MsgTx() + } + + result := &srtList[i] + result.Hex = hexTxns[i] + result.Txid = mtx.TxSha().String() + result.Vin, err = createVinListPrevOut(s, mtx, chainParams, + vinExtra, filterAddrMap) + if err != nil { + return nil, err + } + result.Vout = createVoutList(mtx, chainParams, filterAddrMap) + result.Version = mtx.Version + result.LockTime = mtx.LockTime + + // Transactions grabbed from the mempool aren't yet in a block, + // so conditionally fetch block details here. This will be + // reflected in the final JSON output (mempool won't have + // confirmations or block information). + var blkHeader *wire.BlockHeader + var blkHashStr string + var blkHeight int32 + if blkHash := rtx.blkHash; blkHash != nil { + // Load the raw header bytes from the database. + var headerBytes []byte + err := s.server.db.View(func(dbTx database.Tx) error { + var err error + headerBytes, err = dbTx.FetchBlockHeader(blkHash) + return err + }) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCBlockNotFound, + Message: "Block not found", + } + } + + // Deserialize the block header. + var header wire.BlockHeader + err = header.Deserialize(bytes.NewReader(headerBytes)) + if err != nil { + context := "Failed to deserialize block header" + return nil, internalRPCError(err.Error(), context) + } + + // Get the block height from chain. + height, err := s.chain.BlockHeightByHash(blkHash) + if err != nil { + context := "Failed to obtain block height" + return nil, internalRPCError(err.Error(), context) + } + + blkHeader = &header + blkHashStr = blkHash.String() + blkHeight = height + } + + // Add the block information to the result if there is any. + if blkHeader != nil { + // This is not a typo, they are identical in Bitcoin + // Core as well. + result.Time = blkHeader.Timestamp.Unix() + result.Blocktime = blkHeader.Timestamp.Unix() + result.BlockHash = blkHashStr + result.Confirmations = uint64(1 + best.Height - blkHeight) + } + } + + return srtList, nil +} + +// handleSendRawTransaction implements the sendrawtransaction command. +func handleSendRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.SendRawTransactionCmd) + // Deserialize and send off to tx relay + hexStr := c.HexTx + if len(hexStr)%2 != 0 { + hexStr = "0" + hexStr + } + serializedTx, err := hex.DecodeString(hexStr) + if err != nil { + return nil, rpcDecodeHexError(hexStr) + } + msgtx := wire.NewMsgTx() + err = msgtx.Deserialize(bytes.NewReader(serializedTx)) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCDeserialization, + Message: "TX decode failed: " + err.Error(), + } + } + + tx := btcutil.NewTx(msgtx) + acceptedTxs, err := s.server.txMemPool.ProcessTransaction(tx, false, false) + if err != nil { + // When the error is a rule error, it means the transaction was + // simply rejected as opposed to something actually going wrong, + // so log it as such. Otherwise, something really did go wrong, + // so log it as an actual error. In both cases, a JSON-RPC + // error is returned to the client with the deserialization + // error code (to match bitcoind behavior). + if _, ok := err.(RuleError); ok { + rpcsLog.Debugf("Rejected transaction %v: %v", tx.Sha(), + err) + } else { + rpcsLog.Errorf("Failed to process transaction %v: %v", + tx.Sha(), err) + } + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCDeserialization, + Message: "TX rejected: " + err.Error(), + } + } + + s.server.AnnounceNewTransactions(acceptedTxs) + + // Keep track of all the sendrawtransaction request txns so that they + // can be rebroadcast if they don't make their way into a block. + iv := wire.NewInvVect(wire.InvTypeTx, tx.Sha()) + s.server.AddRebroadcastInventory(iv, tx) + + return tx.Sha().String(), nil +} + +// handleSetGenerate implements the setgenerate command. +func handleSetGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.SetGenerateCmd) + + // Disable generation regardless of the provided generate flag if the + // maximum number of threads (goroutines for our purposes) is 0. + // Otherwise enable or disable it depending on the provided flag. + generate := c.Generate + genProcLimit := -1 + if c.GenProcLimit != nil { + genProcLimit = *c.GenProcLimit + } + if genProcLimit == 0 { + generate = false + } + + if !generate { + s.server.cpuMiner.Stop() + } else { + // Respond with an error if there are no addresses to pay the + // created blocks to. + if len(cfg.miningAddrs) == 0 { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInternal.Code, + Message: "No payment addresses specified " + + "via --miningaddr", + } + } + + // It's safe to call start even if it's already started. + s.server.cpuMiner.SetNumWorkers(int32(genProcLimit)) + s.server.cpuMiner.Start() + } + return nil, nil +} + +// handleStop implements the stop command. +func handleStop(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + s.server.Stop() + return "btcd stopping.", nil +} + +// handleSubmitBlock implements the submitblock command. +func handleSubmitBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.SubmitBlockCmd) + + // Deserialize the submitted block. + hexStr := c.HexBlock + if len(hexStr)%2 != 0 { + hexStr = "0" + c.HexBlock + } + serializedBlock, err := hex.DecodeString(hexStr) + if err != nil { + return nil, rpcDecodeHexError(hexStr) + } + + block, err := btcutil.NewBlockFromBytes(serializedBlock) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCDeserialization, + Message: "Block decode failed: " + err.Error(), + } + } + + _, err = s.server.blockManager.ProcessBlock(block, blockchain.BFNone) + if err != nil { + return fmt.Sprintf("rejected: %s", err.Error()), nil + } + + rpcsLog.Infof("Accepted block %s via submitblock", block.Sha()) + return nil, nil +} + +// handleValidateAddress implements the validateaddress command. +func handleValidateAddress(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.ValidateAddressCmd) + + result := btcjson.ValidateAddressChainResult{} + addr, err := btcutil.DecodeAddress(c.Address, activeNetParams.Params) + if err != nil { + // Return the default value (false) for IsValid. + return result, nil + } + + result.Address = addr.EncodeAddress() + result.IsValid = true + + return result, nil +} + +func verifyChain(s *rpcServer, level, depth int32) error { + best := s.chain.BestSnapshot() + finishHeight := best.Height - depth + if finishHeight < 0 { + finishHeight = 0 + } + rpcsLog.Infof("Verifying chain for %d blocks at level %d", + best.Height-finishHeight, level) + + for height := best.Height; height > finishHeight; height-- { + // Level 0 just looks up the block. + block, err := s.chain.BlockByHeight(height) + if err != nil { + rpcsLog.Errorf("Verify is unable to fetch block at "+ + "height %d: %v", height, err) + return err + } + + // Level 1 does basic chain sanity checks. + if level > 0 { + err := blockchain.CheckBlockSanity(block, + activeNetParams.PowLimit, s.server.timeSource) + if err != nil { + rpcsLog.Errorf("Verify is unable to validate "+ + "block at hash %v height %d: %v", + block.Sha(), height, err) + return err + } + } + } + rpcsLog.Infof("Chain verify completed successfully") + + return nil +} + +// handleVerifyChain implements the verifychain command. +func handleVerifyChain(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.VerifyChainCmd) + + var checkLevel, checkDepth int32 + if c.CheckLevel != nil { + checkLevel = *c.CheckLevel + } + if c.CheckDepth != nil { + checkDepth = *c.CheckDepth + } + + err := verifyChain(s, checkLevel, checkDepth) + return err == nil, nil +} + +// handleVerifyMessage implements the verifymessage command. +func handleVerifyMessage(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.VerifyMessageCmd) + + // Decode the provided address. + addr, err := btcutil.DecodeAddress(c.Address, activeNetParams.Params) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidAddressOrKey, + Message: "Invalid address or key: " + err.Error(), + } + } + + // Only P2PKH addresses are valid for signing. + if _, ok := addr.(*btcutil.AddressPubKeyHash); !ok { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCType, + Message: "Address is not a pay-to-pubkey-hash address", + } + } + + // Decode base64 signature. + sig, err := base64.StdEncoding.DecodeString(c.Signature) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCParse.Code, + Message: "Malformed base64 encoding: " + err.Error(), + } + } + + // Validate the signature - this just shows that it was valid at all. + // we will compare it with the key next. + var buf bytes.Buffer + wire.WriteVarString(&buf, 0, "Bitcoin Signed Message:\n") + wire.WriteVarString(&buf, 0, c.Message) + expectedMessageHash := wire.DoubleSha256(buf.Bytes()) + pk, wasCompressed, err := btcec.RecoverCompact(btcec.S256(), sig, + expectedMessageHash) + if err != nil { + // Mirror Bitcoin Core behavior, which treats error in + // RecoverCompact as invalid signature. + return false, nil + } + + // Reconstruct the pubkey hash. + btcPK := (*btcec.PublicKey)(pk) + var serializedPK []byte + if wasCompressed { + serializedPK = btcPK.SerializeCompressed() + } else { + serializedPK = btcPK.SerializeUncompressed() + } + address, err := btcutil.NewAddressPubKey(serializedPK, + activeNetParams.Params) + if err != nil { + // Again mirror Bitcoin Core behavior, which treats error in public key + // reconstruction as invalid signature. + return false, nil + } + + // Return boolean if addresses match. + return address.EncodeAddress() == c.Address, nil +} + +// rpcServer holds the items the rpc server may need to access (config, +// shutdown, main server, etc.) +type rpcServer struct { + started int32 + shutdown int32 + policy *mining.Policy + server *server + chain *blockchain.BlockChain + authsha [fastsha256.Size]byte + limitauthsha [fastsha256.Size]byte + ntfnMgr *wsNotificationManager + numClients int32 + statusLines map[int]string + statusLock sync.RWMutex + wg sync.WaitGroup + listeners []net.Listener + workState *workState + gbtWorkState *gbtWorkState + helpCacher *helpCacher + quit chan int +} + +// httpStatusLine returns a response Status-Line (RFC 2616 Section 6.1) +// for the given request and response status code. This function was lifted and +// adapted from the standard library HTTP server code since it's not exported. +func (s *rpcServer) httpStatusLine(req *http.Request, code int) string { + // Fast path: + key := code + proto11 := req.ProtoAtLeast(1, 1) + if !proto11 { + key = -key + } + s.statusLock.RLock() + line, ok := s.statusLines[key] + s.statusLock.RUnlock() + if ok { + return line + } + + // Slow path: + proto := "HTTP/1.0" + if proto11 { + proto = "HTTP/1.1" + } + codeStr := strconv.Itoa(code) + text := http.StatusText(code) + if text != "" { + line = proto + " " + codeStr + " " + text + "\r\n" + s.statusLock.Lock() + s.statusLines[key] = line + s.statusLock.Unlock() + } else { + text = "status code " + codeStr + line = proto + " " + codeStr + " " + text + "\r\n" + } + + return line +} + +// writeHTTPResponseHeaders writes the necessary response headers prior to +// writing an HTTP body given a request to use for protocol negotiation, headers +// to write, a status code, and a writer. +func (s *rpcServer) writeHTTPResponseHeaders(req *http.Request, headers http.Header, code int, w io.Writer) error { + _, err := io.WriteString(w, s.httpStatusLine(req, code)) + if err != nil { + return err + } + + err = headers.Write(w) + if err != nil { + return err + } + + _, err = io.WriteString(w, "\r\n") + if err != nil { + return err + } + + return nil +} + +// Stop is used by server.go to stop the rpc listener. +func (s *rpcServer) Stop() error { + if atomic.AddInt32(&s.shutdown, 1) != 1 { + rpcsLog.Infof("RPC server is already in the process of shutting down") + return nil + } + rpcsLog.Warnf("RPC server shutting down") + for _, listener := range s.listeners { + err := listener.Close() + if err != nil { + rpcsLog.Errorf("Problem shutting down rpc: %v", err) + return err + } + } + s.ntfnMgr.Shutdown() + s.ntfnMgr.WaitForShutdown() + close(s.quit) + s.wg.Wait() + rpcsLog.Infof("RPC server shutdown complete") + return nil +} + +// limitConnections responds with a 503 service unavailable and returns true if +// adding another client would exceed the maximum allow RPC clients. +// +// This function is safe for concurrent access. +func (s *rpcServer) limitConnections(w http.ResponseWriter, remoteAddr string) bool { + if int(atomic.LoadInt32(&s.numClients)+1) > cfg.RPCMaxClients { + rpcsLog.Infof("Max RPC clients exceeded [%d] - "+ + "disconnecting client %s", cfg.RPCMaxClients, + remoteAddr) + http.Error(w, "503 Too busy. Try again later.", + http.StatusServiceUnavailable) + return true + } + return false +} + +// incrementClients adds one to the number of connected RPC clients. Note +// this only applies to standard clients. Websocket clients have their own +// limits and are tracked separately. +// +// This function is safe for concurrent access. +func (s *rpcServer) incrementClients() { + atomic.AddInt32(&s.numClients, 1) +} + +// decrementClients subtracts one from the number of connected RPC clients. +// Note this only applies to standard clients. Websocket clients have their own +// limits and are tracked separately. +// +// This function is safe for concurrent access. +func (s *rpcServer) decrementClients() { + atomic.AddInt32(&s.numClients, -1) +} + +// checkAuth checks the HTTP Basic authentication supplied by a wallet +// or RPC client in the HTTP request r. If the supplied authentication +// does not match the username and password expected, a non-nil error is +// returned. +// +// This check is time-constant. +// +// The first bool return value signifies auth success (true if successful) and +// the second bool return value specifies whether the user can change the state +// of the server (true) or whether the user is limited (false). The second is +// always false if the first is. +func (s *rpcServer) checkAuth(r *http.Request, require bool) (bool, bool, error) { + authhdr := r.Header["Authorization"] + if len(authhdr) <= 0 { + if require { + rpcsLog.Warnf("RPC authentication failure from %s", + r.RemoteAddr) + return false, false, errors.New("auth failure") + } + + return false, false, nil + } + + authsha := fastsha256.Sum256([]byte(authhdr[0])) + + // Check for limited auth first as in environments with limited users, those + // are probably expected to have a higher volume of calls + limitcmp := subtle.ConstantTimeCompare(authsha[:], s.limitauthsha[:]) + if limitcmp == 1 { + return true, false, nil + } + + // Check for admin-level auth + cmp := subtle.ConstantTimeCompare(authsha[:], s.authsha[:]) + if cmp == 1 { + return true, true, nil + } + + // Request's auth doesn't match either user + rpcsLog.Warnf("RPC authentication failure from %s", r.RemoteAddr) + return false, false, errors.New("auth failure") +} + +// parsedRPCCmd represents a JSON-RPC request object that has been parsed into +// a known concrete command along with any error that might have happened while +// parsing it. +type parsedRPCCmd struct { + id interface{} + method string + cmd interface{} + err *btcjson.RPCError +} + +// standardCmdResult checks that a parsed command is a standard Bitcoin JSON-RPC +// command and runs the appropriate handler to reply to the command. Any +// commands which are not recognized or not implemented will return an error +// suitable for use in replies. +func (s *rpcServer) standardCmdResult(cmd *parsedRPCCmd, closeChan <-chan struct{}) (interface{}, error) { + handler, ok := rpcHandlers[cmd.method] + if ok { + goto handled + } + _, ok = rpcAskWallet[cmd.method] + if ok { + handler = handleAskWallet + goto handled + } + _, ok = rpcUnimplemented[cmd.method] + if ok { + handler = handleUnimplemented + goto handled + } + return nil, btcjson.ErrRPCMethodNotFound +handled: + + return handler(s, cmd.cmd, closeChan) +} + +// parseCmd parses a JSON-RPC request object into known concrete command. The +// err field of the returned parsedRPCCmd struct will contain an RPC error that +// is suitable for use in replies if the command is invalid in some way such as +// an unregistered command or invalid parameters. +func parseCmd(request *btcjson.Request) *parsedRPCCmd { + var parsedCmd parsedRPCCmd + parsedCmd.id = request.ID + parsedCmd.method = request.Method + + cmd, err := btcjson.UnmarshalCmd(request) + if err != nil { + // When the error is because the method is not registered, + // produce a method not found RPC error. + if jerr, ok := err.(btcjson.Error); ok && + jerr.ErrorCode == btcjson.ErrUnregisteredMethod { + + parsedCmd.err = btcjson.ErrRPCMethodNotFound + return &parsedCmd + } + + // Otherwise, some type of invalid parameters is the + // cause, so produce the equivalent RPC error. + parsedCmd.err = btcjson.NewRPCError( + btcjson.ErrRPCInvalidParams.Code, err.Error()) + return &parsedCmd + } + + parsedCmd.cmd = cmd + return &parsedCmd +} + +// createMarshalledReply returns a new marshalled JSON-RPC response given the +// passed parameters. It will automatically convert errors that are not of +// the type *btcjson.RPCError to the appropriate type as needed. +func createMarshalledReply(id, result interface{}, replyErr error) ([]byte, error) { + var jsonErr *btcjson.RPCError + if replyErr != nil { + if jErr, ok := replyErr.(*btcjson.RPCError); ok { + jsonErr = jErr + } else { + jsonErr = internalRPCError(replyErr.Error(), "") + } + } + + return btcjson.MarshalResponse(id, result, jsonErr) +} + +// jsonRPCRead handles reading and responding to RPC messages. +func (s *rpcServer) jsonRPCRead(w http.ResponseWriter, r *http.Request, isAdmin bool) { + if atomic.LoadInt32(&s.shutdown) != 0 { + return + } + + // Read and close the JSON-RPC request body from the caller. + body, err := ioutil.ReadAll(r.Body) + r.Body.Close() + if err != nil { + errMsg := fmt.Sprintf("error reading JSON message: %v", err) + errCode := http.StatusBadRequest + http.Error(w, strconv.FormatInt(int64(errCode), 10)+" "+errMsg, + errCode) + return + } + + // Unfortunately, the http server doesn't provide the ability to + // change the read deadline for the new connection and having one breaks + // long polling. However, not having a read deadline on the initial + // connection would mean clients can connect and idle forever. Thus, + // hijack the connecton from the HTTP server, clear the read deadline, + // and handle writing the response manually. + hj, ok := w.(http.Hijacker) + if !ok { + errMsg := "webserver doesn't support hijacking" + rpcsLog.Warnf(errMsg) + errCode := http.StatusInternalServerError + http.Error(w, strconv.FormatInt(int64(errCode), 10)+" "+errMsg, + errCode) + return + } + conn, buf, err := hj.Hijack() + if err != nil { + rpcsLog.Warnf("Failed to hijack HTTP connection: %v", err) + errCode := http.StatusInternalServerError + http.Error(w, strconv.FormatInt(int64(errCode), 10)+" "+ + err.Error(), errCode) + return + } + defer conn.Close() + defer buf.Flush() + conn.SetReadDeadline(timeZeroVal) + + // Attempt to parse the raw body into a JSON-RPC request. + var responseID interface{} + var jsonErr error + var result interface{} + var request btcjson.Request + if err := json.Unmarshal(body, &request); err != nil { + jsonErr = &btcjson.RPCError{ + Code: btcjson.ErrRPCParse.Code, + Message: "Failed to parse request: " + err.Error(), + } + } + if jsonErr == nil { + // Requests with no ID (notifications) must not have a response + // per the JSON-RPC spec. + if request.ID == nil { + return + } + + // The parse was at least successful enough to have an ID so + // set it for the response. + responseID = request.ID + + // Setup a close notifier. Since the connection is hijacked, + // the CloseNotifer on the ResponseWriter is not available. + closeChan := make(chan struct{}, 1) + go func() { + _, err := conn.Read(make([]byte, 1)) + if err != nil { + close(closeChan) + } + }() + + // Check if the user is limited and set error if method unauthorized + if !isAdmin { + if _, ok := rpcLimited[request.Method]; !ok { + jsonErr = &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParams.Code, + Message: "limited user not authorized for this method", + } + } + } + + if jsonErr == nil { + // Attempt to parse the JSON-RPC request into a known concrete + // command. + parsedCmd := parseCmd(&request) + if parsedCmd.err != nil { + jsonErr = parsedCmd.err + } else { + result, jsonErr = s.standardCmdResult(parsedCmd, closeChan) + } + } + } + + // Marshal the response. + msg, err := createMarshalledReply(responseID, result, jsonErr) + if err != nil { + rpcsLog.Errorf("Failed to marshal reply: %v", err) + return + } + + // Write the response. + err = s.writeHTTPResponseHeaders(r, w.Header(), http.StatusOK, buf) + if err != nil { + rpcsLog.Error(err) + return + } + if _, err := buf.Write(msg); err != nil { + rpcsLog.Errorf("Failed to write marshalled reply: %v", err) + } +} + +// jsonAuthFail sends a message back to the client if the http auth is rejected. +func jsonAuthFail(w http.ResponseWriter) { + w.Header().Add("WWW-Authenticate", `Basic realm="btcd RPC"`) + http.Error(w, "401 Unauthorized.", http.StatusUnauthorized) +} + +// Start is used by server.go to start the rpc listener. +func (s *rpcServer) Start() { + if atomic.AddInt32(&s.started, 1) != 1 { + return + } + + rpcsLog.Trace("Starting RPC server") + rpcServeMux := http.NewServeMux() + httpServer := &http.Server{ + Handler: rpcServeMux, + + // Timeout connections which don't complete the initial + // handshake within the allowed timeframe. + ReadTimeout: time.Second * rpcAuthTimeoutSeconds, + } + rpcServeMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Connection", "close") + w.Header().Set("Content-Type", "application/json") + r.Close = true + + // Limit the number of connections to max allowed. + if s.limitConnections(w, r.RemoteAddr) { + return + } + + // Keep track of the number of connected clients. + s.incrementClients() + defer s.decrementClients() + _, isAdmin, err := s.checkAuth(r, true) + if err != nil { + jsonAuthFail(w) + return + } + + // Read and respond to the request. + s.jsonRPCRead(w, r, isAdmin) + }) + + // Websocket endpoint. + rpcServeMux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { + authenticated, isAdmin, err := s.checkAuth(r, false) + if err != nil { + jsonAuthFail(w) + return + } + + // Attempt to upgrade the connection to a websocket connection + // using the default size for read/write buffers. + ws, err := websocket.Upgrade(w, r, nil, 0, 0) + if err != nil { + if _, ok := err.(websocket.HandshakeError); !ok { + rpcsLog.Errorf("Unexpected websocket error: %v", + err) + } + http.Error(w, "400 Bad Request.", http.StatusBadRequest) + return + } + s.WebsocketHandler(ws, r.RemoteAddr, authenticated, isAdmin) + }) + + for _, listener := range s.listeners { + s.wg.Add(1) + go func(listener net.Listener) { + rpcsLog.Infof("RPC server listening on %s", listener.Addr()) + httpServer.Serve(listener) + rpcsLog.Tracef("RPC listener done for %s", listener.Addr()) + s.wg.Done() + }(listener) + } + + s.ntfnMgr.Start() +} + +// genCertPair generates a key/cert pair to the paths provided. +func genCertPair(certFile, keyFile string) error { + rpcsLog.Infof("Generating TLS certificates...") + + org := "btcd autogenerated cert" + validUntil := time.Now().Add(10 * 365 * 24 * time.Hour) + cert, key, err := btcutil.NewTLSCertPair(org, validUntil, nil) + if err != nil { + return err + } + + // Write cert and key files. + if err = ioutil.WriteFile(certFile, cert, 0666); err != nil { + return err + } + if err = ioutil.WriteFile(keyFile, key, 0600); err != nil { + os.Remove(certFile) + return err + } + + rpcsLog.Infof("Done generating TLS certificates") + return nil +} + +// newRPCServer returns a new instance of the rpcServer struct. +func newRPCServer(listenAddrs []string, policy *mining.Policy, s *server) (*rpcServer, error) { + rpc := rpcServer{ + policy: policy, + server: s, + chain: s.blockManager.chain, + statusLines: make(map[int]string), + workState: newWorkState(), + gbtWorkState: newGbtWorkState(s.timeSource), + helpCacher: newHelpCacher(), + quit: make(chan int), + } + if cfg.RPCUser != "" && cfg.RPCPass != "" { + login := cfg.RPCUser + ":" + cfg.RPCPass + auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) + rpc.authsha = fastsha256.Sum256([]byte(auth)) + } + if cfg.RPCLimitUser != "" && cfg.RPCLimitPass != "" { + login := cfg.RPCLimitUser + ":" + cfg.RPCLimitPass + auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) + rpc.limitauthsha = fastsha256.Sum256([]byte(auth)) + } + rpc.ntfnMgr = newWsNotificationManager(&rpc) + + // Setup TLS if not disabled. + listenFunc := net.Listen + if !cfg.DisableTLS { + // Generate the TLS cert and key file if both don't already + // exist. + if !fileExists(cfg.RPCKey) && !fileExists(cfg.RPCCert) { + err := genCertPair(cfg.RPCCert, cfg.RPCKey) + if err != nil { + return nil, err + } + } + keypair, err := tls.LoadX509KeyPair(cfg.RPCCert, cfg.RPCKey) + if err != nil { + return nil, err + } + + tlsConfig := tls.Config{ + Certificates: []tls.Certificate{keypair}, + MinVersion: tls.VersionTLS12, + } + + // Change the standard net.Listen function to the tls one. + listenFunc = func(net string, laddr string) (net.Listener, error) { + return tls.Listen(net, laddr, &tlsConfig) + } + } + + // TODO(oga) this code is similar to that in server, should be + // factored into something shared. + ipv4ListenAddrs, ipv6ListenAddrs, _, err := parseListeners(listenAddrs) + if err != nil { + return nil, err + } + listeners := make([]net.Listener, 0, + len(ipv6ListenAddrs)+len(ipv4ListenAddrs)) + for _, addr := range ipv4ListenAddrs { + listener, err := listenFunc("tcp4", addr) + if err != nil { + rpcsLog.Warnf("Can't listen on %s: %v", addr, err) + continue + } + listeners = append(listeners, listener) + } + + for _, addr := range ipv6ListenAddrs { + listener, err := listenFunc("tcp6", addr) + if err != nil { + rpcsLog.Warnf("Can't listen on %s: %v", addr, err) + continue + } + listeners = append(listeners, listener) + } + if len(listeners) == 0 { + return nil, errors.New("RPCS: No valid listen address") + } + + rpc.listeners = listeners + + return &rpc, nil +} + +func init() { + rpcHandlers = rpcHandlersBeforeInit + rand.Seed(time.Now().UnixNano()) +} diff --git a/vendor/github.com/btcsuite/btcd/rpcserverhelp.go b/vendor/github.com/btcsuite/btcd/rpcserverhelp.go new file mode 100644 index 0000000000000000000000000000000000000000..eee0053d93836184f6bb008a186fc9f9a000e286 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/rpcserverhelp.go @@ -0,0 +1,718 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "errors" + "sort" + "strings" + "sync" + + "github.com/btcsuite/btcd/btcjson" +) + +// helpDescsEnUS defines the English descriptions used for the help strings. +var helpDescsEnUS = map[string]string{ + // DebugLevelCmd help. + "debuglevel--synopsis": "Dynamically changes the debug logging level.\n" + + "The levelspec can either a debug level or of the form:\n" + + "<subsystem>=<level>,<subsystem2>=<level2>,...\n" + + "The valid debug levels are trace, debug, info, warn, error, and critical.\n" + + "The valid subsystems are AMGR, ADXR, BCDB, BMGR, BTCD, CHAN, DISC, PEER, RPCS, SCRP, SRVR, and TXMP.\n" + + "Finally the keyword 'show' will return a list of the available subsystems.", + "debuglevel-levelspec": "The debug level(s) to use or the keyword 'show'", + "debuglevel--condition0": "levelspec!=show", + "debuglevel--condition1": "levelspec=show", + "debuglevel--result0": "The string 'Done.'", + "debuglevel--result1": "The list of subsystems", + + // AddNodeCmd help. + "addnode--synopsis": "Attempts to add or remove a persistent peer.", + "addnode-addr": "IP address and port of the peer to operate on", + "addnode-subcmd": "'add' to add a persistent peer, 'remove' to remove a persistent peer, or 'onetry' to try a single connection to a peer", + + // NodeCmd help. + "node--synopsis": "Attempts to add or remove a peer.", + "node-subcmd": "'disconnect' to remove all matching non-persistent peers, 'remove' to remove a persistent peer, or 'connect' to connect to a peer", + "node-target": "Either the IP address and port of the peer to operate on, or a valid peer ID.", + "node-connectsubcmd": "'perm' to make the connected peer a permanent one, 'temp' to try a single connect to a peer", + + // TransactionInput help. + "transactioninput-txid": "The hash of the input transaction", + "transactioninput-vout": "The specific output of the input transaction to redeem", + + // CreateRawTransactionCmd help. + "createrawtransaction--synopsis": "Returns a new transaction spending the provided inputs and sending to the provided addresses.\n" + + "The transaction inputs are not signed in the created transaction.\n" + + "The signrawtransaction RPC command provided by wallet must be used to sign the resulting transaction.", + "createrawtransaction-inputs": "The inputs to the transaction", + "createrawtransaction-amounts": "JSON object with the destination addresses as keys and amounts as values", + "createrawtransaction-amounts--key": "address", + "createrawtransaction-amounts--value": "n.nnn", + "createrawtransaction-amounts--desc": "The destination address as the key and the amount in BTC as the value", + "createrawtransaction-locktime": "Locktime value; a non-zero value will also locktime-activate the inputs", + "createrawtransaction--result0": "Hex-encoded bytes of the serialized transaction", + + // ScriptSig help. + "scriptsig-asm": "Disassembly of the script", + "scriptsig-hex": "Hex-encoded bytes of the script", + + // PrevOut help. + "prevout-addresses": "previous output addresses", + "prevout-value": "previous output value", + + // VinPrevOut help. + "vinprevout-coinbase": "The hex-encoded bytes of the signature script (coinbase txns only)", + "vinprevout-txid": "The hash of the origin transaction (non-coinbase txns only)", + "vinprevout-vout": "The index of the output being redeemed from the origin transaction (non-coinbase txns only)", + "vinprevout-scriptSig": "The signature script used to redeem the origin transaction as a JSON object (non-coinbase txns only)", + "vinprevout-prevOut": "Data from the origin transaction output with index vout.", + "vinprevout-sequence": "The script sequence number", + + // Vin help. + "vin-coinbase": "The hex-encoded bytes of the signature script (coinbase txns only)", + "vin-txid": "The hash of the origin transaction (non-coinbase txns only)", + "vin-vout": "The index of the output being redeemed from the origin transaction (non-coinbase txns only)", + "vin-scriptSig": "The signature script used to redeem the origin transaction as a JSON object (non-coinbase txns only)", + "vin-sequence": "The script sequence number", + + // ScriptPubKeyResult help. + "scriptpubkeyresult-asm": "Disassembly of the script", + "scriptpubkeyresult-hex": "Hex-encoded bytes of the script", + "scriptpubkeyresult-reqSigs": "The number of required signatures", + "scriptpubkeyresult-type": "The type of the script (e.g. 'pubkeyhash')", + "scriptpubkeyresult-addresses": "The bitcoin addresses associated with this script", + + // Vout help. + "vout-value": "The amount in BTC", + "vout-n": "The index of this transaction output", + "vout-scriptPubKey": "The public key script used to pay coins as a JSON object", + + // TxRawDecodeResult help. + "txrawdecoderesult-txid": "The hash of the transaction", + "txrawdecoderesult-version": "The transaction version", + "txrawdecoderesult-locktime": "The transaction lock time", + "txrawdecoderesult-vin": "The transaction inputs as JSON objects", + "txrawdecoderesult-vout": "The transaction outputs as JSON objects", + + // DecodeRawTransactionCmd help. + "decoderawtransaction--synopsis": "Returns a JSON object representing the provided serialized, hex-encoded transaction.", + "decoderawtransaction-hextx": "Serialized, hex-encoded transaction", + + // DecodeScriptResult help. + "decodescriptresult-asm": "Disassembly of the script", + "decodescriptresult-reqSigs": "The number of required signatures", + "decodescriptresult-type": "The type of the script (e.g. 'pubkeyhash')", + "decodescriptresult-addresses": "The bitcoin addresses associated with this script", + "decodescriptresult-p2sh": "The script hash for use in pay-to-script-hash transactions", + + // DecodeScriptCmd help. + "decodescript--synopsis": "Returns a JSON object with information about the provided hex-encoded script.", + "decodescript-hexscript": "Hex-encoded script", + + // GenerateCmd help + "generate--synopsis": "Generates a set number of blocks (simnet or regtest only) and returns a JSON\n" + + " array of their hashes.", + "generate-numblocks": "Number of blocks to generate", + "generate--result0": "The hashes, in order, of blocks generated by the call", + + // GetAddedNodeInfoResultAddr help. + "getaddednodeinforesultaddr-address": "The ip address for this DNS entry", + "getaddednodeinforesultaddr-connected": "The connection 'direction' (inbound/outbound/false)", + + // GetAddedNodeInfoResult help. + "getaddednodeinforesult-addednode": "The ip address or domain of the added peer", + "getaddednodeinforesult-connected": "Whether or not the peer is currently connected", + "getaddednodeinforesult-addresses": "DNS lookup and connection information about the peer", + + // GetAddedNodeInfo help. + "getaddednodeinfo--synopsis": "Returns information about manually added (persistent) peers.", + "getaddednodeinfo-dns": "Specifies whether the returned data is a JSON object including DNS and connection information, or just a list of added peers", + "getaddednodeinfo-node": "Only return information about this specific peer instead of all added peers", + "getaddednodeinfo--condition0": "dns=false", + "getaddednodeinfo--condition1": "dns=true", + "getaddednodeinfo--result0": "List of added peers", + + // GetBestBlockResult help. + "getbestblockresult-hash": "Hex-encoded bytes of the best block hash", + "getbestblockresult-height": "Height of the best block", + + // GetBestBlockCmd help. + "getbestblock--synopsis": "Get block height and hash of best block in the main chain.", + "getbestblock--result0": "Get block height and hash of best block in the main chain.", + + // GetBestBlockHashCmd help. + "getbestblockhash--synopsis": "Returns the hash of the of the best (most recent) block in the longest block chain.", + "getbestblockhash--result0": "The hex-encoded block hash", + + // GetBlockCmd help. + "getblock--synopsis": "Returns information about a block given its hash.", + "getblock-hash": "The hash of the block", + "getblock-verbose": "Specifies the block is returned as a JSON object instead of hex-encoded string", + "getblock-verbosetx": "Specifies that each transaction is returned as a JSON object and only applies if the verbose flag is true (btcd extension)", + "getblock--condition0": "verbose=false", + "getblock--condition1": "verbose=true", + "getblock--result0": "Hex-encoded bytes of the serialized block", + + // TxRawResult help. + "txrawresult-hex": "Hex-encoded transaction", + "txrawresult-txid": "The hash of the transaction", + "txrawresult-version": "The transaction version", + "txrawresult-locktime": "The transaction lock time", + "txrawresult-vin": "The transaction inputs as JSON objects", + "txrawresult-vout": "The transaction outputs as JSON objects", + "txrawresult-blockhash": "Hash of the block the transaction is part of", + "txrawresult-confirmations": "Number of confirmations of the block", + "txrawresult-time": "Transaction time in seconds since 1 Jan 1970 GMT", + "txrawresult-blocktime": "Block time in seconds since the 1 Jan 1970 GMT", + + // SearchRawTransactionsResult help. + "searchrawtransactionsresult-hex": "Hex-encoded transaction", + "searchrawtransactionsresult-txid": "The hash of the transaction", + "searchrawtransactionsresult-version": "The transaction version", + "searchrawtransactionsresult-locktime": "The transaction lock time", + "searchrawtransactionsresult-vin": "The transaction inputs as JSON objects", + "searchrawtransactionsresult-vout": "The transaction outputs as JSON objects", + "searchrawtransactionsresult-blockhash": "Hash of the block the transaction is part of", + "searchrawtransactionsresult-confirmations": "Number of confirmations of the block", + "searchrawtransactionsresult-time": "Transaction time in seconds since 1 Jan 1970 GMT", + "searchrawtransactionsresult-blocktime": "Block time in seconds since the 1 Jan 1970 GMT", + + // GetBlockVerboseResult help. + "getblockverboseresult-hash": "The hash of the block (same as provided)", + "getblockverboseresult-confirmations": "The number of confirmations", + "getblockverboseresult-size": "The size of the block", + "getblockverboseresult-height": "The height of the block in the block chain", + "getblockverboseresult-version": "The block version", + "getblockverboseresult-merkleroot": "Root hash of the merkle tree", + "getblockverboseresult-tx": "The transaction hashes (only when verbosetx=false)", + "getblockverboseresult-rawtx": "The transactions as JSON objects (only when verbosetx=true)", + "getblockverboseresult-time": "The block time in seconds since 1 Jan 1970 GMT", + "getblockverboseresult-nonce": "The block nonce", + "getblockverboseresult-bits": "The bits which represent the block difficulty", + "getblockverboseresult-difficulty": "The proof-of-work difficulty as a multiple of the minimum difficulty", + "getblockverboseresult-previousblockhash": "The hash of the previous block", + "getblockverboseresult-nextblockhash": "The hash of the next block (only if there is one)", + + // GetBlockCountCmd help. + "getblockcount--synopsis": "Returns the number of blocks in the longest block chain.", + "getblockcount--result0": "The current block count", + + // GetBlockHashCmd help. + "getblockhash--synopsis": "Returns hash of the block in best block chain at the given height.", + "getblockhash-index": "The block height", + "getblockhash--result0": "The block hash", + + // GetBlockHeaderCmd help. + "getblockheader--synopsis": "Returns information about a block header given its hash.", + "getblockheader-hash": "The hash of the block", + "getblockheader-verbose": "Specifies the block header is returned as a JSON object instead of hex-encoded string", + "getblockheader--condition0": "verbose=false", + "getblockheader--condition1": "verbose=true", + "getblockheader--result0": "The block header hash", + + // GetBlockHeaderVerboseResult help. + "getblockheaderverboseresult-hash": "The hash of the block (same as provided)", + "getblockheaderverboseresult-confirmations": "The number of confirmations", + "getblockheaderverboseresult-height": "The height of the block in the block chain", + "getblockheaderverboseresult-version": "The block version", + "getblockheaderverboseresult-merkleroot": "Root hash of the merkle tree", + "getblockheaderverboseresult-time": "The block time in seconds since 1 Jan 1970 GMT", + "getblockheaderverboseresult-nonce": "The block nonce", + "getblockheaderverboseresult-bits": "The bits which represent the block difficulty", + "getblockheaderverboseresult-difficulty": "The proof-of-work difficulty as a multiple of the minimum difficulty", + "getblockheaderverboseresult-previousblockhash": "The hash of the previous block", + "getblockheaderverboseresult-nextblockhash": "The hash of the next block (only if there is one)", + + // TemplateRequest help. + "templaterequest-mode": "This is 'template', 'proposal', or omitted", + "templaterequest-capabilities": "List of capabilities", + "templaterequest-longpollid": "The long poll ID of a job to monitor for expiration; required and valid only for long poll requests ", + "templaterequest-sigoplimit": "Number of signature operations allowed in blocks (this parameter is ignored)", + "templaterequest-sizelimit": "Number of bytes allowed in blocks (this parameter is ignored)", + "templaterequest-maxversion": "Highest supported block version number (this parameter is ignored)", + "templaterequest-target": "The desired target for the block template (this parameter is ignored)", + "templaterequest-data": "Hex-encoded block data (only for mode=proposal)", + "templaterequest-workid": "The server provided workid if provided in block template (not applicable)", + + // GetBlockTemplateResultTx help. + "getblocktemplateresulttx-data": "Hex-encoded transaction data (byte-for-byte)", + "getblocktemplateresulttx-hash": "Hex-encoded transaction hash (little endian if treated as a 256-bit number)", + "getblocktemplateresulttx-depends": "Other transactions before this one (by 1-based index in the 'transactions' list) that must be present in the final block if this one is", + "getblocktemplateresulttx-fee": "Difference in value between transaction inputs and outputs (in Satoshi)", + "getblocktemplateresulttx-sigops": "Total number of signature operations as counted for purposes of block limits", + + // GetBlockTemplateResultAux help. + "getblocktemplateresultaux-flags": "Hex-encoded byte-for-byte data to include in the coinbase signature script", + + // GetBlockTemplateResult help. + "getblocktemplateresult-bits": "Hex-encoded compressed difficulty", + "getblocktemplateresult-curtime": "Current time as seen by the server (recommended for block time); must fall within mintime/maxtime rules", + "getblocktemplateresult-height": "Height of the block to be solved", + "getblocktemplateresult-previousblockhash": "Hex-encoded big-endian hash of the previous block", + "getblocktemplateresult-sigoplimit": "Number of sigops allowed in blocks ", + "getblocktemplateresult-sizelimit": "Number of bytes allowed in blocks", + "getblocktemplateresult-transactions": "Array of transactions as JSON objects", + "getblocktemplateresult-version": "The block version", + "getblocktemplateresult-coinbaseaux": "Data that should be included in the coinbase signature script", + "getblocktemplateresult-coinbasetxn": "Information about the coinbase transaction", + "getblocktemplateresult-coinbasevalue": "Total amount available for the coinbase in Satoshi", + "getblocktemplateresult-workid": "This value must be returned with result if provided (not provided)", + "getblocktemplateresult-longpollid": "Identifier for long poll request which allows monitoring for expiration", + "getblocktemplateresult-longpolluri": "An alternate URI to use for long poll requests if provided (not provided)", + "getblocktemplateresult-submitold": "Not applicable", + "getblocktemplateresult-target": "Hex-encoded big-endian number which valid results must be less than", + "getblocktemplateresult-expires": "Maximum number of seconds (starting from when the server sent the response) this work is valid for", + "getblocktemplateresult-maxtime": "Maximum allowed time", + "getblocktemplateresult-mintime": "Minimum allowed time", + "getblocktemplateresult-mutable": "List of mutations the server explicitly allows", + "getblocktemplateresult-noncerange": "Two concatenated hex-encoded big-endian 32-bit integers which represent the valid ranges of nonces the miner may scan", + "getblocktemplateresult-capabilities": "List of server capabilities including 'proposal' to indicate support for block proposals", + "getblocktemplateresult-reject-reason": "Reason the proposal was invalid as-is (only applies to proposal responses)", + + // GetBlockTemplateCmd help. + "getblocktemplate--synopsis": "Returns a JSON object with information necessary to construct a block to mine or accepts a proposal to validate.\n" + + "See BIP0022 and BIP0023 for the full specification.", + "getblocktemplate-request": "Request object which controls the mode and several parameters", + "getblocktemplate--condition0": "mode=template", + "getblocktemplate--condition1": "mode=proposal, rejected", + "getblocktemplate--condition2": "mode=proposal, accepted", + "getblocktemplate--result1": "An error string which represents why the proposal was rejected or nothing if accepted", + + // GetConnectionCountCmd help. + "getconnectioncount--synopsis": "Returns the number of active connections to other peers.", + "getconnectioncount--result0": "The number of connections", + + // GetCurrentNetCmd help. + "getcurrentnet--synopsis": "Get bitcoin network the server is running on.", + "getcurrentnet--result0": "The network identifer", + + // GetDifficultyCmd help. + "getdifficulty--synopsis": "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.", + "getdifficulty--result0": "The difficulty", + + // GetGenerateCmd help. + "getgenerate--synopsis": "Returns if the server is set to generate coins (mine) or not.", + "getgenerate--result0": "True if mining, false if not", + + // GetHashesPerSecCmd help. + "gethashespersec--synopsis": "Returns a recent hashes per second performance measurement while generating coins (mining).", + "gethashespersec--result0": "The number of hashes per second", + + // InfoChainResult help. + "infochainresult-version": "The version of the server", + "infochainresult-protocolversion": "The latest supported protocol version", + "infochainresult-blocks": "The number of blocks processed", + "infochainresult-timeoffset": "The time offset", + "infochainresult-connections": "The number of connected peers", + "infochainresult-proxy": "The proxy used by the server", + "infochainresult-difficulty": "The current target difficulty", + "infochainresult-testnet": "Whether or not server is using testnet", + "infochainresult-relayfee": "The minimum relay fee for non-free transactions in BTC/KB", + "infochainresult-errors": "Any current errors", + + // InfoWalletResult help. + "infowalletresult-version": "The version of the server", + "infowalletresult-protocolversion": "The latest supported protocol version", + "infowalletresult-walletversion": "The version of the wallet server", + "infowalletresult-balance": "The total bitcoin balance of the wallet", + "infowalletresult-blocks": "The number of blocks processed", + "infowalletresult-timeoffset": "The time offset", + "infowalletresult-connections": "The number of connected peers", + "infowalletresult-proxy": "The proxy used by the server", + "infowalletresult-difficulty": "The current target difficulty", + "infowalletresult-testnet": "Whether or not server is using testnet", + "infowalletresult-keypoololdest": "Seconds since 1 Jan 1970 GMT of the oldest pre-generated key in the key pool", + "infowalletresult-keypoolsize": "The number of new keys that are pre-generated", + "infowalletresult-unlocked_until": "The timestamp in seconds since 1 Jan 1970 GMT that the wallet is unlocked for transfers, or 0 if the wallet is locked", + "infowalletresult-paytxfee": "The transaction fee set in BTC/KB", + "infowalletresult-relayfee": "The minimum relay fee for non-free transactions in BTC/KB", + "infowalletresult-errors": "Any current errors", + + // GetInfoCmd help. + "getinfo--synopsis": "Returns a JSON object containing various state info.", + + // GetMempoolInfoCmd help. + "getmempoolinfo--synopsis": "Returns memory pool information", + + // GetMempoolInfoResult help. + "getmempoolinforesult-bytes": "Size in bytes of the mempool", + "getmempoolinforesult-size": "Number of transactions in the mempool", + + // GetMiningInfoResult help. + "getmininginforesult-blocks": "Height of the latest best block", + "getmininginforesult-currentblocksize": "Size of the latest best block", + "getmininginforesult-currentblocktx": "Number of transactions in the latest best block", + "getmininginforesult-difficulty": "Current target difficulty", + "getmininginforesult-errors": "Any current errors", + "getmininginforesult-generate": "Whether or not server is set to generate coins", + "getmininginforesult-genproclimit": "Number of processors to use for coin generation (-1 when disabled)", + "getmininginforesult-hashespersec": "Recent hashes per second performance measurement while generating coins", + "getmininginforesult-networkhashps": "Estimated network hashes per second for the most recent blocks", + "getmininginforesult-pooledtx": "Number of transactions in the memory pool", + "getmininginforesult-testnet": "Whether or not server is using testnet", + + // GetMiningInfoCmd help. + "getmininginfo--synopsis": "Returns a JSON object containing mining-related information.", + + // GetNetworkHashPSCmd help. + "getnetworkhashps--synopsis": "Returns the estimated network hashes per second for the block heights provided by the parameters.", + "getnetworkhashps-blocks": "The number of blocks, or -1 for blocks since last difficulty change", + "getnetworkhashps-height": "Perform estimate ending with this height or -1 for current best chain block height", + "getnetworkhashps--result0": "Estimated hashes per second", + + // GetNetTotalsCmd help. + "getnettotals--synopsis": "Returns a JSON object containing network traffic statistics.", + + // GetNetTotalsResult help. + "getnettotalsresult-totalbytesrecv": "Total bytes received", + "getnettotalsresult-totalbytessent": "Total bytes sent", + "getnettotalsresult-timemillis": "Number of milliseconds since 1 Jan 1970 GMT", + + // GetPeerInfoResult help. + "getpeerinforesult-id": "A unique node ID", + "getpeerinforesult-addr": "The ip address and port of the peer", + "getpeerinforesult-addrlocal": "Local address", + "getpeerinforesult-services": "Services bitmask which represents the services supported by the peer", + "getpeerinforesult-lastsend": "Time the last message was received in seconds since 1 Jan 1970 GMT", + "getpeerinforesult-lastrecv": "Time the last message was sent in seconds since 1 Jan 1970 GMT", + "getpeerinforesult-bytessent": "Total bytes sent", + "getpeerinforesult-bytesrecv": "Total bytes received", + "getpeerinforesult-conntime": "Time the connection was made in seconds since 1 Jan 1970 GMT", + "getpeerinforesult-timeoffset": "The time offset of the peer", + "getpeerinforesult-pingtime": "Number of microseconds the last ping took", + "getpeerinforesult-pingwait": "Number of microseconds a queued ping has been waiting for a response", + "getpeerinforesult-version": "The protocol version of the peer", + "getpeerinforesult-subver": "The user agent of the peer", + "getpeerinforesult-inbound": "Whether or not the peer is an inbound connection", + "getpeerinforesult-startingheight": "The latest block height the peer knew about when the connection was established", + "getpeerinforesult-currentheight": "The current height of the peer", + "getpeerinforesult-banscore": "The ban score", + "getpeerinforesult-syncnode": "Whether or not the peer is the sync peer", + + // GetPeerInfoCmd help. + "getpeerinfo--synopsis": "Returns data about each connected network peer as an array of json objects.", + + // GetRawMempoolVerboseResult help. + "getrawmempoolverboseresult-size": "Transaction size in bytes", + "getrawmempoolverboseresult-fee": "Transaction fee in bitcoins", + "getrawmempoolverboseresult-time": "Local time transaction entered pool in seconds since 1 Jan 1970 GMT", + "getrawmempoolverboseresult-height": "Block height when transaction entered the pool", + "getrawmempoolverboseresult-startingpriority": "Priority when transaction entered the pool", + "getrawmempoolverboseresult-currentpriority": "Current priority", + "getrawmempoolverboseresult-depends": "Unconfirmed transactions used as inputs for this transaction", + + // GetRawMempoolCmd help. + "getrawmempool--synopsis": "Returns information about all of the transactions currently in the memory pool.", + "getrawmempool-verbose": "Returns JSON object when true or an array of transaction hashes when false", + "getrawmempool--condition0": "verbose=false", + "getrawmempool--condition1": "verbose=true", + "getrawmempool--result0": "Array of transaction hashes", + + // GetRawTransactionCmd help. + "getrawtransaction--synopsis": "Returns information about a transaction given its hash.", + "getrawtransaction-txid": "The hash of the transaction", + "getrawtransaction-verbose": "Specifies the transaction is returned as a JSON object instead of a hex-encoded string", + "getrawtransaction--condition0": "verbose=false", + "getrawtransaction--condition1": "verbose=true", + "getrawtransaction--result0": "Hex-encoded bytes of the serialized transaction", + + // GetTxOutResult help. + "gettxoutresult-bestblock": "The block hash that contains the transaction output", + "gettxoutresult-confirmations": "The number of confirmations", + "gettxoutresult-value": "The transaction amount in BTC", + "gettxoutresult-scriptPubKey": "The public key script used to pay coins as a JSON object", + "gettxoutresult-version": "The transaction version", + "gettxoutresult-coinbase": "Whether or not the transaction is a coinbase", + + // GetTxOutCmd help. + "gettxout--synopsis": "Returns information about an unspent transaction output..", + "gettxout-txid": "The hash of the transaction", + "gettxout-vout": "The index of the output", + "gettxout-includemempool": "Include the mempool when true", + + // GetWorkResult help. + "getworkresult-data": "Hex-encoded block data", + "getworkresult-hash1": "(DEPRECATED) Hex-encoded formatted hash buffer", + "getworkresult-midstate": "(DEPRECATED) Hex-encoded precomputed hash state after hashing first half of the data", + "getworkresult-target": "Hex-encoded little-endian hash target", + + // GetWorkCmd help. + "getwork--synopsis": "(DEPRECATED - Use getblocktemplate instead) Returns formatted hash data to work on or checks and submits solved data.", + "getwork-data": "Hex-encoded data to check", + "getwork--condition0": "no data provided", + "getwork--condition1": "data provided", + "getwork--result1": "Whether or not the solved data is valid and was added to the chain", + + // HelpCmd help. + "help--synopsis": "Returns a list of all commands or help for a specified command.", + "help-command": "The command to retrieve help for", + "help--condition0": "no command provided", + "help--condition1": "command specified", + "help--result0": "List of commands", + "help--result1": "Help for specified command", + + // PingCmd help. + "ping--synopsis": "Queues a ping to be sent to each connected peer.\n" + + "Ping times are provided by getpeerinfo via the pingtime and pingwait fields.", + + // SearchRawTransactionsCmd help. + "searchrawtransactions--synopsis": "Returns raw data for transactions involving the passed address.\n" + + "Returned transactions are pulled from both the database, and transactions currently in the mempool.\n" + + "Transactions pulled from the mempool will have the 'confirmations' field set to 0.\n" + + "Usage of this RPC requires the optional --addrindex flag to be activated, otherwise all responses will simply return with an error stating the address index has not yet been built.\n" + + "Similarly, until the address index has caught up with the current best height, all requests will return an error response in order to avoid serving stale data.", + "searchrawtransactions-address": "The Bitcoin address to search for", + "searchrawtransactions-verbose": "Specifies the transaction is returned as a JSON object instead of hex-encoded string", + "searchrawtransactions--condition0": "verbose=0", + "searchrawtransactions--condition1": "verbose=1", + "searchrawtransactions-skip": "The number of leading transactions to leave out of the final response", + "searchrawtransactions-count": "The maximum number of transactions to return", + "searchrawtransactions-vinextra": "Specify that extra data from previous output will be returned in vin", + "searchrawtransactions-reverse": "Specifies that the transactions should be returned in reverse chronological order", + "searchrawtransactions-filteraddrs": "Address list. Only inputs or outputs with matching address will be returned", + "searchrawtransactions--result0": "Hex-encoded serialized transaction", + + // SendRawTransactionCmd help. + "sendrawtransaction--synopsis": "Submits the serialized, hex-encoded transaction to the local peer and relays it to the network.", + "sendrawtransaction-hextx": "Serialized, hex-encoded signed transaction", + "sendrawtransaction-allowhighfees": "Whether or not to allow insanely high fees (btcd does not yet implement this parameter, so it has no effect)", + "sendrawtransaction--result0": "The hash of the transaction", + + // SetGenerateCmd help. + "setgenerate--synopsis": "Set the server to generate coins (mine) or not.", + "setgenerate-generate": "Use true to enable generation, false to disable it", + "setgenerate-genproclimit": "The number of processors (cores) to limit generation to or -1 for default", + + // StopCmd help. + "stop--synopsis": "Shutdown btcd.", + "stop--result0": "The string 'btcd stopping.'", + + // SubmitBlockOptions help. + "submitblockoptions-workid": "This parameter is currently ignored", + + // SubmitBlockCmd help. + "submitblock--synopsis": "Attempts to submit a new serialized, hex-encoded block to the network.", + "submitblock-hexblock": "Serialized, hex-encoded block", + "submitblock-options": "This parameter is currently ignored", + "submitblock--condition0": "Block successfully submitted", + "submitblock--condition1": "Block rejected", + "submitblock--result1": "The reason the block was rejected", + + // ValidateAddressResult help. + "validateaddresschainresult-isvalid": "Whether or not the address is valid", + "validateaddresschainresult-address": "The bitcoin address (only when isvalid is true)", + + // ValidateAddressCmd help. + "validateaddress--synopsis": "Verify an address is valid.", + "validateaddress-address": "Bitcoin address to validate", + + // VerifyChainCmd help. + "verifychain--synopsis": "Verifies the block chain database.\n" + + "The actual checks performed by the checklevel parameter are implementation specific.\n" + + "For btcd this is:\n" + + "checklevel=0 - Look up each block and ensure it can be loaded from the database.\n" + + "checklevel=1 - Perform basic context-free sanity checks on each block.", + "verifychain-checklevel": "How thorough the block verification is", + "verifychain-checkdepth": "The number of blocks to check", + "verifychain--result0": "Whether or not the chain verified", + + // VerifyMessageCmd help. + "verifymessage--synopsis": "Verify a signed message.", + "verifymessage-address": "The bitcoin address to use for the signature", + "verifymessage-signature": "The base-64 encoded signature provided by the signer", + "verifymessage-message": "The signed message", + "verifymessage--result0": "Whether or not the signature verified", + + // -------- Websocket-specific help -------- + + // Session help. + "session--synopsis": "Return details regarding a websocket client's current connection session.", + "sessionresult-sessionid": "The unique session ID for a client's websocket connection.", + + // NotifyBlocksCmd help. + "notifyblocks--synopsis": "Request notifications for whenever a block is connected or disconnected from the main (best) chain.", + + // StopNotifyBlocksCmd help. + "stopnotifyblocks--synopsis": "Cancel registered notifications for whenever a block is connected or disconnected from the main (best) chain.", + + // NotifyNewTransactionsCmd help. + "notifynewtransactions--synopsis": "Send either a txaccepted or a txacceptedverbose notification when a new transaction is accepted into the mempool.", + "notifynewtransactions-verbose": "Specifies which type of notification to receive. If verbose is true, then the caller receives txacceptedverbose, otherwise the caller receives txaccepted", + + // StopNotifyNewTransactionsCmd help. + "stopnotifynewtransactions--synopsis": "Stop sending either a txaccepted or a txacceptedverbose notification when a new transaction is accepted into the mempool.", + + // NotifyReceivedCmd help. + "notifyreceived--synopsis": "Send a recvtx notification when a transaction added to mempool or appears in a newly-attached block contains a txout pkScript sending to any of the passed addresses.\n" + + "Matching outpoints are automatically registered for redeemingtx notifications.", + "notifyreceived-addresses": "List of address to receive notifications about", + + // StopNotifyReceivedCmd help. + "stopnotifyreceived--synopsis": "Cancel registered receive notifications for each passed address.", + "stopnotifyreceived-addresses": "List of address to cancel receive notifications for", + + // OutPoint help. + "outpoint-hash": "The hex-encoded bytes of the outpoint hash", + "outpoint-index": "The index of the outpoint", + + // NotifySpentCmd help. + "notifyspent--synopsis": "Send a redeemingtx notification when a transaction spending an outpoint appears in mempool (if relayed to this btcd instance) and when such a transaction first appears in a newly-attached block.", + "notifyspent-outpoints": "List of transaction outpoints to monitor.", + + // StopNotifySpentCmd help. + "stopnotifyspent--synopsis": "Cancel registered spending notifications for each passed outpoint.", + "stopnotifyspent-outpoints": "List of transaction outpoints to stop monitoring.", + + // Rescan help. + "rescan--synopsis": "Rescan block chain for transactions to addresses.\n" + + "When the endblock parameter is omitted, the rescan continues through the best block in the main chain.\n" + + "Rescan results are sent as recvtx and redeemingtx notifications.\n" + + "This call returns once the rescan completes.", + "rescan-beginblock": "Hash of the first block to begin rescanning", + "rescan-addresses": "List of addresses to include in the rescan", + "rescan-outpoints": "List of transaction outpoints to include in the rescan", + "rescan-endblock": "Hash of final block to rescan", +} + +// rpcResultTypes specifies the result types that each RPC command can return. +// This information is used to generate the help. Each result type must be a +// pointer to the type (or nil to indicate no return value). +var rpcResultTypes = map[string][]interface{}{ + "addnode": nil, + "createrawtransaction": {(*string)(nil)}, + "debuglevel": {(*string)(nil), (*string)(nil)}, + "decoderawtransaction": {(*btcjson.TxRawDecodeResult)(nil)}, + "decodescript": {(*btcjson.DecodeScriptResult)(nil)}, + "generate": {(*[]string)(nil)}, + "getaddednodeinfo": {(*[]string)(nil), (*[]btcjson.GetAddedNodeInfoResult)(nil)}, + "getbestblock": {(*btcjson.GetBestBlockResult)(nil)}, + "getbestblockhash": {(*string)(nil)}, + "getblock": {(*string)(nil), (*btcjson.GetBlockVerboseResult)(nil)}, + "getblockcount": {(*int64)(nil)}, + "getblockhash": {(*string)(nil)}, + "getblockheader": {(*string)(nil), (*btcjson.GetBlockHeaderVerboseResult)(nil)}, + "getblocktemplate": {(*btcjson.GetBlockTemplateResult)(nil), (*string)(nil), nil}, + "getconnectioncount": {(*int32)(nil)}, + "getcurrentnet": {(*uint32)(nil)}, + "getdifficulty": {(*float64)(nil)}, + "getgenerate": {(*bool)(nil)}, + "gethashespersec": {(*float64)(nil)}, + "getinfo": {(*btcjson.InfoChainResult)(nil)}, + "getmempoolinfo": {(*btcjson.GetMempoolInfoResult)(nil)}, + "getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)}, + "getnettotals": {(*btcjson.GetNetTotalsResult)(nil)}, + "getnetworkhashps": {(*int64)(nil)}, + "getpeerinfo": {(*[]btcjson.GetPeerInfoResult)(nil)}, + "getrawmempool": {(*[]string)(nil), (*btcjson.GetRawMempoolVerboseResult)(nil)}, + "getrawtransaction": {(*string)(nil), (*btcjson.TxRawResult)(nil)}, + "gettxout": {(*btcjson.GetTxOutResult)(nil)}, + "getwork": {(*btcjson.GetWorkResult)(nil), (*bool)(nil)}, + "node": nil, + "help": {(*string)(nil), (*string)(nil)}, + "ping": nil, + "searchrawtransactions": {(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)}, + "sendrawtransaction": {(*string)(nil)}, + "setgenerate": nil, + "stop": {(*string)(nil)}, + "submitblock": {nil, (*string)(nil)}, + "validateaddress": {(*btcjson.ValidateAddressChainResult)(nil)}, + "verifychain": {(*bool)(nil)}, + "verifymessage": {(*bool)(nil)}, + + // Websocket commands. + "session": {(*btcjson.SessionResult)(nil)}, + "notifyblocks": nil, + "stopnotifyblocks": nil, + "notifynewtransactions": nil, + "stopnotifynewtransactions": nil, + "notifyreceived": nil, + "stopnotifyreceived": nil, + "notifyspent": nil, + "stopnotifyspent": nil, + "rescan": nil, +} + +// helpCacher provides a concurrent safe type that provides help and usage for +// the RPC server commands and caches the results for future calls. +type helpCacher struct { + sync.Mutex + usage string + methodHelp map[string]string +} + +// rpcMethodHelp returns an RPC help string for the provided method. +// +// This function is safe for concurrent access. +func (c *helpCacher) rpcMethodHelp(method string) (string, error) { + c.Lock() + defer c.Unlock() + + // Return the cached method help if it exists. + if help, exists := c.methodHelp[method]; exists { + return help, nil + } + + // Look up the result types for the method. + resultTypes, ok := rpcResultTypes[method] + if !ok { + return "", errors.New("no result types specified for method " + + method) + } + + // Generate, cache, and return the help. + help, err := btcjson.GenerateHelp(method, helpDescsEnUS, resultTypes...) + if err != nil { + return "", err + } + c.methodHelp[method] = help + return help, nil +} + +// rpcUsage returns one-line usage for all support RPC commands. +// +// This function is safe for concurrent access. +func (c *helpCacher) rpcUsage(includeWebsockets bool) (string, error) { + c.Lock() + defer c.Unlock() + + // Return the cached usage if it is available. + if c.usage != "" { + return c.usage, nil + } + + // Generate a list of one-line usage for every command. + usageTexts := make([]string, 0, len(rpcHandlers)) + for k := range rpcHandlers { + usage, err := btcjson.MethodUsageText(k) + if err != nil { + return "", err + } + usageTexts = append(usageTexts, usage) + } + + // Include websockets commands if requested. + if includeWebsockets { + for k := range wsHandlers { + usage, err := btcjson.MethodUsageText(k) + if err != nil { + return "", err + } + usageTexts = append(usageTexts, usage) + } + } + + sort.Sort(sort.StringSlice(usageTexts)) + c.usage = strings.Join(usageTexts, "\n") + return c.usage, nil +} + +// newHelpCacher returns a new instance of a help cacher which provides help and +// usage for the RPC server commands and caches the results for future calls. +func newHelpCacher() *helpCacher { + return &helpCacher{ + methodHelp: make(map[string]string), + } +} diff --git a/vendor/github.com/btcsuite/btcd/rpcserverhelp_test.go b/vendor/github.com/btcsuite/btcd/rpcserverhelp_test.go new file mode 100644 index 0000000000000000000000000000000000000000..45974313782d7f36711656a7ebea1c70d591d33c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/rpcserverhelp_test.go @@ -0,0 +1,65 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import "testing" + +// TestHelp ensures the help is reasonably accurate by checking that every +// command specified also has result types defined and the one-line usage and +// help text can be generated for them. +func TestHelp(t *testing.T) { + // Ensure there are result types specified for every handler. + for k := range rpcHandlers { + if _, ok := rpcResultTypes[k]; !ok { + t.Errorf("RPC handler defined for method '%v' without "+ + "also specifying result types", k) + continue + } + + } + for k := range wsHandlers { + if _, ok := rpcResultTypes[k]; !ok { + t.Errorf("RPC handler defined for method '%v' without "+ + "also specifying result types", k) + continue + } + + } + + // Ensure the usage for every command can be generated without errors. + helpCacher := newHelpCacher() + if _, err := helpCacher.rpcUsage(true); err != nil { + t.Fatalf("Failed to generate one-line usage: %v", err) + } + if _, err := helpCacher.rpcUsage(true); err != nil { + t.Fatalf("Failed to generate one-line usage (cached): %v", err) + } + + // Ensure the help for every command can be generated without errors. + for k := range rpcHandlers { + if _, err := helpCacher.rpcMethodHelp(k); err != nil { + t.Errorf("Failed to generate help for method '%v': %v", + k, err) + continue + } + if _, err := helpCacher.rpcMethodHelp(k); err != nil { + t.Errorf("Failed to generate help for method '%v'"+ + "(cached): %v", k, err) + continue + } + } + for k := range wsHandlers { + if _, err := helpCacher.rpcMethodHelp(k); err != nil { + t.Errorf("Failed to generate help for method '%v': %v", + k, err) + continue + } + if _, err := helpCacher.rpcMethodHelp(k); err != nil { + t.Errorf("Failed to generate help for method '%v'"+ + "(cached): %v", k, err) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/rpcwebsocket.go b/vendor/github.com/btcsuite/btcd/rpcwebsocket.go new file mode 100644 index 0000000000000000000000000000000000000000..074e9a6a221d1fa163f0b806bdb622e87b1b770f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/rpcwebsocket.go @@ -0,0 +1,2155 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "container/list" + "crypto/subtle" + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "sync" + "time" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/fastsha256" + "github.com/btcsuite/golangcrypto/ripemd160" + "github.com/btcsuite/websocket" +) + +const ( + // websocketSendBufferSize is the number of elements the send channel + // can queue before blocking. Note that this only applies to requests + // handled directly in the websocket client input handler or the async + // handler since notifications have their own queuing mechanism + // independent of the send channel buffer. + websocketSendBufferSize = 50 +) + +// timeZeroVal is simply the zero value for a time.Time and is used to avoid +// creating multiple instances. +var timeZeroVal time.Time + +// wsCommandHandler describes a callback function used to handle a specific +// command. +type wsCommandHandler func(*wsClient, interface{}) (interface{}, error) + +// wsHandlers maps RPC command strings to appropriate websocket handler +// functions. This is set by init because help references wsHandlers and thus +// causes a dependency loop. +var wsHandlers map[string]wsCommandHandler +var wsHandlersBeforeInit = map[string]wsCommandHandler{ + "help": handleWebsocketHelp, + "notifyblocks": handleNotifyBlocks, + "notifynewtransactions": handleNotifyNewTransactions, + "notifyreceived": handleNotifyReceived, + "notifyspent": handleNotifySpent, + "session": handleSession, + "stopnotifyblocks": handleStopNotifyBlocks, + "stopnotifynewtransactions": handleStopNotifyNewTransactions, + "stopnotifyspent": handleStopNotifySpent, + "stopnotifyreceived": handleStopNotifyReceived, + "rescan": handleRescan, +} + +// wsAsyncHandlers holds the websocket commands which should be run +// asynchronously to the main input handler goroutine. This allows long-running +// operations to run concurrently (and one at a time) while still responding +// to the majority of normal requests which can be answered quickly. +var wsAsyncHandlers = map[string]struct{}{ + "rescan": {}, +} + +// WebsocketHandler handles a new websocket client by creating a new wsClient, +// starting it, and blocking until the connection closes. Since it blocks, it +// must be run in a separate goroutine. It should be invoked from the websocket +// server handler which runs each new connection in a new goroutine thereby +// satisfying the requirement. +func (s *rpcServer) WebsocketHandler(conn *websocket.Conn, remoteAddr string, + authenticated bool, isAdmin bool) { + + // Clear the read deadline that was set before the websocket hijacked + // the connection. + conn.SetReadDeadline(timeZeroVal) + + // Limit max number of websocket clients. + rpcsLog.Infof("New websocket client %s", remoteAddr) + if s.ntfnMgr.NumClients()+1 > cfg.RPCMaxWebsockets { + rpcsLog.Infof("Max websocket clients exceeded [%d] - "+ + "disconnecting client %s", cfg.RPCMaxWebsockets, + remoteAddr) + conn.Close() + return + } + + // Create a new websocket client to handle the new websocket connection + // and wait for it to shutdown. Once it has shutdown (and hence + // disconnected), remove it and any notifications it registered for. + client, err := newWebsocketClient(s, conn, remoteAddr, authenticated, isAdmin) + if err != nil { + rpcsLog.Errorf("Failed to serve client %s: %v", remoteAddr, err) + conn.Close() + return + } + s.ntfnMgr.AddClient(client) + client.Start() + client.WaitForShutdown() + s.ntfnMgr.RemoveClient(client) + rpcsLog.Infof("Disconnected websocket client %s", remoteAddr) +} + +// wsNotificationManager is a connection and notification manager used for +// websockets. It allows websocket clients to register for notifications they +// are interested in. When an event happens elsewhere in the code such as +// transactions being added to the memory pool or block connects/disconnects, +// the notification manager is provided with the relevant details needed to +// figure out which websocket clients need to be notified based on what they +// have registered for and notifies them accordingly. It is also used to keep +// track of all connected websocket clients. +type wsNotificationManager struct { + // server is the RPC server the notification manager is associated with. + server *rpcServer + + // queueNotification queues a notification for handling. + queueNotification chan interface{} + + // notificationMsgs feeds notificationHandler with notifications + // and client (un)registeration requests from a queue as well as + // registeration and unregisteration requests from clients. + notificationMsgs chan interface{} + + // Access channel for current number of connected clients. + numClients chan int + + // Shutdown handling + wg sync.WaitGroup + quit chan struct{} +} + +// queueHandler manages a queue of empty interfaces, reading from in and +// sending the oldest unsent to out. This handler stops when either of the +// in or quit channels are closed, and closes out before returning, without +// waiting to send any variables still remaining in the queue. +func queueHandler(in <-chan interface{}, out chan<- interface{}, quit <-chan struct{}) { + var q []interface{} + var dequeue chan<- interface{} + skipQueue := out + var next interface{} +out: + for { + select { + case n, ok := <-in: + if !ok { + // Sender closed input channel. + break out + } + + // Either send to out immediately if skipQueue is + // non-nil (queue is empty) and reader is ready, + // or append to the queue and send later. + select { + case skipQueue <- n: + default: + q = append(q, n) + dequeue = out + skipQueue = nil + next = q[0] + } + + case dequeue <- next: + copy(q, q[1:]) + q[len(q)-1] = nil // avoid leak + q = q[:len(q)-1] + if len(q) == 0 { + dequeue = nil + skipQueue = out + } else { + next = q[0] + } + + case <-quit: + break out + } + } + close(out) +} + +// queueHandler maintains a queue of notifications and notification handler +// control messages. +func (m *wsNotificationManager) queueHandler() { + queueHandler(m.queueNotification, m.notificationMsgs, m.quit) + m.wg.Done() +} + +// NotifyBlockConnected passes a block newly-connected to the best chain +// to the notification manager for block and transaction notification +// processing. +func (m *wsNotificationManager) NotifyBlockConnected(block *btcutil.Block) { + // As NotifyBlockConnected will be called by the block manager + // and the RPC server may no longer be running, use a select + // statement to unblock enqueuing the notification once the RPC + // server has begun shutting down. + select { + case m.queueNotification <- (*notificationBlockConnected)(block): + case <-m.quit: + } +} + +// NotifyBlockDisconnected passes a block disconnected from the best chain +// to the notification manager for block notification processing. +func (m *wsNotificationManager) NotifyBlockDisconnected(block *btcutil.Block) { + // As NotifyBlockDisconnected will be called by the block manager + // and the RPC server may no longer be running, use a select + // statement to unblock enqueuing the notification once the RPC + // server has begun shutting down. + select { + case m.queueNotification <- (*notificationBlockDisconnected)(block): + case <-m.quit: + } +} + +// NotifyMempoolTx passes a transaction accepted by mempool to the +// notification manager for transaction notification processing. If +// isNew is true, the tx is is a new transaction, rather than one +// added to the mempool during a reorg. +func (m *wsNotificationManager) NotifyMempoolTx(tx *btcutil.Tx, isNew bool) { + n := ¬ificationTxAcceptedByMempool{ + isNew: isNew, + tx: tx, + } + + // As NotifyMempoolTx will be called by mempool and the RPC server + // may no longer be running, use a select statement to unblock + // enqueuing the notification once the RPC server has begun + // shutting down. + select { + case m.queueNotification <- n: + case <-m.quit: + } +} + +// Notification types +type notificationBlockConnected btcutil.Block +type notificationBlockDisconnected btcutil.Block +type notificationTxAcceptedByMempool struct { + isNew bool + tx *btcutil.Tx +} + +// Notification control requests +type notificationRegisterClient wsClient +type notificationUnregisterClient wsClient +type notificationRegisterBlocks wsClient +type notificationUnregisterBlocks wsClient +type notificationRegisterNewMempoolTxs wsClient +type notificationUnregisterNewMempoolTxs wsClient +type notificationRegisterSpent struct { + wsc *wsClient + ops []*wire.OutPoint +} +type notificationUnregisterSpent struct { + wsc *wsClient + op *wire.OutPoint +} +type notificationRegisterAddr struct { + wsc *wsClient + addrs []string +} +type notificationUnregisterAddr struct { + wsc *wsClient + addr string +} + +// notificationHandler reads notifications and control messages from the queue +// handler and processes one at a time. +func (m *wsNotificationManager) notificationHandler() { + // clients is a map of all currently connected websocket clients. + clients := make(map[chan struct{}]*wsClient) + + // Maps used to hold lists of websocket clients to be notified on + // certain events. Each websocket client also keeps maps for the events + // which have multiple triggers to make removal from these lists on + // connection close less horrendously expensive. + // + // Where possible, the quit channel is used as the unique id for a client + // since it is quite a bit more efficient than using the entire struct. + blockNotifications := make(map[chan struct{}]*wsClient) + txNotifications := make(map[chan struct{}]*wsClient) + watchedOutPoints := make(map[wire.OutPoint]map[chan struct{}]*wsClient) + watchedAddrs := make(map[string]map[chan struct{}]*wsClient) + +out: + for { + select { + case n, ok := <-m.notificationMsgs: + if !ok { + // queueHandler quit. + break out + } + switch n := n.(type) { + case *notificationBlockConnected: + block := (*btcutil.Block)(n) + + // Skip iterating through all txs if no + // tx notification requests exist. + if len(watchedOutPoints) != 0 || len(watchedAddrs) != 0 { + for _, tx := range block.Transactions() { + m.notifyForTx(watchedOutPoints, + watchedAddrs, tx, block) + } + } + + if len(blockNotifications) != 0 { + m.notifyBlockConnected(blockNotifications, + block) + } + + case *notificationBlockDisconnected: + m.notifyBlockDisconnected(blockNotifications, + (*btcutil.Block)(n)) + + case *notificationTxAcceptedByMempool: + if n.isNew && len(txNotifications) != 0 { + m.notifyForNewTx(txNotifications, n.tx) + } + m.notifyForTx(watchedOutPoints, watchedAddrs, n.tx, nil) + + case *notificationRegisterBlocks: + wsc := (*wsClient)(n) + blockNotifications[wsc.quit] = wsc + + case *notificationUnregisterBlocks: + wsc := (*wsClient)(n) + delete(blockNotifications, wsc.quit) + + case *notificationRegisterClient: + wsc := (*wsClient)(n) + clients[wsc.quit] = wsc + + case *notificationUnregisterClient: + wsc := (*wsClient)(n) + // Remove any requests made by the client as well as + // the client itself. + delete(blockNotifications, wsc.quit) + delete(txNotifications, wsc.quit) + for k := range wsc.spentRequests { + op := k + m.removeSpentRequest(watchedOutPoints, wsc, &op) + } + for addr := range wsc.addrRequests { + m.removeAddrRequest(watchedAddrs, wsc, addr) + } + delete(clients, wsc.quit) + + case *notificationRegisterSpent: + m.addSpentRequests(watchedOutPoints, n.wsc, n.ops) + + case *notificationUnregisterSpent: + m.removeSpentRequest(watchedOutPoints, n.wsc, n.op) + + case *notificationRegisterAddr: + m.addAddrRequests(watchedAddrs, n.wsc, n.addrs) + + case *notificationUnregisterAddr: + m.removeAddrRequest(watchedAddrs, n.wsc, n.addr) + + case *notificationRegisterNewMempoolTxs: + wsc := (*wsClient)(n) + txNotifications[wsc.quit] = wsc + + case *notificationUnregisterNewMempoolTxs: + wsc := (*wsClient)(n) + delete(txNotifications, wsc.quit) + + default: + rpcsLog.Warn("Unhandled notification type") + } + + case m.numClients <- len(clients): + + case <-m.quit: + // RPC server shutting down. + break out + } + } + + for _, c := range clients { + c.Disconnect() + } + m.wg.Done() +} + +// NumClients returns the number of clients actively being served. +func (m *wsNotificationManager) NumClients() (n int) { + select { + case n = <-m.numClients: + case <-m.quit: // Use default n (0) if server has shut down. + } + return +} + +// RegisterBlockUpdates requests block update notifications to the passed +// websocket client. +func (m *wsNotificationManager) RegisterBlockUpdates(wsc *wsClient) { + m.queueNotification <- (*notificationRegisterBlocks)(wsc) +} + +// UnregisterBlockUpdates removes block update notifications for the passed +// websocket client. +func (m *wsNotificationManager) UnregisterBlockUpdates(wsc *wsClient) { + m.queueNotification <- (*notificationUnregisterBlocks)(wsc) +} + +// notifyBlockConnected notifies websocket clients that have registered for +// block updates when a block is connected to the main chain. +func (*wsNotificationManager) notifyBlockConnected(clients map[chan struct{}]*wsClient, + block *btcutil.Block) { + + // Notify interested websocket clients about the connected block. + ntfn := btcjson.NewBlockConnectedNtfn(block.Sha().String(), + int32(block.Height()), block.MsgBlock().Header.Timestamp.Unix()) + marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn) + if err != nil { + rpcsLog.Error("Failed to marshal block connected notification: "+ + "%v", err) + return + } + for _, wsc := range clients { + wsc.QueueNotification(marshalledJSON) + } +} + +// notifyBlockDisconnected notifies websocket clients that have registered for +// block updates when a block is disconnected from the main chain (due to a +// reorganize). +func (*wsNotificationManager) notifyBlockDisconnected(clients map[chan struct{}]*wsClient, block *btcutil.Block) { + // Skip notification creation if no clients have requested block + // connected/disconnected notifications. + if len(clients) == 0 { + return + } + + // Notify interested websocket clients about the disconnected block. + ntfn := btcjson.NewBlockDisconnectedNtfn(block.Sha().String(), + int32(block.Height()), block.MsgBlock().Header.Timestamp.Unix()) + marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn) + if err != nil { + rpcsLog.Error("Failed to marshal block disconnected "+ + "notification: %v", err) + return + } + for _, wsc := range clients { + wsc.QueueNotification(marshalledJSON) + } +} + +// RegisterNewMempoolTxsUpdates requests notifications to the passed websocket +// client when new transactions are added to the memory pool. +func (m *wsNotificationManager) RegisterNewMempoolTxsUpdates(wsc *wsClient) { + m.queueNotification <- (*notificationRegisterNewMempoolTxs)(wsc) +} + +// UnregisterNewMempoolTxsUpdates removes notifications to the passed websocket +// client when new transaction are added to the memory pool. +func (m *wsNotificationManager) UnregisterNewMempoolTxsUpdates(wsc *wsClient) { + m.queueNotification <- (*notificationUnregisterNewMempoolTxs)(wsc) +} + +// notifyForNewTx notifies websocket clients that have registered for updates +// when a new transaction is added to the memory pool. +func (m *wsNotificationManager) notifyForNewTx(clients map[chan struct{}]*wsClient, tx *btcutil.Tx) { + txShaStr := tx.Sha().String() + mtx := tx.MsgTx() + + var amount int64 + for _, txOut := range mtx.TxOut { + amount += txOut.Value + } + + ntfn := btcjson.NewTxAcceptedNtfn(txShaStr, btcutil.Amount(amount).ToBTC()) + marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn) + if err != nil { + rpcsLog.Errorf("Failed to marshal tx notification: %s", err.Error()) + return + } + + var verboseNtfn *btcjson.TxAcceptedVerboseNtfn + var marshalledJSONVerbose []byte + for _, wsc := range clients { + if wsc.verboseTxUpdates { + if marshalledJSONVerbose != nil { + wsc.QueueNotification(marshalledJSONVerbose) + continue + } + + net := m.server.server.chainParams + rawTx, err := createTxRawResult(net, mtx, txShaStr, nil, + "", 0, 0) + if err != nil { + return + } + + verboseNtfn = btcjson.NewTxAcceptedVerboseNtfn(*rawTx) + marshalledJSONVerbose, err = btcjson.MarshalCmd(nil, + verboseNtfn) + if err != nil { + rpcsLog.Errorf("Failed to marshal verbose tx "+ + "notification: %s", err.Error()) + return + } + wsc.QueueNotification(marshalledJSONVerbose) + } else { + wsc.QueueNotification(marshalledJSON) + } + } +} + +// RegisterSpentRequests requests a notification when each of the passed +// outpoints is confirmed spent (contained in a block connected to the main +// chain) for the passed websocket client. The request is automatically +// removed once the notification has been sent. +func (m *wsNotificationManager) RegisterSpentRequests(wsc *wsClient, ops []*wire.OutPoint) { + m.queueNotification <- ¬ificationRegisterSpent{ + wsc: wsc, + ops: ops, + } +} + +// addSpentRequests modifies a map of watched outpoints to sets of websocket +// clients to add a new request watch all of the outpoints in ops and create +// and send a notification when spent to the websocket client wsc. +func (*wsNotificationManager) addSpentRequests(opMap map[wire.OutPoint]map[chan struct{}]*wsClient, + wsc *wsClient, ops []*wire.OutPoint) { + + for _, op := range ops { + // Track the request in the client as well so it can be quickly + // be removed on disconnect. + wsc.spentRequests[*op] = struct{}{} + + // Add the client to the list to notify when the outpoint is seen. + // Create the list as needed. + cmap, ok := opMap[*op] + if !ok { + cmap = make(map[chan struct{}]*wsClient) + opMap[*op] = cmap + } + cmap[wsc.quit] = wsc + } +} + +// UnregisterSpentRequest removes a request from the passed websocket client +// to be notified when the passed outpoint is confirmed spent (contained in a +// block connected to the main chain). +func (m *wsNotificationManager) UnregisterSpentRequest(wsc *wsClient, op *wire.OutPoint) { + m.queueNotification <- ¬ificationUnregisterSpent{ + wsc: wsc, + op: op, + } +} + +// removeSpentRequest modifies a map of watched outpoints to remove the +// websocket client wsc from the set of clients to be notified when a +// watched outpoint is spent. If wsc is the last client, the outpoint +// key is removed from the map. +func (*wsNotificationManager) removeSpentRequest(ops map[wire.OutPoint]map[chan struct{}]*wsClient, + wsc *wsClient, op *wire.OutPoint) { + + // Remove the request tracking from the client. + delete(wsc.spentRequests, *op) + + // Remove the client from the list to notify. + notifyMap, ok := ops[*op] + if !ok { + rpcsLog.Warnf("Attempt to remove nonexistent spent request "+ + "for websocket client %s", wsc.addr) + return + } + delete(notifyMap, wsc.quit) + + // Remove the map entry altogether if there are + // no more clients interested in it. + if len(notifyMap) == 0 { + delete(ops, *op) + } +} + +// txHexString returns the serialized transaction encoded in hexadecimal. +func txHexString(tx *btcutil.Tx) string { + buf := bytes.NewBuffer(make([]byte, 0, tx.MsgTx().SerializeSize())) + // Ignore Serialize's error, as writing to a bytes.buffer cannot fail. + tx.MsgTx().Serialize(buf) + return hex.EncodeToString(buf.Bytes()) +} + +// blockDetails creates a BlockDetails struct to include in btcws notifications +// from a block and a transaction's block index. +func blockDetails(block *btcutil.Block, txIndex int) *btcjson.BlockDetails { + if block == nil { + return nil + } + return &btcjson.BlockDetails{ + Height: int32(block.Height()), + Hash: block.Sha().String(), + Index: txIndex, + Time: block.MsgBlock().Header.Timestamp.Unix(), + } +} + +// newRedeemingTxNotification returns a new marshalled redeemingtx notification +// with the passed parameters. +func newRedeemingTxNotification(txHex string, index int, block *btcutil.Block) ([]byte, error) { + // Create and marshal the notification. + ntfn := btcjson.NewRedeemingTxNtfn(txHex, blockDetails(block, index)) + return btcjson.MarshalCmd(nil, ntfn) +} + +// notifyForTxOuts examines each transaction output, notifying interested +// websocket clients of the transaction if an output spends to a watched +// address. A spent notification request is automatically registered for +// the client for each matching output. +func (m *wsNotificationManager) notifyForTxOuts(ops map[wire.OutPoint]map[chan struct{}]*wsClient, + addrs map[string]map[chan struct{}]*wsClient, tx *btcutil.Tx, block *btcutil.Block) { + + // Nothing to do if nobody is listening for address notifications. + if len(addrs) == 0 { + return + } + + txHex := "" + wscNotified := make(map[chan struct{}]struct{}) + for i, txOut := range tx.MsgTx().TxOut { + _, txAddrs, _, err := txscript.ExtractPkScriptAddrs( + txOut.PkScript, m.server.server.chainParams) + if err != nil { + continue + } + + for _, txAddr := range txAddrs { + cmap, ok := addrs[txAddr.EncodeAddress()] + if !ok { + continue + } + + if txHex == "" { + txHex = txHexString(tx) + } + ntfn := btcjson.NewRecvTxNtfn(txHex, blockDetails(block, + tx.Index())) + + marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn) + if err != nil { + rpcsLog.Errorf("Failed to marshal processedtx notification: %v", err) + continue + } + + op := []*wire.OutPoint{wire.NewOutPoint(tx.Sha(), uint32(i))} + for wscQuit, wsc := range cmap { + m.addSpentRequests(ops, wsc, op) + + if _, ok := wscNotified[wscQuit]; !ok { + wscNotified[wscQuit] = struct{}{} + wsc.QueueNotification(marshalledJSON) + } + } + } + } +} + +// notifyForTx examines the inputs and outputs of the passed transaction, +// notifying websocket clients of outputs spending to a watched address +// and inputs spending a watched outpoint. +func (m *wsNotificationManager) notifyForTx(ops map[wire.OutPoint]map[chan struct{}]*wsClient, + addrs map[string]map[chan struct{}]*wsClient, tx *btcutil.Tx, block *btcutil.Block) { + + if len(ops) != 0 { + m.notifyForTxIns(ops, tx, block) + } + if len(addrs) != 0 { + m.notifyForTxOuts(ops, addrs, tx, block) + } +} + +// notifyForTxIns examines the inputs of the passed transaction and sends +// interested websocket clients a redeemingtx notification if any inputs +// spend a watched output. If block is non-nil, any matching spent +// requests are removed. +func (m *wsNotificationManager) notifyForTxIns(ops map[wire.OutPoint]map[chan struct{}]*wsClient, + tx *btcutil.Tx, block *btcutil.Block) { + + // Nothing to do if nobody is watching outpoints. + if len(ops) == 0 { + return + } + + txHex := "" + wscNotified := make(map[chan struct{}]struct{}) + for _, txIn := range tx.MsgTx().TxIn { + prevOut := &txIn.PreviousOutPoint + if cmap, ok := ops[*prevOut]; ok { + if txHex == "" { + txHex = txHexString(tx) + } + marshalledJSON, err := newRedeemingTxNotification(txHex, tx.Index(), block) + if err != nil { + rpcsLog.Warnf("Failed to marshal redeemingtx notification: %v", err) + continue + } + for wscQuit, wsc := range cmap { + if block != nil { + m.removeSpentRequest(ops, wsc, prevOut) + } + + if _, ok := wscNotified[wscQuit]; !ok { + wscNotified[wscQuit] = struct{}{} + wsc.QueueNotification(marshalledJSON) + } + } + } + } +} + +// RegisterTxOutAddressRequests requests notifications to the passed websocket +// client when a transaction output spends to the passed address. +func (m *wsNotificationManager) RegisterTxOutAddressRequests(wsc *wsClient, addrs []string) { + m.queueNotification <- ¬ificationRegisterAddr{ + wsc: wsc, + addrs: addrs, + } +} + +// addAddrRequests adds the websocket client wsc to the address to client set +// addrMap so wsc will be notified for any mempool or block transaction outputs +// spending to any of the addresses in addrs. +func (*wsNotificationManager) addAddrRequests(addrMap map[string]map[chan struct{}]*wsClient, + wsc *wsClient, addrs []string) { + + for _, addr := range addrs { + // Track the request in the client as well so it can be quickly be + // removed on disconnect. + wsc.addrRequests[addr] = struct{}{} + + // Add the client to the set of clients to notify when the + // outpoint is seen. Create map as needed. + cmap, ok := addrMap[addr] + if !ok { + cmap = make(map[chan struct{}]*wsClient) + addrMap[addr] = cmap + } + cmap[wsc.quit] = wsc + } +} + +// UnregisterTxOutAddressRequest removes a request from the passed websocket +// client to be notified when a transaction spends to the passed address. +func (m *wsNotificationManager) UnregisterTxOutAddressRequest(wsc *wsClient, addr string) { + m.queueNotification <- ¬ificationUnregisterAddr{ + wsc: wsc, + addr: addr, + } +} + +// removeAddrRequest removes the websocket client wsc from the address to +// client set addrs so it will no longer receive notification updates for +// any transaction outputs send to addr. +func (*wsNotificationManager) removeAddrRequest(addrs map[string]map[chan struct{}]*wsClient, + wsc *wsClient, addr string) { + + // Remove the request tracking from the client. + delete(wsc.addrRequests, addr) + + // Remove the client from the list to notify. + cmap, ok := addrs[addr] + if !ok { + rpcsLog.Warnf("Attempt to remove nonexistent addr request "+ + "<%s> for websocket client %s", addr, wsc.addr) + return + } + delete(cmap, wsc.quit) + + // Remove the map entry altogether if there are no more clients + // interested in it. + if len(cmap) == 0 { + delete(addrs, addr) + } +} + +// AddClient adds the passed websocket client to the notification manager. +func (m *wsNotificationManager) AddClient(wsc *wsClient) { + m.queueNotification <- (*notificationRegisterClient)(wsc) +} + +// RemoveClient removes the passed websocket client and all notifications +// registered for it. +func (m *wsNotificationManager) RemoveClient(wsc *wsClient) { + select { + case m.queueNotification <- (*notificationUnregisterClient)(wsc): + case <-m.quit: + } +} + +// Start starts the goroutines required for the manager to queue and process +// websocket client notifications. +func (m *wsNotificationManager) Start() { + m.wg.Add(2) + go m.queueHandler() + go m.notificationHandler() +} + +// WaitForShutdown blocks until all notification manager goroutines have +// finished. +func (m *wsNotificationManager) WaitForShutdown() { + m.wg.Wait() +} + +// Shutdown shuts down the manager, stopping the notification queue and +// notification handler goroutines. +func (m *wsNotificationManager) Shutdown() { + close(m.quit) +} + +// newWsNotificationManager returns a new notification manager ready for use. +// See wsNotificationManager for more details. +func newWsNotificationManager(server *rpcServer) *wsNotificationManager { + return &wsNotificationManager{ + server: server, + queueNotification: make(chan interface{}), + notificationMsgs: make(chan interface{}), + numClients: make(chan int), + quit: make(chan struct{}), + } +} + +// wsResponse houses a message to send to a connected websocket client as +// well as a channel to reply on when the message is sent. +type wsResponse struct { + msg []byte + doneChan chan bool +} + +// wsClient provides an abstraction for handling a websocket client. The +// overall data flow is split into 3 main goroutines, a possible 4th goroutine +// for long-running operations (only started if request is made), and a +// websocket manager which is used to allow things such as broadcasting +// requested notifications to all connected websocket clients. Inbound +// messages are read via the inHandler goroutine and generally dispatched to +// their own handler. However, certain potentially long-running operations such +// as rescans, are sent to the asyncHander goroutine and are limited to one at a +// time. There are two outbound message types - one for responding to client +// requests and another for async notifications. Responses to client requests +// use SendMessage which employs a buffered channel thereby limiting the number +// of outstanding requests that can be made. Notifications are sent via +// QueueNotification which implements a queue via notificationQueueHandler to +// ensure sending notifications from other subsystems can't block. Ultimately, +// all messages are sent via the outHandler. +type wsClient struct { + sync.Mutex + + // server is the RPC server that is servicing the client. + server *rpcServer + + // conn is the underlying websocket connection. + conn *websocket.Conn + + // disconnected indicated whether or not the websocket client is + // disconnected. + disconnected bool + + // addr is the remote address of the client. + addr string + + // authenticated specifies whether a client has been authenticated + // and therefore is allowed to communicated over the websocket. + authenticated bool + + // isAdmin specifies whether a client may change the state of the server; + // false means its access is only to the limited set of RPC calls. + isAdmin bool + + // sessionID is a random ID generated for each client when connected. + // These IDs may be queried by a client using the session RPC. A change + // to the session ID indicates that the client reconnected. + sessionID uint64 + + // verboseTxUpdates specifies whether a client has requested verbose + // information about all new transactions. + verboseTxUpdates bool + + // addrRequests is a set of addresses the caller has requested to be + // notified about. It is maintained here so all requests can be removed + // when a wallet disconnects. Owned by the notification manager. + addrRequests map[string]struct{} + + // spentRequests is a set of unspent Outpoints a wallet has requested + // notifications for when they are spent by a processed transaction. + // Owned by the notification manager. + spentRequests map[wire.OutPoint]struct{} + + // Networking infrastructure. + asyncStarted bool + asyncChan chan *parsedRPCCmd + ntfnChan chan []byte + sendChan chan wsResponse + quit chan struct{} + wg sync.WaitGroup +} + +// handleMessage is the main handler for incoming requests. It enforces +// authentication, parses the incoming json, looks up and executes handlers +// (including pass through for standard RPC commands), and sends the appropriate +// response. It also detects commands which are marked as long-running and +// sends them off to the asyncHander for processing. +func (c *wsClient) handleMessage(msg []byte) { + if !c.authenticated { + // Disconnect immediately if the provided command fails to + // parse when the client is not already authenticated. + var request btcjson.Request + if err := json.Unmarshal(msg, &request); err != nil { + c.Disconnect() + return + } + parsedCmd := parseCmd(&request) + if parsedCmd.err != nil { + c.Disconnect() + return + } + + // Disconnect immediately if the first command is not + // authenticate when not already authenticated. + authCmd, ok := parsedCmd.cmd.(*btcjson.AuthenticateCmd) + if !ok { + rpcsLog.Warnf("Unauthenticated websocket message " + + "received") + c.Disconnect() + return + } + + // Check credentials. + login := authCmd.Username + ":" + authCmd.Passphrase + auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) + authSha := fastsha256.Sum256([]byte(auth)) + cmp := subtle.ConstantTimeCompare(authSha[:], c.server.authsha[:]) + limitcmp := subtle.ConstantTimeCompare(authSha[:], c.server.limitauthsha[:]) + if cmp != 1 && limitcmp != 1 { + rpcsLog.Warnf("Auth failure.") + c.Disconnect() + return + } + c.authenticated = true + c.isAdmin = cmp == 1 + + // Marshal and send response. + reply, err := createMarshalledReply(parsedCmd.id, nil, nil) + if err != nil { + rpcsLog.Errorf("Failed to marshal authenticate reply: "+ + "%v", err.Error()) + return + } + c.SendMessage(reply, nil) + return + } + + // Attempt to parse the raw message into a JSON-RPC request. + var request btcjson.Request + if err := json.Unmarshal(msg, &request); err != nil { + jsonErr := &btcjson.RPCError{ + Code: btcjson.ErrRPCParse.Code, + Message: "Failed to parse request: " + err.Error(), + } + + // Marshal and send response. + reply, err := createMarshalledReply(nil, nil, jsonErr) + if err != nil { + rpcsLog.Errorf("Failed to marshal parse failure "+ + "reply: %v", err) + return + } + c.SendMessage(reply, nil) + return + } + // Requests with no ID (notifications) must not have a response per the + // JSON-RPC spec. + if request.ID == nil { + return + } + + // Check if the user is limited and disconnect client if unauthorized + if !c.isAdmin { + if _, ok := rpcLimited[request.Method]; !ok { + jsonErr := &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParams.Code, + Message: "limited user not authorized for this method", + } + // Marshal and send response. + reply, err := createMarshalledReply(request.ID, nil, jsonErr) + if err != nil { + rpcsLog.Errorf("Failed to marshal parse failure "+ + "reply: %v", err) + return + } + c.SendMessage(reply, nil) + return + } + } + + // Attempt to parse the JSON-RPC request into a known concrete command. + cmd := parseCmd(&request) + if cmd.err != nil { + // Marshal and send response. + reply, err := createMarshalledReply(cmd.id, nil, cmd.err) + if err != nil { + rpcsLog.Errorf("Failed to marshal parse failure "+ + "reply: %v", err) + return + } + c.SendMessage(reply, nil) + return + } + rpcsLog.Debugf("Received command <%s> from %s", cmd.method, c.addr) + + // Disconnect if already authenticated and another authenticate command + // is received. + if _, ok := cmd.cmd.(*btcjson.AuthenticateCmd); ok { + rpcsLog.Warnf("Websocket client %s is already authenticated", + c.addr) + c.Disconnect() + return + } + + // When the command is marked as a long-running command, send it off + // to the asyncHander goroutine for processing. + if _, ok := wsAsyncHandlers[cmd.method]; ok { + // Start up the async goroutine for handling long-running + // requests asynchonrously if needed. + if !c.asyncStarted { + rpcsLog.Tracef("Starting async handler for %s", c.addr) + c.wg.Add(1) + go c.asyncHandler() + c.asyncStarted = true + } + c.asyncChan <- cmd + return + } + + // Lookup the websocket extension for the command and if it doesn't + // exist fallback to handling the command as a standard command. + wsHandler, ok := wsHandlers[cmd.method] + if !ok { + // No websocket-specific handler so handle like a legacy + // RPC connection. + result, jsonErr := c.server.standardCmdResult(cmd, nil) + reply, err := createMarshalledReply(cmd.id, result, jsonErr) + if err != nil { + rpcsLog.Errorf("Failed to marshal reply for <%s> "+ + "command: %v", cmd.method, err) + return + } + + c.SendMessage(reply, nil) + return + } + + // Invoke the handler and marshal and send response. + result, jsonErr := wsHandler(c, cmd.cmd) + reply, err := createMarshalledReply(cmd.id, result, jsonErr) + if err != nil { + rpcsLog.Errorf("Failed to marshal reply for <%s> command: %v", + cmd.method, err) + return + } + c.SendMessage(reply, nil) +} + +// inHandler handles all incoming messages for the websocket connection. It +// must be run as a goroutine. +func (c *wsClient) inHandler() { +out: + for { + // Break out of the loop once the quit channel has been closed. + // Use a non-blocking select here so we fall through otherwise. + select { + case <-c.quit: + break out + default: + } + + _, msg, err := c.conn.ReadMessage() + if err != nil { + // Log the error if it's not due to disconnecting. + if err != io.EOF { + rpcsLog.Errorf("Websocket receive error from "+ + "%s: %v", c.addr, err) + } + break out + } + c.handleMessage(msg) + } + + // Ensure the connection is closed. + c.Disconnect() + c.wg.Done() + rpcsLog.Tracef("Websocket client input handler done for %s", c.addr) +} + +// notificationQueueHandler handles the queuing of outgoing notifications for +// the websocket client. This runs as a muxer for various sources of input to +// ensure that queuing up notifications to be sent will not block. Otherwise, +// slow clients could bog down the other systems (such as the mempool or block +// manager) which are queuing the data. The data is passed on to outHandler to +// actually be written. It must be run as a goroutine. +func (c *wsClient) notificationQueueHandler() { + ntfnSentChan := make(chan bool, 1) // nonblocking sync + + // pendingNtfns is used as a queue for notifications that are ready to + // be sent once there are no outstanding notifications currently being + // sent. The waiting flag is used over simply checking for items in the + // pending list to ensure cleanup knows what has and hasn't been sent + // to the outHandler. Currently no special cleanup is needed, however + // if something like a done channel is added to notifications in the + // future, not knowing what has and hasn't been sent to the outHandler + // (and thus who should respond to the done channel) would be + // problematic without using this approach. + pendingNtfns := list.New() + waiting := false +out: + for { + select { + // This channel is notified when a message is being queued to + // be sent across the network socket. It will either send the + // message immediately if a send is not already in progress, or + // queue the message to be sent once the other pending messages + // are sent. + case msg := <-c.ntfnChan: + if !waiting { + c.SendMessage(msg, ntfnSentChan) + } else { + pendingNtfns.PushBack(msg) + } + waiting = true + + // This channel is notified when a notification has been sent + // across the network socket. + case <-ntfnSentChan: + // No longer waiting if there are no more messages in + // the pending messages queue. + next := pendingNtfns.Front() + if next == nil { + waiting = false + continue + } + + // Notify the outHandler about the next item to + // asynchronously send. + msg := pendingNtfns.Remove(next).([]byte) + c.SendMessage(msg, ntfnSentChan) + + case <-c.quit: + break out + } + } + + // Drain any wait channels before exiting so nothing is left waiting + // around to send. +cleanup: + for { + select { + case <-c.ntfnChan: + case <-ntfnSentChan: + default: + break cleanup + } + } + c.wg.Done() + rpcsLog.Tracef("Websocket client notification queue handler done "+ + "for %s", c.addr) +} + +// outHandler handles all outgoing messages for the websocket connection. It +// must be run as a goroutine. It uses a buffered channel to serialize output +// messages while allowing the sender to continue running asynchronously. It +// must be run as a goroutine. +func (c *wsClient) outHandler() { +out: + for { + // Send any messages ready for send until the quit channel is + // closed. + select { + case r := <-c.sendChan: + err := c.conn.WriteMessage(websocket.TextMessage, r.msg) + if err != nil { + c.Disconnect() + break out + } + if r.doneChan != nil { + r.doneChan <- true + } + + case <-c.quit: + break out + } + } + + // Drain any wait channels before exiting so nothing is left waiting + // around to send. +cleanup: + for { + select { + case r := <-c.sendChan: + if r.doneChan != nil { + r.doneChan <- false + } + default: + break cleanup + } + } + c.wg.Done() + rpcsLog.Tracef("Websocket client output handler done for %s", c.addr) +} + +// asyncHandler handles all long-running requests such as rescans which are +// not run directly in the inHandler routine unlike most requests. This allows +// normal quick requests to continue to be processed and responded to even while +// lengthy operations are underway. Only one long-running operation is +// permitted at a time, so multiple long-running requests are queued and +// serialized. It must be run as a goroutine. Also, this goroutine is not +// started until/if the first long-running request is made. +func (c *wsClient) asyncHandler() { + asyncHandlerDoneChan := make(chan struct{}, 1) // nonblocking sync + pendingCmds := list.New() + waiting := false + + // runHandler runs the handler for the passed command and sends the + // reply. + runHandler := func(parsedCmd *parsedRPCCmd) { + wsHandler, ok := wsHandlers[parsedCmd.method] + if !ok { + rpcsLog.Warnf("No handler for command <%s>", + parsedCmd.method) + return + } + + // Invoke the handler and marshal and send response. + result, jsonErr := wsHandler(c, parsedCmd.cmd) + reply, err := createMarshalledReply(parsedCmd.id, result, + jsonErr) + if err != nil { + rpcsLog.Errorf("Failed to marshal reply for <%s> "+ + "command: %v", parsedCmd.method, err) + return + } + c.SendMessage(reply, nil) + } + +out: + for { + select { + case cmd := <-c.asyncChan: + if !waiting { + c.wg.Add(1) + go func(cmd *parsedRPCCmd) { + runHandler(cmd) + asyncHandlerDoneChan <- struct{}{} + c.wg.Done() + }(cmd) + } else { + pendingCmds.PushBack(cmd) + } + waiting = true + + case <-asyncHandlerDoneChan: + // No longer waiting if there are no more messages in + // the pending messages queue. + next := pendingCmds.Front() + if next == nil { + waiting = false + continue + } + + // Notify the outHandler about the next item to + // asynchronously send. + element := pendingCmds.Remove(next) + c.wg.Add(1) + go func(cmd *parsedRPCCmd) { + runHandler(cmd) + asyncHandlerDoneChan <- struct{}{} + c.wg.Done() + }(element.(*parsedRPCCmd)) + + case <-c.quit: + break out + } + } + + // Drain any wait channels before exiting so nothing is left waiting + // around to send. +cleanup: + for { + select { + case <-c.asyncChan: + case <-asyncHandlerDoneChan: + default: + break cleanup + } + } + + c.wg.Done() + rpcsLog.Tracef("Websocket client async handler done for %s", c.addr) +} + +// SendMessage sends the passed json to the websocket client. It is backed +// by a buffered channel, so it will not block until the send channel is full. +// Note however that QueueNotification must be used for sending async +// notifications instead of the this function. This approach allows a limit to +// the number of outstanding requests a client can make without preventing or +// blocking on async notifications. +func (c *wsClient) SendMessage(marshalledJSON []byte, doneChan chan bool) { + // Don't send the message if disconnected. + if c.Disconnected() { + if doneChan != nil { + doneChan <- false + } + return + } + + c.sendChan <- wsResponse{msg: marshalledJSON, doneChan: doneChan} +} + +// ErrClientQuit describes the error where a client send is not processed due +// to the client having already been disconnected or dropped. +var ErrClientQuit = errors.New("client quit") + +// QueueNotification queues the passed notification to be sent to the websocket +// client. This function, as the name implies, is only intended for +// notifications since it has additional logic to prevent other subsystems, such +// as the memory pool and block manager, from blocking even when the send +// channel is full. +// +// If the client is in the process of shutting down, this function returns +// ErrClientQuit. This is intended to be checked by long-running notification +// handlers to stop processing if there is no more work needed to be done. +func (c *wsClient) QueueNotification(marshalledJSON []byte) error { + // Don't queue the message if disconnected. + if c.Disconnected() { + return ErrClientQuit + } + + c.ntfnChan <- marshalledJSON + return nil +} + +// Disconnected returns whether or not the websocket client is disconnected. +func (c *wsClient) Disconnected() bool { + c.Lock() + defer c.Unlock() + + return c.disconnected +} + +// Disconnect disconnects the websocket client. +func (c *wsClient) Disconnect() { + c.Lock() + defer c.Unlock() + + // Nothing to do if already disconnected. + if c.disconnected { + return + } + + rpcsLog.Tracef("Disconnecting websocket client %s", c.addr) + close(c.quit) + c.conn.Close() + c.disconnected = true +} + +// Start begins processing input and output messages. +func (c *wsClient) Start() { + rpcsLog.Tracef("Starting websocket client %s", c.addr) + + // Start processing input and output. + c.wg.Add(3) + go c.inHandler() + go c.notificationQueueHandler() + go c.outHandler() +} + +// WaitForShutdown blocks until the websocket client goroutines are stopped +// and the connection is closed. +func (c *wsClient) WaitForShutdown() { + c.wg.Wait() +} + +// newWebsocketClient returns a new websocket client given the notification +// manager, websocket connection, remote address, and whether or not the client +// has already been authenticated (via HTTP Basic access authentication). The +// returned client is ready to start. Once started, the client will process +// incoming and outgoing messages in separate goroutines complete with queuing +// and asynchrous handling for long-running operations. +func newWebsocketClient(server *rpcServer, conn *websocket.Conn, + remoteAddr string, authenticated bool, isAdmin bool) (*wsClient, error) { + + sessionID, err := wire.RandomUint64() + if err != nil { + return nil, err + } + + client := &wsClient{ + conn: conn, + addr: remoteAddr, + authenticated: authenticated, + isAdmin: isAdmin, + sessionID: sessionID, + server: server, + addrRequests: make(map[string]struct{}), + spentRequests: make(map[wire.OutPoint]struct{}), + ntfnChan: make(chan []byte, 1), // nonblocking sync + asyncChan: make(chan *parsedRPCCmd, 1), // nonblocking sync + sendChan: make(chan wsResponse, websocketSendBufferSize), + quit: make(chan struct{}), + } + return client, nil +} + +// handleWebsocketHelp implements the help command for websocket connections. +func handleWebsocketHelp(wsc *wsClient, icmd interface{}) (interface{}, error) { + cmd, ok := icmd.(*btcjson.HelpCmd) + if !ok { + return nil, btcjson.ErrRPCInternal + } + + // Provide a usage overview of all commands when no specific command + // was specified. + var command string + if cmd.Command != nil { + command = *cmd.Command + } + if command == "" { + usage, err := wsc.server.helpCacher.rpcUsage(true) + if err != nil { + context := "Failed to generate RPC usage" + return nil, internalRPCError(err.Error(), context) + } + return usage, nil + } + + // Check that the command asked for is supported and implemented. + // Search the list of websocket handlers as well as the main list of + // handlers since help should only be provided for those cases. + valid := true + if _, ok := rpcHandlers[command]; !ok { + if _, ok := wsHandlers[command]; !ok { + valid = false + } + } + if !valid { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: "Unknown command: " + command, + } + } + + // Get the help for the command. + help, err := wsc.server.helpCacher.rpcMethodHelp(command) + if err != nil { + context := "Failed to generate help" + return nil, internalRPCError(err.Error(), context) + } + return help, nil +} + +// handleNotifyBlocks implements the notifyblocks command extension for +// websocket connections. +func handleNotifyBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) { + wsc.server.ntfnMgr.RegisterBlockUpdates(wsc) + return nil, nil +} + +// handleSession implements the session command extension for websocket +// connections. +func handleSession(wsc *wsClient, icmd interface{}) (interface{}, error) { + return &btcjson.SessionResult{SessionID: wsc.sessionID}, nil +} + +// handleStopNotifyBlocks implements the stopnotifyblocks command extension for +// websocket connections. +func handleStopNotifyBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) { + wsc.server.ntfnMgr.UnregisterBlockUpdates(wsc) + return nil, nil +} + +// handleNotifySpent implements the notifyspent command extension for +// websocket connections. +func handleNotifySpent(wsc *wsClient, icmd interface{}) (interface{}, error) { + cmd, ok := icmd.(*btcjson.NotifySpentCmd) + if !ok { + return nil, btcjson.ErrRPCInternal + } + + outpoints, err := deserializeOutpoints(cmd.OutPoints) + if err != nil { + return nil, err + } + + wsc.server.ntfnMgr.RegisterSpentRequests(wsc, outpoints) + return nil, nil +} + +// handleNotifyNewTransations implements the notifynewtransactions command +// extension for websocket connections. +func handleNotifyNewTransactions(wsc *wsClient, icmd interface{}) (interface{}, error) { + cmd, ok := icmd.(*btcjson.NotifyNewTransactionsCmd) + if !ok { + return nil, btcjson.ErrRPCInternal + } + + wsc.verboseTxUpdates = cmd.Verbose != nil && *cmd.Verbose + wsc.server.ntfnMgr.RegisterNewMempoolTxsUpdates(wsc) + return nil, nil +} + +// handleStopNotifyNewTransations implements the stopnotifynewtransactions +// command extension for websocket connections. +func handleStopNotifyNewTransactions(wsc *wsClient, icmd interface{}) (interface{}, error) { + wsc.server.ntfnMgr.UnregisterNewMempoolTxsUpdates(wsc) + return nil, nil +} + +// handleNotifyReceived implements the notifyreceived command extension for +// websocket connections. +func handleNotifyReceived(wsc *wsClient, icmd interface{}) (interface{}, error) { + cmd, ok := icmd.(*btcjson.NotifyReceivedCmd) + if !ok { + return nil, btcjson.ErrRPCInternal + } + + // Decode addresses to validate input, but the strings slice is used + // directly if these are all ok. + err := checkAddressValidity(cmd.Addresses) + if err != nil { + return nil, err + } + + wsc.server.ntfnMgr.RegisterTxOutAddressRequests(wsc, cmd.Addresses) + return nil, nil +} + +// handleStopNotifySpent implements the stopnotifyspent command extension for +// websocket connections. +func handleStopNotifySpent(wsc *wsClient, icmd interface{}) (interface{}, error) { + cmd, ok := icmd.(*btcjson.StopNotifySpentCmd) + if !ok { + return nil, btcjson.ErrRPCInternal + } + + outpoints, err := deserializeOutpoints(cmd.OutPoints) + if err != nil { + return nil, err + } + + for _, outpoint := range outpoints { + wsc.server.ntfnMgr.UnregisterSpentRequest(wsc, outpoint) + } + + return nil, nil +} + +// handleStopNotifyReceived implements the stopnotifyreceived command extension +// for websocket connections. +func handleStopNotifyReceived(wsc *wsClient, icmd interface{}) (interface{}, error) { + cmd, ok := icmd.(*btcjson.StopNotifyReceivedCmd) + if !ok { + return nil, btcjson.ErrRPCInternal + } + + // Decode addresses to validate input, but the strings slice is used + // directly if these are all ok. + err := checkAddressValidity(cmd.Addresses) + if err != nil { + return nil, err + } + + for _, addr := range cmd.Addresses { + wsc.server.ntfnMgr.UnregisterTxOutAddressRequest(wsc, addr) + } + + return nil, nil +} + +// checkAddressValidity checks the validity of each address in the passed +// string slice. It does this by attempting to decode each address using the +// current active network parameters. If any single address fails to decode +// properly, the function returns an error. Otherwise, nil is returned. +func checkAddressValidity(addrs []string) error { + for _, addr := range addrs { + _, err := btcutil.DecodeAddress(addr, activeNetParams.Params) + if err != nil { + return &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidAddressOrKey, + Message: fmt.Sprintf("Invalid address or key: %v", + addr), + } + } + } + return nil +} + +// deserializeOutpoints deserializes each serialized outpoint. +func deserializeOutpoints(serializedOuts []btcjson.OutPoint) ([]*wire.OutPoint, error) { + outpoints := make([]*wire.OutPoint, 0, len(serializedOuts)) + for i := range serializedOuts { + blockHash, err := wire.NewShaHashFromStr(serializedOuts[i].Hash) + if err != nil { + return nil, rpcDecodeHexError(serializedOuts[i].Hash) + } + index := serializedOuts[i].Index + outpoints = append(outpoints, wire.NewOutPoint(blockHash, index)) + } + + return outpoints, nil +} + +type rescanKeys struct { + fallbacks map[string]struct{} + pubKeyHashes map[[ripemd160.Size]byte]struct{} + scriptHashes map[[ripemd160.Size]byte]struct{} + compressedPubKeys map[[33]byte]struct{} + uncompressedPubKeys map[[65]byte]struct{} + unspent map[wire.OutPoint]struct{} +} + +// unspentSlice returns a slice of currently-unspent outpoints for the rescan +// lookup keys. This is primarily intended to be used to register outpoints +// for continuous notifications after a rescan has completed. +func (r *rescanKeys) unspentSlice() []*wire.OutPoint { + ops := make([]*wire.OutPoint, 0, len(r.unspent)) + for op := range r.unspent { + opCopy := op + ops = append(ops, &opCopy) + } + return ops +} + +// ErrRescanReorg defines the error that is returned when an unrecoverable +// reorganize is detected during a rescan. +var ErrRescanReorg = btcjson.RPCError{ + Code: btcjson.ErrRPCDatabase, + Message: "Reorganize", +} + +// rescanBlock rescans all transactions in a single block. This is a helper +// function for handleRescan. +func rescanBlock(wsc *wsClient, lookups *rescanKeys, blk *btcutil.Block) { + for _, tx := range blk.Transactions() { + // Hexadecimal representation of this tx. Only created if + // needed, and reused for later notifications if already made. + var txHex string + + // All inputs and outputs must be iterated through to correctly + // modify the unspent map, however, just a single notification + // for any matching transaction inputs or outputs should be + // created and sent. + spentNotified := false + recvNotified := false + + for _, txin := range tx.MsgTx().TxIn { + if _, ok := lookups.unspent[txin.PreviousOutPoint]; ok { + delete(lookups.unspent, txin.PreviousOutPoint) + + if spentNotified { + continue + } + + if txHex == "" { + txHex = txHexString(tx) + } + marshalledJSON, err := newRedeemingTxNotification(txHex, tx.Index(), blk) + if err != nil { + rpcsLog.Errorf("Failed to marshal redeemingtx notification: %v", err) + continue + } + + err = wsc.QueueNotification(marshalledJSON) + // Stop the rescan early if the websocket client + // disconnected. + if err == ErrClientQuit { + return + } + spentNotified = true + } + } + + for txOutIdx, txout := range tx.MsgTx().TxOut { + _, addrs, _, _ := txscript.ExtractPkScriptAddrs( + txout.PkScript, wsc.server.server.chainParams) + + for _, addr := range addrs { + switch a := addr.(type) { + case *btcutil.AddressPubKeyHash: + if _, ok := lookups.pubKeyHashes[*a.Hash160()]; !ok { + continue + } + + case *btcutil.AddressScriptHash: + if _, ok := lookups.scriptHashes[*a.Hash160()]; !ok { + continue + } + + case *btcutil.AddressPubKey: + found := false + switch sa := a.ScriptAddress(); len(sa) { + case 33: // Compressed + var key [33]byte + copy(key[:], sa) + if _, ok := lookups.compressedPubKeys[key]; ok { + found = true + } + + case 65: // Uncompressed + var key [65]byte + copy(key[:], sa) + if _, ok := lookups.uncompressedPubKeys[key]; ok { + found = true + } + + default: + rpcsLog.Warnf("Skipping rescanned pubkey of unknown "+ + "serialized length %d", len(sa)) + continue + } + + // If the transaction output pays to the pubkey of + // a rescanned P2PKH address, include it as well. + if !found { + pkh := a.AddressPubKeyHash() + if _, ok := lookups.pubKeyHashes[*pkh.Hash160()]; !ok { + continue + } + } + + default: + // A new address type must have been added. Encode as a + // payment address string and check the fallback map. + addrStr := addr.EncodeAddress() + _, ok := lookups.fallbacks[addrStr] + if !ok { + continue + } + } + + outpoint := wire.OutPoint{ + Hash: *tx.Sha(), + Index: uint32(txOutIdx), + } + lookups.unspent[outpoint] = struct{}{} + + if recvNotified { + continue + } + + if txHex == "" { + txHex = txHexString(tx) + } + ntfn := btcjson.NewRecvTxNtfn(txHex, + blockDetails(blk, tx.Index())) + + marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn) + if err != nil { + rpcsLog.Errorf("Failed to marshal recvtx notification: %v", err) + return + } + + err = wsc.QueueNotification(marshalledJSON) + // Stop the rescan early if the websocket client + // disconnected. + if err == ErrClientQuit { + return + } + recvNotified = true + } + } + } +} + +// recoverFromReorg attempts to recover from a detected reorganize during a +// rescan. It fetches a new range of block shas from the database and +// verifies that the new range of blocks is on the same fork as a previous +// range of blocks. If this condition does not hold true, the JSON-RPC error +// for an unrecoverable reorganize is returned. +func recoverFromReorg(chain *blockchain.BlockChain, minBlock, maxBlock int32, + lastBlock *wire.ShaHash) ([]wire.ShaHash, error) { + + hashList, err := chain.HeightRange(minBlock, maxBlock) + if err != nil { + rpcsLog.Errorf("Error looking up block range: %v", err) + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCDatabase, + Message: "Database error: " + err.Error(), + } + } + if lastBlock == nil || len(hashList) == 0 { + return hashList, nil + } + + blk, err := chain.BlockByHash(&hashList[0]) + if err != nil { + rpcsLog.Errorf("Error looking up possibly reorged block: %v", + err) + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCDatabase, + Message: "Database error: " + err.Error(), + } + } + jsonErr := descendantBlock(lastBlock, blk) + if jsonErr != nil { + return nil, jsonErr + } + return hashList, nil +} + +// descendantBlock returns the appropriate JSON-RPC error if a current block +// fetched during a reorganize is not a direct child of the parent block hash. +func descendantBlock(prevHash *wire.ShaHash, curBlock *btcutil.Block) error { + curHash := &curBlock.MsgBlock().Header.PrevBlock + if !prevHash.IsEqual(curHash) { + rpcsLog.Errorf("Stopping rescan for reorged block %v "+ + "(replaced by block %v)", prevHash, curHash) + return &ErrRescanReorg + } + return nil +} + +// handleRescan implements the rescan command extension for websocket +// connections. +// +// NOTE: This does not smartly handle reorgs, and fixing requires database +// changes (for safe, concurrent access to full block ranges, and support +// for other chains than the best chain). It will, however, detect whether +// a reorg removed a block that was previously processed, and result in the +// handler erroring. Clients must handle this by finding a block still in +// the chain (perhaps from a rescanprogress notification) to resume their +// rescan. +func handleRescan(wsc *wsClient, icmd interface{}) (interface{}, error) { + cmd, ok := icmd.(*btcjson.RescanCmd) + if !ok { + return nil, btcjson.ErrRPCInternal + } + + outpoints := make([]*wire.OutPoint, 0, len(cmd.OutPoints)) + for i := range cmd.OutPoints { + cmdOutpoint := &cmd.OutPoints[i] + blockHash, err := wire.NewShaHashFromStr(cmdOutpoint.Hash) + if err != nil { + return nil, rpcDecodeHexError(cmdOutpoint.Hash) + } + outpoint := wire.NewOutPoint(blockHash, cmdOutpoint.Index) + outpoints = append(outpoints, outpoint) + } + + numAddrs := len(cmd.Addresses) + if numAddrs == 1 { + rpcsLog.Info("Beginning rescan for 1 address") + } else { + rpcsLog.Infof("Beginning rescan for %d addresses", numAddrs) + } + + // Build lookup maps. + lookups := rescanKeys{ + fallbacks: map[string]struct{}{}, + pubKeyHashes: map[[ripemd160.Size]byte]struct{}{}, + scriptHashes: map[[ripemd160.Size]byte]struct{}{}, + compressedPubKeys: map[[33]byte]struct{}{}, + uncompressedPubKeys: map[[65]byte]struct{}{}, + unspent: map[wire.OutPoint]struct{}{}, + } + var compressedPubkey [33]byte + var uncompressedPubkey [65]byte + for _, addrStr := range cmd.Addresses { + addr, err := btcutil.DecodeAddress(addrStr, activeNetParams.Params) + if err != nil { + jsonErr := btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidAddressOrKey, + Message: "Rescan address " + addrStr + ": " + + err.Error(), + } + return nil, &jsonErr + } + switch a := addr.(type) { + case *btcutil.AddressPubKeyHash: + lookups.pubKeyHashes[*a.Hash160()] = struct{}{} + + case *btcutil.AddressScriptHash: + lookups.scriptHashes[*a.Hash160()] = struct{}{} + + case *btcutil.AddressPubKey: + pubkeyBytes := a.ScriptAddress() + switch len(pubkeyBytes) { + case 33: // Compressed + copy(compressedPubkey[:], pubkeyBytes) + lookups.compressedPubKeys[compressedPubkey] = struct{}{} + + case 65: // Uncompressed + copy(uncompressedPubkey[:], pubkeyBytes) + lookups.uncompressedPubKeys[uncompressedPubkey] = struct{}{} + + default: + jsonErr := btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidAddressOrKey, + Message: "Pubkey " + addrStr + " is of unknown length", + } + return nil, &jsonErr + } + + default: + // A new address type must have been added. Use encoded + // payment address string as a fallback until a fast path + // is added. + lookups.fallbacks[addrStr] = struct{}{} + } + } + for _, outpoint := range outpoints { + lookups.unspent[*outpoint] = struct{}{} + } + + chain := wsc.server.chain + + minBlockHash, err := wire.NewShaHashFromStr(cmd.BeginBlock) + if err != nil { + return nil, rpcDecodeHexError(cmd.BeginBlock) + } + minBlock, err := chain.BlockHeightByHash(minBlockHash) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCBlockNotFound, + Message: "Error getting block: " + err.Error(), + } + } + + maxBlock := int32(math.MaxInt32) + if cmd.EndBlock != nil { + maxBlockHash, err := wire.NewShaHashFromStr(*cmd.EndBlock) + if err != nil { + return nil, rpcDecodeHexError(*cmd.EndBlock) + } + maxBlock, err = chain.BlockHeightByHash(maxBlockHash) + if err != nil { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCBlockNotFound, + Message: "Error getting block: " + err.Error(), + } + } + } + + // lastBlock and lastBlockHash track the previously-rescanned block. + // They equal nil when no previous blocks have been rescanned. + var lastBlock *btcutil.Block + var lastBlockHash *wire.ShaHash + + // A ticker is created to wait at least 10 seconds before notifying the + // websocket client of the current progress completed by the rescan. + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + + // Instead of fetching all block shas at once, fetch in smaller chunks + // to ensure large rescans consume a limited amount of memory. +fetchRange: + for minBlock < maxBlock { + // Limit the max number of hashes to fetch at once to the + // maximum number of items allowed in a single inventory. + // This value could be higher since it's not creating inventory + // messages, but this mirrors the limiting logic used in the + // peer-to-peer protocol. + maxLoopBlock := maxBlock + if maxLoopBlock-minBlock > wire.MaxInvPerMsg { + maxLoopBlock = minBlock + wire.MaxInvPerMsg + } + hashList, err := chain.HeightRange(minBlock, maxLoopBlock) + if err != nil { + rpcsLog.Errorf("Error looking up block range: %v", err) + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCDatabase, + Message: "Database error: " + err.Error(), + } + } + if len(hashList) == 0 { + // The rescan is finished if no blocks hashes for this + // range were successfully fetched and a stop block + // was provided. + if maxBlock != math.MaxInt32 { + break + } + + // If the rescan is through the current block, set up + // the client to continue to receive notifications + // regarding all rescanned addresses and the current set + // of unspent outputs. + // + // This is done safely by temporarily grabbing exclusive + // access of the block manager. If no more blocks have + // been attached between this pause and the fetch above, + // then it is safe to register the websocket client for + // continuous notifications if necessary. Otherwise, + // continue the fetch loop again to rescan the new + // blocks (or error due to an irrecoverable reorganize). + blockManager := wsc.server.server.blockManager + pauseGuard := blockManager.Pause() + best := blockManager.chain.BestSnapshot() + curHash := best.Hash + again := true + if lastBlockHash == nil || *lastBlockHash == *curHash { + again = false + n := wsc.server.ntfnMgr + n.RegisterSpentRequests(wsc, lookups.unspentSlice()) + n.RegisterTxOutAddressRequests(wsc, cmd.Addresses) + } + close(pauseGuard) + if err != nil { + rpcsLog.Errorf("Error fetching best block "+ + "hash: %v", err) + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCDatabase, + Message: "Database error: " + + err.Error(), + } + } + if again { + continue + } + break + } + + loopHashList: + for i := range hashList { + blk, err := chain.BlockByHash(&hashList[i]) + if err != nil { + // Only handle reorgs if a block could not be + // found for the hash. + if dbErr, ok := err.(database.Error); !ok || + dbErr.ErrorCode != database.ErrBlockNotFound { + + rpcsLog.Errorf("Error looking up "+ + "block: %v", err) + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCDatabase, + Message: "Database error: " + + err.Error(), + } + } + + // If an absolute max block was specified, don't + // attempt to handle the reorg. + if maxBlock != math.MaxInt32 { + rpcsLog.Errorf("Stopping rescan for "+ + "reorged block %v", + cmd.EndBlock) + return nil, &ErrRescanReorg + } + + // If the lookup for the previously valid block + // hash failed, there may have been a reorg. + // Fetch a new range of block hashes and verify + // that the previously processed block (if there + // was any) still exists in the database. If it + // doesn't, we error. + // + // A goto is used to branch executation back to + // before the range was evaluated, as it must be + // reevaluated for the new hashList. + minBlock += int32(i) + hashList, err = recoverFromReorg(chain, + minBlock, maxBlock, lastBlockHash) + if err != nil { + return nil, err + } + if len(hashList) == 0 { + break fetchRange + } + goto loopHashList + } + if i == 0 && lastBlockHash != nil { + // Ensure the new hashList is on the same fork + // as the last block from the old hashList. + jsonErr := descendantBlock(lastBlockHash, blk) + if jsonErr != nil { + return nil, jsonErr + } + } + + // A select statement is used to stop rescans if the + // client requesting the rescan has disconnected. + select { + case <-wsc.quit: + rpcsLog.Debugf("Stopped rescan at height %v "+ + "for disconnected client", blk.Height()) + return nil, nil + default: + rescanBlock(wsc, &lookups, blk) + lastBlock = blk + lastBlockHash = blk.Sha() + } + + // Periodically notify the client of the progress + // completed. Continue with next block if no progress + // notification is needed yet. + select { + case <-ticker.C: // fallthrough + default: + continue + } + + n := btcjson.NewRescanProgressNtfn(hashList[i].String(), + int32(blk.Height()), + blk.MsgBlock().Header.Timestamp.Unix()) + mn, err := btcjson.MarshalCmd(nil, n) + if err != nil { + rpcsLog.Errorf("Failed to marshal rescan "+ + "progress notification: %v", err) + continue + } + + if err = wsc.QueueNotification(mn); err == ErrClientQuit { + // Finished if the client disconnected. + rpcsLog.Debugf("Stopped rescan at height %v "+ + "for disconnected client", blk.Height()) + return nil, nil + } + } + + minBlock += int32(len(hashList)) + } + + // Notify websocket client of the finished rescan. Due to how btcd + // asynchronously queues notifications to not block calling code, + // there is no guarantee that any of the notifications created during + // rescan (such as rescanprogress, recvtx and redeemingtx) will be + // received before the rescan RPC returns. Therefore, another method + // is needed to safely inform clients that all rescan notifications have + // been sent. + n := btcjson.NewRescanFinishedNtfn(lastBlockHash.String(), + lastBlock.Height(), + lastBlock.MsgBlock().Header.Timestamp.Unix()) + if mn, err := btcjson.MarshalCmd(nil, n); err != nil { + rpcsLog.Errorf("Failed to marshal rescan finished "+ + "notification: %v", err) + } else { + // The rescan is finished, so we don't care whether the client + // has disconnected at this point, so discard error. + _ = wsc.QueueNotification(mn) + } + + rpcsLog.Info("Finished rescan") + return nil, nil +} + +func init() { + wsHandlers = wsHandlersBeforeInit +} diff --git a/vendor/github.com/btcsuite/btcd/sample-btcd.conf b/vendor/github.com/btcsuite/btcd/sample-btcd.conf new file mode 100644 index 0000000000000000000000000000000000000000..da4e4b8cb2990f3161c2a74cdb72830dcfa9dc67 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/sample-btcd.conf @@ -0,0 +1,326 @@ +[Application Options] + +; ------------------------------------------------------------------------------ +; Data settings +; ------------------------------------------------------------------------------ + +; The directory to store data such as the block chain and peer addresses. The +; block chain takes several GB, so this location must have a lot of free space. +; The default is ~/.btcd/data on POSIX OSes, $LOCALAPPDATA/Btcd/data on Windows, +; ~/Library/Application Support/Btcd/data on Mac OS, and $home/btcd/data on +; Plan9. Environment variables are expanded so they may be used. NOTE: Windows +; environment variables are typically %VARIABLE%, but they must be accessed with +; $VARIABLE here. Also, ~ is expanded to $LOCALAPPDATA on Windows. +; datadir=~/.btcd/data + + +; ------------------------------------------------------------------------------ +; Network settings +; ------------------------------------------------------------------------------ + +; Use testnet. +; testnet=1 + +; Connect via a SOCKS5 proxy. NOTE: Specifying a proxy will disable listening +; for incoming connections unless listen addresses are provided via the 'listen' +; option. +; proxy=127.0.0.1:9050 +; proxyuser= +; proxypass= + +; The SOCKS5 proxy above is assumed to be Tor (https://www.torproject.org). +; If the proxy is not tor the following may be used to prevent using tor +; specific SOCKS queries to lookup addresses (this increases anonymity when tor +; is used by preventing your IP being leaked via DNS). +; noonion=1 + +; Use an alternative proxy to connect to .onion addresses. The proxy is assumed +; to be a Tor node. Non .onion addresses will be contacted with the main proxy +; or without a proxy if none is set. +; onion=127.0.0.1:9051 +; onionuser= +; onionpass= + +; Enable Tor stream isolation by randomizing proxy user credentials resulting in +; Tor creating a new circuit for each connection. This makes it more difficult +; to correlate connections. +; torisolation=1 + +; Use Universal Plug and Play (UPnP) to automatically open the listen port +; and obtain the external IP address from supported devices. NOTE: This option +; will have no effect if exernal IP addresses are specified. +; upnp=1 + +; Specify the external IP addresses your node is listening on. One address per +; line. btcd will not contact 3rd-party sites to obtain external ip addresses. +; This means if you are behind NAT, your node will not be able to advertise a +; reachable address unless you specify it here or enable the 'upnp' option (and +; have a supported device). +; externalip=1.2.3.4 +; externalip=2002::1234 + +; ****************************************************************************** +; Summary of 'addpeer' versus 'connect'. +; +; Only one of the following two options, 'addpeer' and 'connect', may be +; specified. Both allow you to specify peers that you want to stay connected +; with, but the behavior is slightly different. By default, btcd will query DNS +; to find peers to connect to, so unless you have a specific reason such as +; those described below, you probably won't need to modify anything here. +; +; 'addpeer' does not prevent connections to other peers discovered from +; the peers you are connected to and also lets the remote peers know you are +; available so they can notify other peers they can to connect to you. This +; option might be useful if you are having problems finding a node for some +; reason (perhaps due to a firewall). +; +; 'connect', on the other hand, will ONLY connect to the specified peers and +; no others. It also disables listening (unless you explicitly set listen +; addresses via the 'listen' option) and DNS seeding, so you will not be +; advertised as an available peer to the peers you connect to and won't accept +; connections from any other peers. So, the 'connect' option effectively allows +; you to only connect to "trusted" peers. +; ****************************************************************************** + +; Add persistent peers to connect to as desired. One peer per line. +; You may specify each IP address with or without a port. The default port will +; be added automatically if one is not specified here. +; addpeer=192.168.1.1 +; addpeer=10.0.0.2:8333 +; addpeer=fe80::1 +; addpeer=[fe80::2]:8333 + +; Add persistent peers that you ONLY want to connect to as desired. One peer +; per line. You may specify each IP address with or without a port. The +; default port will be added automatically if one is not specified here. +; NOTE: Specifying this option has other side effects as described above in +; the 'addpeer' versus 'connect' summary section. +; connect=192.168.1.1 +; connect=10.0.0.2:8333 +; connect=fe80::1 +; connect=[fe80::2]:8333 + +; Maximum number of inbound and outbound peers. +; maxpeers=125 + +; Disable banning of misbehaving peers. +; nobanning=1 + +; Maximum allowed ban score before disconnecting and banning misbehaving peers.` +; banthreshold=100 + +; How long to ban misbehaving peers. Valid time units are {s, m, h}. +; Minimum 1s. +; banduration=24h +; banduration=11h30m15s + +; Disable DNS seeding for peers. By default, when btcd starts, it will use +; DNS to query for available peers to connect with. +; nodnsseed=1 + +; Specify the interfaces to listen on. One listen address per line. +; NOTE: The default port is modified by some options such as 'testnet', so it is +; recommended to not specify a port and allow a proper default to be chosen +; unless you have a specific reason to do otherwise. +; All interfaces on default port (this is the default): +; listen= +; All ipv4 interfaces on default port: +; listen=0.0.0.0 +; All ipv6 interfaces on default port: +; listen=:: +; All interfaces on port 8333: +; listen=:8333 +; All ipv4 interfaces on port 8333: +; listen=0.0.0.0:8333 +; All ipv6 interfaces on port 8333: +; listen=[::]:8333 +; Only ipv4 localhost on port 8333: +; listen=127.0.0.1:8333 +; Only ipv6 localhost on port 8333: +; listen=[::1]:8333 +; Only ipv4 localhost on non-standard port 8336: +; listen=127.0.0.1:8336 +; All interfaces on non-standard port 8336: +; listen=:8336 +; All ipv4 interfaces on non-standard port 8336: +; listen=0.0.0.0:8336 +; All ipv6 interfaces on non-standard port 8336: +; listen=[::]:8336 + +; Disable listening for incoming connections. This will override all listeners. +; nolisten=1 + +; Disable peer bloom filtering. See BIP0111. +; nopeerbloomfilters=1 + + +; ------------------------------------------------------------------------------ +; RPC server options - The following options control the built-in RPC server +; which is used to control and query information from a running btcd process. +; +; NOTE: The RPC server is disabled by default if rpcuser AND rpcpass, or +; rpclimituser AND rpclimitpass, are not specified. +; ------------------------------------------------------------------------------ + +; Secure the RPC API by specifying the username and password. You can also +; specify a limited username and password. You must specify at least one +; full set of credentials - limited or admin - or the RPC server will +; be disabled. +; rpcuser=whatever_admin_username_you_want +; rpcpass= +; rpclimituser=whatever_limited_username_you_want +; rpclimitpass= + +; Specify the interfaces for the RPC server listen on. One listen address per +; line. NOTE: The default port is modified by some options such as 'testnet', +; so it is recommended to not specify a port and allow a proper default to be +; chosen unless you have a specific reason to do otherwise. By default, the +; RPC server will only listen on localhost for IPv4 and IPv6. +; All interfaces on default port: +; rpclisten= +; All ipv4 interfaces on default port: +; rpclisten=0.0.0.0 +; All ipv6 interfaces on default port: +; rpclisten=:: +; All interfaces on port 8334: +; rpclisten=:8334 +; All ipv4 interfaces on port 8334: +; rpclisten=0.0.0.0:8334 +; All ipv6 interfaces on port 8334: +; rpclisten=[::]:8334 +; Only ipv4 localhost on port 8334: +; rpclisten=127.0.0.1:8334 +; Only ipv6 localhost on port 8334: +; rpclisten=[::1]:8334 +; Only ipv4 localhost on non-standard port 8337: +; rpclisten=127.0.0.1:8337 +; All interfaces on non-standard port 8337: +; rpclisten=:8337 +; All ipv4 interfaces on non-standard port 8337: +; rpclisten=0.0.0.0:8337 +; All ipv6 interfaces on non-standard port 8337: +; rpclisten=[::]:8337 + +; Specify the maximum number of concurrent RPC clients for standard connections. +; rpcmaxclients=10 + +; Specify the maximum number of concurrent RPC websocket clients. +; rpcmaxwebsockets=25 + +; Use the following setting to disable the RPC server even if the rpcuser and +; rpcpass are specified above. This allows one to quickly disable the RPC +; server without having to remove credentials from the config file. +; norpc=1 + +; Use the following setting to disable TLS for the RPC server. NOTE: This +; option only works if the RPC server is bound to localhost interfaces (which is +; the default). +; notls=1 + + +; ------------------------------------------------------------------------------ +; Mempool Settings - The following options +; ------------------------------------------------------------------------------ + +; Set the minimum transaction fee to be considered a non-zero fee, +; minrelaytxfee=0.00001 + +; Rate-limit free transactions to the value 15 * 1000 bytes per +; minute. +; limitfreerelay=15 + +; Require high priority for relaying free or low-fee transactions. +; norelaypriority=0 + +; Limit orphan transaction pool to 1000 transactions. +; maxorphantx=1000 + +; Do not accept transactions from remote peers. +; blocksonly=1 + + +; ------------------------------------------------------------------------------ +; Optional Transaction Indexes +; ------------------------------------------------------------------------------ + +; Build and maintain a full address-based transaction index. +; addrindex=1 +; Delete the entire address index on start up, then exit. +; dropaddrindex=0 + + +; ------------------------------------------------------------------------------ +; Optional Indexes +; ------------------------------------------------------------------------------ + +; Build and maintain a full hash-based transaction index which makes all +; transactions available via the getrawtransaction RPC. +; txindex=1 + +; Build and maintain a full address-based transaction index which makes the +; searchrawtransactions RPC available. +; addrindex=1 + + +; ------------------------------------------------------------------------------ +; Signature Verification Cache +; ------------------------------------------------------------------------------ + +; Limit the signature cache to a max of 50000 entries. +; sigcachemaxsize=50000 + + +; ------------------------------------------------------------------------------ +; Coin Generation (Mining) Settings - The following options control the +; generation of block templates used by external mining applications through RPC +; calls as well as the built-in CPU miner (if enabled). +; ------------------------------------------------------------------------------ + +; Enable built-in CPU mining. +; +; NOTE: This is typically only useful for testing purposes such as testnet or +; simnet since the difficutly on mainnet is far too high for CPU mining to be +; worth your while. +; generate=false + +; Add addresses to pay mined blocks to for CPU mining and the block templates +; generated for the getwork RPC as desired. One address per line. +; miningaddr=1yourbitcoinaddress +; miningaddr=1yourbitcoinaddress2 +; miningaddr=1yourbitcoinaddress3 + +; Specify the minimum block size in bytes to create. By default, only +; transactions which have enough fees or a high enough priority will be included +; in generated block templates. Specifying a minimum block size will instead +; attempt to fill generated block templates up with transactions until it is at +; least the specified number of bytes. +; blockminsize=0 + +; Specify the maximum block size in bytes to create. This value will be limited +; to the consensus limit if it is larger than that value. +; blockmaxsize=750000 + +; Specify the size in bytes of the high-priority/low-fee area when creating a +; block. Transactions which consist of large amounts, old inputs, and small +; sizes have the highest priority. One consequence of this is that as low-fee +; or free transactions age, they raise in priority thereby making them more +; likely to be included in this section of a new block. This value is limited +; by the blackmaxsize option and will be limited as needed. +; blockprioritysize=50000 + + +; ------------------------------------------------------------------------------ +; Debug +; ------------------------------------------------------------------------------ + +; Debug logging level. +; Valid levels are {trace, debug, info, warn, error, critical} +; You may also specify <subsystem>=<level>,<subsystem2>=<level>,... to set +; log level for individual subsystems. Use btcd --debuglevel=show to list +; available subsystems. +; debuglevel=info + +; The port used to listen for HTTP profile requests. The profile server will +; be disabled if this option is not specified. The profile information can be +; accessed at http://localhost:<profileport>/debug/pprof once running. +; profile=6061 diff --git a/vendor/github.com/btcsuite/btcd/server.go b/vendor/github.com/btcsuite/btcd/server.go new file mode 100644 index 0000000000000000000000000000000000000000..f2c64ec9150724f48b5b8a593d37c5be27cd75fd --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/server.go @@ -0,0 +1,2570 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "math" + mrand "math/rand" + "net" + "runtime" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/btcsuite/btcd/addrmgr" + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/blockchain/indexers" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/mining" + "github.com/btcsuite/btcd/peer" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcutil/bloom" +) + +const ( + // These constants are used by the DNS seed code to pick a random last + // seen time. + secondsIn3Days int32 = 24 * 60 * 60 * 3 + secondsIn4Days int32 = 24 * 60 * 60 * 4 +) + +const ( + // defaultServices describes the default services that are supported by + // the server. + defaultServices = wire.SFNodeNetwork | wire.SFNodeBloom + + // defaultMaxOutbound is the default number of max outbound peers. + defaultMaxOutbound = 8 + + // connectionRetryInterval is the base amount of time to wait in between + // retries when connecting to persistent peers. It is adjusted by the + // number of retries such that there is a retry backoff. + connectionRetryInterval = time.Second * 5 + + // maxConnectionRetryInterval is the max amount of time retrying of a + // persistent peer is allowed to grow to. This is necessary since the + // retry logic uses a backoff mechanism which increases the interval + // base done the number of retries that have been done. + maxConnectionRetryInterval = time.Minute * 5 +) + +var ( + // userAgentName is the user agent name and is used to help identify + // ourselves to other bitcoin peers. + userAgentName = "btcd" + + // userAgentVersion is the user agent version and is used to help + // identify ourselves to other bitcoin peers. + userAgentVersion = fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch) +) + +// broadcastMsg provides the ability to house a bitcoin message to be broadcast +// to all connected peers except specified excluded peers. +type broadcastMsg struct { + message wire.Message + excludePeers []*serverPeer +} + +// broadcastInventoryAdd is a type used to declare that the InvVect it contains +// needs to be added to the rebroadcast map +type broadcastInventoryAdd relayMsg + +// broadcastInventoryDel is a type used to declare that the InvVect it contains +// needs to be removed from the rebroadcast map +type broadcastInventoryDel *wire.InvVect + +// relayMsg packages an inventory vector along with the newly discovered +// inventory so the relay has access to that information. +type relayMsg struct { + invVect *wire.InvVect + data interface{} +} + +// updatePeerHeightsMsg is a message sent from the blockmanager to the server +// after a new block has been accepted. The purpose of the message is to update +// the heights of peers that were known to announce the block before we +// connected it to the main chain or recognized it as an orphan. With these +// updates, peer heights will be kept up to date, allowing for fresh data when +// selecting sync peer candidacy. +type updatePeerHeightsMsg struct { + newSha *wire.ShaHash + newHeight int32 + originPeer *serverPeer +} + +// peerState maintains state of inbound, persistent, outbound peers as well +// as banned peers and outbound groups. +type peerState struct { + pendingPeers map[string]*serverPeer + inboundPeers map[int32]*serverPeer + outboundPeers map[int32]*serverPeer + persistentPeers map[int32]*serverPeer + banned map[string]time.Time + outboundGroups map[string]int + maxOutboundPeers int +} + +// Count returns the count of all known peers. +func (ps *peerState) Count() int { + return len(ps.inboundPeers) + len(ps.outboundPeers) + + len(ps.persistentPeers) +} + +// OutboundCount returns the count of known outbound peers. +func (ps *peerState) OutboundCount() int { + return len(ps.outboundPeers) + len(ps.persistentPeers) +} + +// NeedMoreOutbound returns true if more outbound peers are required. +func (ps *peerState) NeedMoreOutbound() bool { + return ps.OutboundCount() < ps.maxOutboundPeers && + ps.Count() < cfg.MaxPeers +} + +// NeedMoreTries returns true if more outbound peer attempts can be tried. +func (ps *peerState) NeedMoreTries() bool { + return len(ps.pendingPeers) < 2*(ps.maxOutboundPeers-ps.OutboundCount()) +} + +// forAllOutboundPeers is a helper function that runs closure on all outbound +// peers known to peerState. +func (ps *peerState) forAllOutboundPeers(closure func(sp *serverPeer)) { + for _, e := range ps.outboundPeers { + closure(e) + } + for _, e := range ps.persistentPeers { + closure(e) + } +} + +// forPendingPeers is a helper function that runs closure on all pending peers +// known to peerState. +func (ps *peerState) forPendingPeers(closure func(sp *serverPeer)) { + for _, e := range ps.pendingPeers { + closure(e) + } +} + +// forAllPeers is a helper function that runs closure on all peers known to +// peerState. +func (ps *peerState) forAllPeers(closure func(sp *serverPeer)) { + for _, e := range ps.inboundPeers { + closure(e) + } + ps.forAllOutboundPeers(closure) +} + +// server provides a bitcoin server for handling communications to and from +// bitcoin peers. +type server struct { + // The following variables must only be used atomically. + // Putting the uint64s first makes them 64-bit aligned for 32-bit systems. + bytesReceived uint64 // Total bytes received from all peers since start. + bytesSent uint64 // Total bytes sent by all peers since start. + started int32 + shutdown int32 + shutdownSched int32 + + listeners []net.Listener + chainParams *chaincfg.Params + addrManager *addrmgr.AddrManager + sigCache *txscript.SigCache + rpcServer *rpcServer + blockManager *blockManager + txMemPool *txMemPool + cpuMiner *CPUMiner + modifyRebroadcastInv chan interface{} + pendingPeers chan *serverPeer + newPeers chan *serverPeer + donePeers chan *serverPeer + banPeers chan *serverPeer + retryPeers chan *serverPeer + wakeup chan struct{} + query chan interface{} + relayInv chan relayMsg + broadcast chan broadcastMsg + peerHeightsUpdate chan updatePeerHeightsMsg + wg sync.WaitGroup + quit chan struct{} + nat NAT + db database.DB + timeSource blockchain.MedianTimeSource + services wire.ServiceFlag + + // The following fields are used for optional indexes. They will be nil + // if the associated index is not enabled. These fields are set during + // initial creation of the server and never changed afterwards, so they + // do not need to be protected for concurrent access. + txIndex *indexers.TxIndex + addrIndex *indexers.AddrIndex +} + +// serverPeer extends the peer to maintain state shared by the server and +// the blockmanager. +type serverPeer struct { + *peer.Peer + + server *server + persistent bool + continueHash *wire.ShaHash + relayMtx sync.Mutex + disableRelayTx bool + requestQueue []*wire.InvVect + requestedTxns map[wire.ShaHash]struct{} + requestedBlocks map[wire.ShaHash]struct{} + filter *bloom.Filter + knownAddresses map[string]struct{} + banScore dynamicBanScore + quit chan struct{} + // The following chans are used to sync blockmanager and server. + txProcessed chan struct{} + blockProcessed chan struct{} +} + +// newServerPeer returns a new serverPeer instance. The peer needs to be set by +// the caller. +func newServerPeer(s *server, isPersistent bool) *serverPeer { + return &serverPeer{ + server: s, + persistent: isPersistent, + requestedTxns: make(map[wire.ShaHash]struct{}), + requestedBlocks: make(map[wire.ShaHash]struct{}), + filter: bloom.LoadFilter(nil), + knownAddresses: make(map[string]struct{}), + quit: make(chan struct{}), + txProcessed: make(chan struct{}, 1), + blockProcessed: make(chan struct{}, 1), + } +} + +// newestBlock returns the current best block hash and height using the format +// required by the configuration for the peer package. +func (sp *serverPeer) newestBlock() (*wire.ShaHash, int32, error) { + best := sp.server.blockManager.chain.BestSnapshot() + return best.Hash, best.Height, nil +} + +// addKnownAddresses adds the given addresses to the set of known addreses to +// the peer to prevent sending duplicate addresses. +func (sp *serverPeer) addKnownAddresses(addresses []*wire.NetAddress) { + for _, na := range addresses { + sp.knownAddresses[addrmgr.NetAddressKey(na)] = struct{}{} + } +} + +// addressKnown true if the given address is already known to the peer. +func (sp *serverPeer) addressKnown(na *wire.NetAddress) bool { + _, exists := sp.knownAddresses[addrmgr.NetAddressKey(na)] + return exists +} + +// setDisableRelayTx toggles relaying of transactions for the given peer. +// It is safe for concurrent access. +func (sp *serverPeer) setDisableRelayTx(disable bool) { + sp.relayMtx.Lock() + sp.disableRelayTx = disable + sp.relayMtx.Unlock() +} + +// relayTxDisabled returns whether or not relaying of transactions for the given +// peer is disabled. +// It is safe for concurrent access. +func (sp *serverPeer) relayTxDisabled() bool { + sp.relayMtx.Lock() + defer sp.relayMtx.Unlock() + + return sp.disableRelayTx +} + +// pushAddrMsg sends an addr message to the connected peer using the provided +// addresses. +func (sp *serverPeer) pushAddrMsg(addresses []*wire.NetAddress) { + // Filter addresses already known to the peer. + addrs := make([]*wire.NetAddress, 0, len(addresses)) + for _, addr := range addresses { + if !sp.addressKnown(addr) { + addrs = append(addrs, addr) + } + } + known, err := sp.PushAddrMsg(addrs) + if err != nil { + peerLog.Errorf("Can't push address message to %s: %v", sp.Peer, err) + sp.Disconnect() + return + } + sp.addKnownAddresses(known) +} + +// addBanScore increases the persistent and decaying ban score fields by the +// values passed as parameters. If the resulting score exceeds half of the ban +// threshold, a warning is logged including the reason provided. Further, if +// the score is above the ban threshold, the peer will be banned and +// disconnected. +func (sp *serverPeer) addBanScore(persistent, transient uint32, reason string) { + // No warning is logged and no score is calculated if banning is disabled. + if cfg.DisableBanning { + return + } + warnThreshold := cfg.BanThreshold >> 1 + if transient == 0 && persistent == 0 { + // The score is not being increased, but a warning message is still + // logged if the score is above the warn threshold. + score := sp.banScore.Int() + if score > warnThreshold { + peerLog.Warnf("Misbehaving peer %s: %s -- ban score is %d, "+ + "it was not increased this time", sp, reason, score) + } + return + } + score := sp.banScore.Increase(persistent, transient) + if score > warnThreshold { + peerLog.Warnf("Misbehaving peer %s: %s -- ban score increased to %d", + sp, reason, score) + if score > cfg.BanThreshold { + peerLog.Warnf("Misbehaving peer %s -- banning and disconnecting", + sp) + sp.server.BanPeer(sp) + sp.Disconnect() + } + } +} + +// OnVersion is invoked when a peer receives a version bitcoin message +// and is used to negotiate the protocol version details as well as kick start +// the communications. +func (sp *serverPeer) OnVersion(p *peer.Peer, msg *wire.MsgVersion) { + // Add the remote peer time as a sample for creating an offset against + // the local clock to keep the network time in sync. + sp.server.timeSource.AddTimeSample(p.Addr(), msg.Timestamp) + + // Signal the block manager this peer is a new sync candidate. + sp.server.blockManager.NewPeer(sp) + + // Choose whether or not to relay transactions before a filter command + // is received. + sp.setDisableRelayTx(msg.DisableRelayTx) + + // Update the address manager and request known addresses from the + // remote peer for outbound connections. This is skipped when running + // on the simulation test network since it is only intended to connect + // to specified peers and actively avoids advertising and connecting to + // discovered peers. + if !cfg.SimNet { + addrManager := sp.server.addrManager + // Outbound connections. + if !p.Inbound() { + // TODO(davec): Only do this if not doing the initial block + // download and the local address is routable. + if !cfg.DisableListen /* && isCurrent? */ { + // Get address that best matches. + lna := addrManager.GetBestLocalAddress(p.NA()) + if addrmgr.IsRoutable(lna) { + // Filter addresses the peer already knows about. + addresses := []*wire.NetAddress{lna} + sp.pushAddrMsg(addresses) + } + } + + // Request known addresses if the server address manager needs + // more and the peer has a protocol version new enough to + // include a timestamp with addresses. + hasTimestamp := p.ProtocolVersion() >= + wire.NetAddressTimeVersion + if addrManager.NeedMoreAddresses() && hasTimestamp { + p.QueueMessage(wire.NewMsgGetAddr(), nil) + } + + // Mark the address as a known good address. + addrManager.Good(p.NA()) + } else { + // A peer might not be advertising the same address that it + // actually connected from. One example of why this can happen + // is with NAT. Only add the address to the address manager if + // the addresses agree. + if addrmgr.NetAddressKey(&msg.AddrMe) == addrmgr.NetAddressKey(p.NA()) { + addrManager.AddAddress(p.NA(), p.NA()) + addrManager.Good(p.NA()) + } + } + } + + // Add valid peer to the server. + sp.server.AddPeer(sp) +} + +// OnMemPool is invoked when a peer receives a mempool bitcoin message. +// It creates and sends an inventory message with the contents of the memory +// pool up to the maximum inventory allowed per message. When the peer has a +// bloom filter loaded, the contents are filtered accordingly. +func (sp *serverPeer) OnMemPool(p *peer.Peer, msg *wire.MsgMemPool) { + // A decaying ban score increase is applied to prevent flooding. + // The ban score accumulates and passes the ban threshold if a burst of + // mempool messages comes from a peer. The score decays each minute to + // half of its value. + sp.addBanScore(0, 33, "mempool") + + // Generate inventory message with the available transactions in the + // transaction memory pool. Limit it to the max allowed inventory + // per message. The NewMsgInvSizeHint function automatically limits + // the passed hint to the maximum allowed, so it's safe to pass it + // without double checking it here. + txMemPool := sp.server.txMemPool + txDescs := txMemPool.TxDescs() + invMsg := wire.NewMsgInvSizeHint(uint(len(txDescs))) + + for i, txDesc := range txDescs { + // Another thread might have removed the transaction from the + // pool since the initial query. + hash := txDesc.Tx.Sha() + if !txMemPool.IsTransactionInPool(hash) { + continue + } + + // Either add all transactions when there is no bloom filter, + // or only the transactions that match the filter when there is + // one. + if !sp.filter.IsLoaded() || sp.filter.MatchTxAndUpdate(txDesc.Tx) { + iv := wire.NewInvVect(wire.InvTypeTx, hash) + invMsg.AddInvVect(iv) + if i+1 >= wire.MaxInvPerMsg { + break + } + } + } + + // Send the inventory message if there is anything to send. + if len(invMsg.InvList) > 0 { + p.QueueMessage(invMsg, nil) + } +} + +// OnTx is invoked when a peer receives a tx bitcoin message. It blocks +// until the bitcoin transaction has been fully processed. Unlock the block +// handler this does not serialize all transactions through a single thread +// transactions don't rely on the previous one in a linear fashion like blocks. +func (sp *serverPeer) OnTx(p *peer.Peer, msg *wire.MsgTx) { + if cfg.BlocksOnly { + peerLog.Tracef("Ignoring tx %v from %v - blocksonly enabled", + msg.TxSha(), p) + return + } + + // Add the transaction to the known inventory for the peer. + // Convert the raw MsgTx to a btcutil.Tx which provides some convenience + // methods and things such as hash caching. + tx := btcutil.NewTx(msg) + iv := wire.NewInvVect(wire.InvTypeTx, tx.Sha()) + p.AddKnownInventory(iv) + + // Queue the transaction up to be handled by the block manager and + // intentionally block further receives until the transaction is fully + // processed and known good or bad. This helps prevent a malicious peer + // from queuing up a bunch of bad transactions before disconnecting (or + // being disconnected) and wasting memory. + sp.server.blockManager.QueueTx(tx, sp) + <-sp.txProcessed +} + +// OnBlock is invoked when a peer receives a block bitcoin message. It +// blocks until the bitcoin block has been fully processed. +func (sp *serverPeer) OnBlock(p *peer.Peer, msg *wire.MsgBlock, buf []byte) { + // Convert the raw MsgBlock to a btcutil.Block which provides some + // convenience methods and things such as hash caching. + block := btcutil.NewBlockFromBlockAndBytes(msg, buf) + + // Add the block to the known inventory for the peer. + iv := wire.NewInvVect(wire.InvTypeBlock, block.Sha()) + p.AddKnownInventory(iv) + + // Queue the block up to be handled by the block + // manager and intentionally block further receives + // until the bitcoin block is fully processed and known + // good or bad. This helps prevent a malicious peer + // from queuing up a bunch of bad blocks before + // disconnecting (or being disconnected) and wasting + // memory. Additionally, this behavior is depended on + // by at least the block acceptance test tool as the + // reference implementation processes blocks in the same + // thread and therefore blocks further messages until + // the bitcoin block has been fully processed. + sp.server.blockManager.QueueBlock(block, sp) + <-sp.blockProcessed +} + +// OnInv is invoked when a peer receives an inv bitcoin message and is +// used to examine the inventory being advertised by the remote peer and react +// accordingly. We pass the message down to blockmanager which will call +// QueueMessage with any appropriate responses. +func (sp *serverPeer) OnInv(p *peer.Peer, msg *wire.MsgInv) { + if !cfg.BlocksOnly { + if len(msg.InvList) > 0 { + sp.server.blockManager.QueueInv(msg, sp) + } + return + } + + newInv := wire.NewMsgInvSizeHint(uint(len(msg.InvList))) + for _, invVect := range msg.InvList { + if invVect.Type == wire.InvTypeTx { + peerLog.Tracef("Ignoring tx %v in inv from %v -- "+ + "blocksonly enabled", invVect.Hash, p) + if p.ProtocolVersion() >= wire.BIP0037Version { + peerLog.Infof("Peer %v is announcing "+ + "transactions -- disconnecting", p) + p.Disconnect() + return + } + continue + } + err := newInv.AddInvVect(invVect) + if err != nil { + peerLog.Errorf("Failed to add inventory vector: %v", err) + break + } + } + + if len(newInv.InvList) > 0 { + sp.server.blockManager.QueueInv(newInv, sp) + } +} + +// OnHeaders is invoked when a peer receives a headers bitcoin +// message. The message is passed down to the block manager. +func (sp *serverPeer) OnHeaders(p *peer.Peer, msg *wire.MsgHeaders) { + sp.server.blockManager.QueueHeaders(msg, sp) +} + +// handleGetData is invoked when a peer receives a getdata bitcoin message and +// is used to deliver block and transaction information. +func (sp *serverPeer) OnGetData(p *peer.Peer, msg *wire.MsgGetData) { + numAdded := 0 + notFound := wire.NewMsgNotFound() + + length := len(msg.InvList) + // A decaying ban score increase is applied to prevent exhausting resources + // with unusually large inventory queries. + // Requesting more than the maximum inventory vector length within a short + // period of time yields a score above the default ban threshold. Sustained + // bursts of small requests are not penalized as that would potentially ban + // peers performing IBD. + // This incremental score decays each minute to half of its value. + sp.addBanScore(0, uint32(length)*99/wire.MaxInvPerMsg, "getdata") + + // We wait on this wait channel periodically to prevent queuing + // far more data than we can send in a reasonable time, wasting memory. + // The waiting occurs after the database fetch for the next one to + // provide a little pipelining. + var waitChan chan struct{} + doneChan := make(chan struct{}, 1) + + for i, iv := range msg.InvList { + var c chan struct{} + // If this will be the last message we send. + if i == length-1 && len(notFound.InvList) == 0 { + c = doneChan + } else if (i+1)%3 == 0 { + // Buffered so as to not make the send goroutine block. + c = make(chan struct{}, 1) + } + var err error + switch iv.Type { + case wire.InvTypeTx: + err = sp.server.pushTxMsg(sp, &iv.Hash, c, waitChan) + case wire.InvTypeBlock: + err = sp.server.pushBlockMsg(sp, &iv.Hash, c, waitChan) + case wire.InvTypeFilteredBlock: + err = sp.server.pushMerkleBlockMsg(sp, &iv.Hash, c, waitChan) + default: + peerLog.Warnf("Unknown type in inventory request %d", + iv.Type) + continue + } + if err != nil { + notFound.AddInvVect(iv) + + // When there is a failure fetching the final entry + // and the done channel was sent in due to there + // being no outstanding not found inventory, consume + // it here because there is now not found inventory + // that will use the channel momentarily. + if i == len(msg.InvList)-1 && c != nil { + <-c + } + } + numAdded++ + waitChan = c + } + if len(notFound.InvList) != 0 { + p.QueueMessage(notFound, doneChan) + } + + // Wait for messages to be sent. We can send quite a lot of data at this + // point and this will keep the peer busy for a decent amount of time. + // We don't process anything else by them in this time so that we + // have an idea of when we should hear back from them - else the idle + // timeout could fire when we were only half done sending the blocks. + if numAdded > 0 { + <-doneChan + } +} + +// OnGetBlocks is invoked when a peer receives a getblocks bitcoin +// message. +func (sp *serverPeer) OnGetBlocks(p *peer.Peer, msg *wire.MsgGetBlocks) { + // Return all block hashes to the latest one (up to max per message) if + // no stop hash was specified. + // Attempt to find the ending index of the stop hash if specified. + chain := sp.server.blockManager.chain + endIdx := int32(math.MaxInt32) + if !msg.HashStop.IsEqual(&zeroHash) { + height, err := chain.BlockHeightByHash(&msg.HashStop) + if err == nil { + endIdx = height + 1 + } + } + + // Find the most recent known block based on the block locator. + // Use the block after the genesis block if no other blocks in the + // provided locator are known. This does mean the client will start + // over with the genesis block if unknown block locators are provided. + // This mirrors the behavior in the reference implementation. + startIdx := int32(1) + for _, hash := range msg.BlockLocatorHashes { + height, err := chain.BlockHeightByHash(hash) + if err == nil { + // Start with the next hash since we know this one. + startIdx = height + 1 + break + } + } + + // Don't attempt to fetch more than we can put into a single message. + autoContinue := false + if endIdx-startIdx > wire.MaxBlocksPerMsg { + endIdx = startIdx + wire.MaxBlocksPerMsg + autoContinue = true + } + + // Fetch the inventory from the block database. + hashList, err := chain.HeightRange(startIdx, endIdx) + if err != nil { + peerLog.Warnf("Block lookup failed: %v", err) + return + } + + // Generate inventory message. + invMsg := wire.NewMsgInv() + for i := range hashList { + iv := wire.NewInvVect(wire.InvTypeBlock, &hashList[i]) + invMsg.AddInvVect(iv) + } + + // Send the inventory message if there is anything to send. + if len(invMsg.InvList) > 0 { + invListLen := len(invMsg.InvList) + if autoContinue && invListLen == wire.MaxBlocksPerMsg { + // Intentionally use a copy of the final hash so there + // is not a reference into the inventory slice which + // would prevent the entire slice from being eligible + // for GC as soon as it's sent. + continueHash := invMsg.InvList[invListLen-1].Hash + sp.continueHash = &continueHash + } + p.QueueMessage(invMsg, nil) + } +} + +// OnGetHeaders is invoked when a peer receives a getheaders bitcoin +// message. +func (sp *serverPeer) OnGetHeaders(p *peer.Peer, msg *wire.MsgGetHeaders) { + // Ignore getheaders requests if not in sync. + if !sp.server.blockManager.IsCurrent() { + return + } + + // Attempt to look up the height of the provided stop hash. + chain := sp.server.blockManager.chain + endIdx := int32(math.MaxInt32) + height, err := chain.BlockHeightByHash(&msg.HashStop) + if err == nil { + endIdx = height + 1 + } + + // There are no block locators so a specific header is being requested + // as identified by the stop hash. + if len(msg.BlockLocatorHashes) == 0 { + // No blocks with the stop hash were found so there is nothing + // to do. Just return. This behavior mirrors the reference + // implementation. + if endIdx == math.MaxInt32 { + return + } + + // Fetch the raw block header bytes from the database. + var headerBytes []byte + err := sp.server.db.View(func(dbTx database.Tx) error { + var err error + headerBytes, err = dbTx.FetchBlockHeader(&msg.HashStop) + return err + }) + if err != nil { + peerLog.Warnf("Lookup of known block hash failed: %v", + err) + return + } + + // Deserialize the block header. + var header wire.BlockHeader + err = header.Deserialize(bytes.NewReader(headerBytes)) + if err != nil { + peerLog.Warnf("Block header deserialize failed: %v", + err) + return + } + + headersMsg := wire.NewMsgHeaders() + headersMsg.AddBlockHeader(&header) + p.QueueMessage(headersMsg, nil) + return + } + + // Find the most recent known block based on the block locator. + // Use the block after the genesis block if no other blocks in the + // provided locator are known. This does mean the client will start + // over with the genesis block if unknown block locators are provided. + // This mirrors the behavior in the reference implementation. + startIdx := int32(1) + for _, hash := range msg.BlockLocatorHashes { + height, err := chain.BlockHeightByHash(hash) + if err == nil { + // Start with the next hash since we know this one. + startIdx = height + 1 + break + } + } + + // Don't attempt to fetch more than we can put into a single message. + if endIdx-startIdx > wire.MaxBlockHeadersPerMsg { + endIdx = startIdx + wire.MaxBlockHeadersPerMsg + } + + // Fetch the inventory from the block database. + hashList, err := chain.HeightRange(startIdx, endIdx) + if err != nil { + peerLog.Warnf("Header lookup failed: %v", err) + return + } + + // Generate headers message and send it. + headersMsg := wire.NewMsgHeaders() + err = sp.server.db.View(func(dbTx database.Tx) error { + for i := range hashList { + headerBytes, err := dbTx.FetchBlockHeader(&hashList[i]) + if err != nil { + return err + } + + var header wire.BlockHeader + err = header.Deserialize(bytes.NewReader(headerBytes)) + if err != nil { + return err + } + headersMsg.AddBlockHeader(&header) + } + + return nil + }) + if err != nil { + peerLog.Warnf("Failed to build headers: %v", err) + return + } + + p.QueueMessage(headersMsg, nil) +} + +// OnFilterAdd is invoked when a peer receives a filteradd bitcoin +// message and is used by remote peers to add data to an already loaded bloom +// filter. The peer will be disconnected if a filter is not loaded when this +// message is received. +func (sp *serverPeer) OnFilterAdd(p *peer.Peer, msg *wire.MsgFilterAdd) { + if sp.filter.IsLoaded() { + peerLog.Debugf("%s sent a filteradd request with no filter "+ + "loaded -- disconnecting", p) + p.Disconnect() + return + } + + sp.filter.Add(msg.Data) +} + +// OnFilterClear is invoked when a peer receives a filterclear bitcoin +// message and is used by remote peers to clear an already loaded bloom filter. +// The peer will be disconnected if a filter is not loaded when this message is +// received. +func (sp *serverPeer) OnFilterClear(p *peer.Peer, msg *wire.MsgFilterClear) { + if !sp.filter.IsLoaded() { + peerLog.Debugf("%s sent a filterclear request with no "+ + "filter loaded -- disconnecting", p) + p.Disconnect() + return + } + + sp.filter.Unload() +} + +// OnFilterLoad is invoked when a peer receives a filterload bitcoin +// message and it used to load a bloom filter that should be used for +// delivering merkle blocks and associated transactions that match the filter. +func (sp *serverPeer) OnFilterLoad(p *peer.Peer, msg *wire.MsgFilterLoad) { + sp.setDisableRelayTx(false) + + sp.filter.Reload(msg) +} + +// OnGetAddr is invoked when a peer receives a getaddr bitcoin message +// and is used to provide the peer with known addresses from the address +// manager. +func (sp *serverPeer) OnGetAddr(p *peer.Peer, msg *wire.MsgGetAddr) { + // Don't return any addresses when running on the simulation test + // network. This helps prevent the network from becoming another + // public test network since it will not be able to learn about other + // peers that have not specifically been provided. + if cfg.SimNet { + return + } + + // Do not accept getaddr requests from outbound peers. This reduces + // fingerprinting attacks. + if !p.Inbound() { + return + } + + // Get the current known addresses from the address manager. + addrCache := sp.server.addrManager.AddressCache() + + // Push the addresses. + sp.pushAddrMsg(addrCache) +} + +// OnAddr is invoked when a peer receives an addr bitcoin message and is +// used to notify the server about advertised addresses. +func (sp *serverPeer) OnAddr(p *peer.Peer, msg *wire.MsgAddr) { + // Ignore addresses when running on the simulation test network. This + // helps prevent the network from becoming another public test network + // since it will not be able to learn about other peers that have not + // specifically been provided. + if cfg.SimNet { + return + } + + // Ignore old style addresses which don't include a timestamp. + if p.ProtocolVersion() < wire.NetAddressTimeVersion { + return + } + + // A message that has no addresses is invalid. + if len(msg.AddrList) == 0 { + peerLog.Errorf("Command [%s] from %s does not contain any addresses", + msg.Command(), p) + p.Disconnect() + return + } + + for _, na := range msg.AddrList { + // Don't add more address if we're disconnecting. + if !p.Connected() { + return + } + + // Set the timestamp to 5 days ago if it's more than 24 hours + // in the future so this address is one of the first to be + // removed when space is needed. + now := time.Now() + if na.Timestamp.After(now.Add(time.Minute * 10)) { + na.Timestamp = now.Add(-1 * time.Hour * 24 * 5) + } + + // Add address to known addresses for this peer. + sp.addKnownAddresses([]*wire.NetAddress{na}) + } + + // Add addresses to server address manager. The address manager handles + // the details of things such as preventing duplicate addresses, max + // addresses, and last seen updates. + // XXX bitcoind gives a 2 hour time penalty here, do we want to do the + // same? + sp.server.addrManager.AddAddresses(msg.AddrList, p.NA()) +} + +// OnRead is invoked when a peer receives a message and it is used to update +// the bytes received by the server. +func (sp *serverPeer) OnRead(p *peer.Peer, bytesRead int, msg wire.Message, err error) { + sp.server.AddBytesReceived(uint64(bytesRead)) +} + +// OnWrite is invoked when a peer sends a message and it is used to update +// the bytes sent by the server. +func (sp *serverPeer) OnWrite(p *peer.Peer, bytesWritten int, msg wire.Message, err error) { + sp.server.AddBytesSent(uint64(bytesWritten)) +} + +// randomUint16Number returns a random uint16 in a specified input range. Note +// that the range is in zeroth ordering; if you pass it 1800, you will get +// values from 0 to 1800. +func randomUint16Number(max uint16) uint16 { + // In order to avoid modulo bias and ensure every possible outcome in + // [0, max) has equal probability, the random number must be sampled + // from a random source that has a range limited to a multiple of the + // modulus. + var randomNumber uint16 + var limitRange = (math.MaxUint16 / max) * max + for { + binary.Read(rand.Reader, binary.LittleEndian, &randomNumber) + if randomNumber < limitRange { + return (randomNumber % max) + } + } +} + +// AddRebroadcastInventory adds 'iv' to the list of inventories to be +// rebroadcasted at random intervals until they show up in a block. +func (s *server) AddRebroadcastInventory(iv *wire.InvVect, data interface{}) { + // Ignore if shutting down. + if atomic.LoadInt32(&s.shutdown) != 0 { + return + } + + s.modifyRebroadcastInv <- broadcastInventoryAdd{invVect: iv, data: data} +} + +// RemoveRebroadcastInventory removes 'iv' from the list of items to be +// rebroadcasted if present. +func (s *server) RemoveRebroadcastInventory(iv *wire.InvVect) { + // Ignore if shutting down. + if atomic.LoadInt32(&s.shutdown) != 0 { + return + } + + s.modifyRebroadcastInv <- broadcastInventoryDel(iv) +} + +// AnnounceNewTransactions generates and relays inventory vectors and notifies +// both websocket and getblocktemplate long poll clients of the passed +// transactions. This function should be called whenever new transactions +// are added to the mempool. +func (s *server) AnnounceNewTransactions(newTxs []*btcutil.Tx) { + // Generate and relay inventory vectors for all newly accepted + // transactions into the memory pool due to the original being + // accepted. + for _, tx := range newTxs { + // Generate the inventory vector and relay it. + iv := wire.NewInvVect(wire.InvTypeTx, tx.Sha()) + s.RelayInventory(iv, tx) + + if s.rpcServer != nil { + // Notify websocket clients about mempool transactions. + s.rpcServer.ntfnMgr.NotifyMempoolTx(tx, true) + + // Potentially notify any getblocktemplate long poll clients + // about stale block templates due to the new transaction. + s.rpcServer.gbtWorkState.NotifyMempoolTx( + s.txMemPool.LastUpdated()) + } + } +} + +// pushTxMsg sends a tx message for the provided transaction hash to the +// connected peer. An error is returned if the transaction hash is not known. +func (s *server) pushTxMsg(sp *serverPeer, sha *wire.ShaHash, doneChan chan<- struct{}, waitChan <-chan struct{}) error { + // Attempt to fetch the requested transaction from the pool. A + // call could be made to check for existence first, but simply trying + // to fetch a missing transaction results in the same behavior. + tx, err := s.txMemPool.FetchTransaction(sha) + if err != nil { + peerLog.Tracef("Unable to fetch tx %v from transaction "+ + "pool: %v", sha, err) + + if doneChan != nil { + doneChan <- struct{}{} + } + return err + } + + // Once we have fetched data wait for any previous operation to finish. + if waitChan != nil { + <-waitChan + } + + sp.QueueMessage(tx.MsgTx(), doneChan) + + return nil +} + +// pushBlockMsg sends a block message for the provided block hash to the +// connected peer. An error is returned if the block hash is not known. +func (s *server) pushBlockMsg(sp *serverPeer, hash *wire.ShaHash, doneChan chan<- struct{}, waitChan <-chan struct{}) error { + // Fetch the raw block bytes from the database. + var blockBytes []byte + err := sp.server.db.View(func(dbTx database.Tx) error { + var err error + blockBytes, err = dbTx.FetchBlock(hash) + return err + }) + if err != nil { + peerLog.Tracef("Unable to fetch requested block hash %v: %v", + hash, err) + + if doneChan != nil { + doneChan <- struct{}{} + } + return err + } + + // Deserialize the block. + var msgBlock wire.MsgBlock + err = msgBlock.Deserialize(bytes.NewReader(blockBytes)) + if err != nil { + peerLog.Tracef("Unable to deserialize requested block hash "+ + "%v: %v", hash, err) + + if doneChan != nil { + doneChan <- struct{}{} + } + return err + } + + // Once we have fetched data wait for any previous operation to finish. + if waitChan != nil { + <-waitChan + } + + // We only send the channel for this message if we aren't sending + // an inv straight after. + var dc chan<- struct{} + continueHash := sp.continueHash + sendInv := continueHash != nil && continueHash.IsEqual(hash) + if !sendInv { + dc = doneChan + } + sp.QueueMessage(&msgBlock, dc) + + // When the peer requests the final block that was advertised in + // response to a getblocks message which requested more blocks than + // would fit into a single message, send it a new inventory message + // to trigger it to issue another getblocks message for the next + // batch of inventory. + if sendInv { + best := sp.server.blockManager.chain.BestSnapshot() + invMsg := wire.NewMsgInvSizeHint(1) + iv := wire.NewInvVect(wire.InvTypeBlock, best.Hash) + invMsg.AddInvVect(iv) + sp.QueueMessage(invMsg, doneChan) + sp.continueHash = nil + } + return nil +} + +// pushMerkleBlockMsg sends a merkleblock message for the provided block hash to +// the connected peer. Since a merkle block requires the peer to have a filter +// loaded, this call will simply be ignored if there is no filter loaded. An +// error is returned if the block hash is not known. +func (s *server) pushMerkleBlockMsg(sp *serverPeer, hash *wire.ShaHash, doneChan chan<- struct{}, waitChan <-chan struct{}) error { + // Do not send a response if the peer doesn't have a filter loaded. + if !sp.filter.IsLoaded() { + if doneChan != nil { + doneChan <- struct{}{} + } + return nil + } + + // Fetch the raw block bytes from the database. + blk, err := sp.server.blockManager.chain.BlockByHash(hash) + if err != nil { + peerLog.Tracef("Unable to fetch requested block hash %v: %v", + hash, err) + + if doneChan != nil { + doneChan <- struct{}{} + } + return err + } + + // Generate a merkle block by filtering the requested block according + // to the filter for the peer. + merkle, matchedTxIndices := bloom.NewMerkleBlock(blk, sp.filter) + + // Once we have fetched data wait for any previous operation to finish. + if waitChan != nil { + <-waitChan + } + + // Send the merkleblock. Only send the done channel with this message + // if no transactions will be sent afterwards. + var dc chan<- struct{} + if len(matchedTxIndices) == 0 { + dc = doneChan + } + sp.QueueMessage(merkle, dc) + + // Finally, send any matched transactions. + blkTransactions := blk.MsgBlock().Transactions + for i, txIndex := range matchedTxIndices { + // Only send the done channel on the final transaction. + var dc chan<- struct{} + if i == len(matchedTxIndices)-1 { + dc = doneChan + } + if txIndex < uint32(len(blkTransactions)) { + sp.QueueMessage(blkTransactions[txIndex], dc) + } + } + + return nil +} + +// handleUpdatePeerHeight updates the heights of all peers who were known to +// announce a block we recently accepted. +func (s *server) handleUpdatePeerHeights(state *peerState, umsg updatePeerHeightsMsg) { + state.forAllPeers(func(sp *serverPeer) { + // The origin peer should already have the updated height. + if sp == umsg.originPeer { + return + } + + // This is a pointer to the underlying memory which doesn't + // change. + latestBlkSha := sp.LastAnnouncedBlock() + + // Skip this peer if it hasn't recently announced any new blocks. + if latestBlkSha == nil { + return + } + + // If the peer has recently announced a block, and this block + // matches our newly accepted block, then update their block + // height. + if *latestBlkSha == *umsg.newSha { + sp.UpdateLastBlockHeight(umsg.newHeight) + sp.UpdateLastAnnouncedBlock(nil) + } + }) +} + +// handleAddPeerMsg deals with adding new peers. It is invoked from the +// peerHandler goroutine. +func (s *server) handleAddPeerMsg(state *peerState, sp *serverPeer) bool { + if sp == nil { + return false + } + + // Ignore new peers if we're shutting down. + if atomic.LoadInt32(&s.shutdown) != 0 { + srvrLog.Infof("New peer %s ignored - server is shutting down", sp) + sp.Disconnect() + return false + } + + // Disconnect banned peers. + host, _, err := net.SplitHostPort(sp.Addr()) + if err != nil { + srvrLog.Debugf("can't split hostport %v", err) + sp.Disconnect() + return false + } + if banEnd, ok := state.banned[host]; ok { + if time.Now().Before(banEnd) { + srvrLog.Debugf("Peer %s is banned for another %v - disconnecting", + host, banEnd.Sub(time.Now())) + sp.Disconnect() + return false + } + + srvrLog.Infof("Peer %s is no longer banned", host) + delete(state.banned, host) + } + + // TODO: Check for max peers from a single IP. + + // Limit max outbound peers. + if _, ok := state.pendingPeers[sp.Addr()]; ok { + if state.OutboundCount() >= state.maxOutboundPeers { + srvrLog.Infof("Max outbound peers reached [%d] - disconnecting "+ + "peer %s", state.maxOutboundPeers, sp) + sp.Disconnect() + return false + } + } + + // Limit max number of total peers. + if state.Count() >= cfg.MaxPeers { + srvrLog.Infof("Max peers reached [%d] - disconnecting peer %s", + cfg.MaxPeers, sp) + sp.Disconnect() + // TODO(oga) how to handle permanent peers here? + // they should be rescheduled. + return false + } + + // Add the new peer and start it. + srvrLog.Debugf("New peer %s", sp) + if sp.Inbound() { + state.inboundPeers[sp.ID()] = sp + } else { + state.outboundGroups[addrmgr.GroupKey(sp.NA())]++ + if sp.persistent { + state.persistentPeers[sp.ID()] = sp + } else { + state.outboundPeers[sp.ID()] = sp + } + // Remove from pending peers. + delete(state.pendingPeers, sp.Addr()) + } + + return true +} + +// handleDonePeerMsg deals with peers that have signalled they are done. It is +// invoked from the peerHandler goroutine. +func (s *server) handleDonePeerMsg(state *peerState, sp *serverPeer) { + if _, ok := state.pendingPeers[sp.Addr()]; ok { + delete(state.pendingPeers, sp.Addr()) + srvrLog.Debugf("Removed pending peer %s", sp) + return + } + + var list map[int32]*serverPeer + if sp.persistent { + list = state.persistentPeers + } else if sp.Inbound() { + list = state.inboundPeers + } else { + list = state.outboundPeers + } + if _, ok := list[sp.ID()]; ok { + // Issue an asynchronous reconnect if the peer was a + // persistent outbound connection. + if !sp.Inbound() && sp.persistent && atomic.LoadInt32(&s.shutdown) == 0 { + // Retry peer + sp2 := s.newOutboundPeer(sp.Addr(), sp.persistent) + if sp2 != nil { + go s.retryConn(sp2, false) + } + } + if !sp.Inbound() && sp.VersionKnown() { + state.outboundGroups[addrmgr.GroupKey(sp.NA())]-- + } + delete(list, sp.ID()) + srvrLog.Debugf("Removed peer %s", sp) + return + } + + // Update the address' last seen time if the peer has acknowledged + // our version and has sent us its version as well. + if sp.VerAckReceived() && sp.VersionKnown() && sp.NA() != nil { + s.addrManager.Connected(sp.NA()) + } + + // If we get here it means that either we didn't know about the peer + // or we purposefully deleted it. +} + +// handleBanPeerMsg deals with banning peers. It is invoked from the +// peerHandler goroutine. +func (s *server) handleBanPeerMsg(state *peerState, sp *serverPeer) { + host, _, err := net.SplitHostPort(sp.Addr()) + if err != nil { + srvrLog.Debugf("can't split ban peer %s %v", sp.Addr(), err) + return + } + direction := directionString(sp.Inbound()) + srvrLog.Infof("Banned peer %s (%s) for %v", host, direction, + cfg.BanDuration) + state.banned[host] = time.Now().Add(cfg.BanDuration) +} + +// handleRelayInvMsg deals with relaying inventory to peers that are not already +// known to have it. It is invoked from the peerHandler goroutine. +func (s *server) handleRelayInvMsg(state *peerState, msg relayMsg) { + state.forAllPeers(func(sp *serverPeer) { + if !sp.Connected() { + return + } + + if msg.invVect.Type == wire.InvTypeTx { + // Don't relay the transaction to the peer when it has + // transaction relaying disabled. + if sp.relayTxDisabled() { + return + } + // Don't relay the transaction if there is a bloom + // filter loaded and the transaction doesn't match it. + if sp.filter.IsLoaded() { + tx, ok := msg.data.(*btcutil.Tx) + if !ok { + peerLog.Warnf("Underlying data for tx" + + " inv relay is not a transaction") + return + } + + if !sp.filter.MatchTxAndUpdate(tx) { + return + } + } + } + + // Queue the inventory to be relayed with the next batch. + // It will be ignored if the peer is already known to + // have the inventory. + sp.QueueInventory(msg.invVect) + }) +} + +// handleBroadcastMsg deals with broadcasting messages to peers. It is invoked +// from the peerHandler goroutine. +func (s *server) handleBroadcastMsg(state *peerState, bmsg *broadcastMsg) { + state.forAllPeers(func(sp *serverPeer) { + if !sp.Connected() { + return + } + + for _, ep := range bmsg.excludePeers { + if sp == ep { + return + } + } + + sp.QueueMessage(bmsg.message, nil) + }) +} + +type getConnCountMsg struct { + reply chan int32 +} + +type getPeersMsg struct { + reply chan []*serverPeer +} + +type getAddedNodesMsg struct { + reply chan []*serverPeer +} + +type disconnectNodeMsg struct { + cmp func(*serverPeer) bool + reply chan error +} + +type connectNodeMsg struct { + addr string + permanent bool + reply chan error +} + +type removeNodeMsg struct { + cmp func(*serverPeer) bool + reply chan error +} + +// handleQuery is the central handler for all queries and commands from other +// goroutines related to peer state. +func (s *server) handleQuery(state *peerState, querymsg interface{}) { + switch msg := querymsg.(type) { + case getConnCountMsg: + nconnected := int32(0) + state.forAllPeers(func(sp *serverPeer) { + if sp.Connected() { + nconnected++ + } + }) + msg.reply <- nconnected + + case getPeersMsg: + peers := make([]*serverPeer, 0, state.Count()) + state.forAllPeers(func(sp *serverPeer) { + if !sp.Connected() { + return + } + peers = append(peers, sp) + }) + msg.reply <- peers + + case connectNodeMsg: + // XXX(oga) duplicate oneshots? + for _, peer := range state.persistentPeers { + if peer.Addr() == msg.addr { + if msg.permanent { + msg.reply <- errors.New("peer already connected") + } else { + msg.reply <- errors.New("peer exists as a permanent peer") + } + return + } + } + + // TODO(oga) if too many, nuke a non-perm peer. + sp := s.newOutboundPeer(msg.addr, msg.permanent) + if sp != nil { + go s.peerConnHandler(sp) + msg.reply <- nil + } else { + msg.reply <- errors.New("failed to add peer") + } + case removeNodeMsg: + found := disconnectPeer(state.persistentPeers, msg.cmp, func(sp *serverPeer) { + // Keep group counts ok since we remove from + // the list now. + state.outboundGroups[addrmgr.GroupKey(sp.NA())]-- + }) + + if found { + msg.reply <- nil + } else { + msg.reply <- errors.New("peer not found") + } + // Request a list of the persistent (added) peers. + case getAddedNodesMsg: + // Respond with a slice of the relavent peers. + peers := make([]*serverPeer, 0, len(state.persistentPeers)) + for _, sp := range state.persistentPeers { + peers = append(peers, sp) + } + msg.reply <- peers + case disconnectNodeMsg: + // Check inbound peers. We pass a nil callback since we don't + // require any additional actions on disconnect for inbound peers. + found := disconnectPeer(state.inboundPeers, msg.cmp, nil) + if found { + msg.reply <- nil + return + } + + // Check outbound peers. + found = disconnectPeer(state.outboundPeers, msg.cmp, func(sp *serverPeer) { + // Keep group counts ok since we remove from + // the list now. + state.outboundGroups[addrmgr.GroupKey(sp.NA())]-- + }) + if found { + // If there are multiple outbound connections to the same + // ip:port, continue disconnecting them all until no such + // peers are found. + for found { + found = disconnectPeer(state.outboundPeers, msg.cmp, func(sp *serverPeer) { + state.outboundGroups[addrmgr.GroupKey(sp.NA())]-- + }) + } + msg.reply <- nil + return + } + + msg.reply <- errors.New("peer not found") + } +} + +// disconnectPeer attempts to drop the connection of a tageted peer in the +// passed peer list. Targets are identified via usage of the passed +// `compareFunc`, which should return `true` if the passed peer is the target +// peer. This function returns true on success and false if the peer is unable +// to be located. If the peer is found, and the passed callback: `whenFound' +// isn't nil, we call it with the peer as the argument before it is removed +// from the peerList, and is disconnected from the server. +func disconnectPeer(peerList map[int32]*serverPeer, compareFunc func(*serverPeer) bool, whenFound func(*serverPeer)) bool { + for addr, peer := range peerList { + if compareFunc(peer) { + if whenFound != nil { + whenFound(peer) + } + + // This is ok because we are not continuing + // to iterate so won't corrupt the loop. + delete(peerList, addr) + peer.Disconnect() + return true + } + } + return false +} + +// newPeerConfig returns the configuration for the given serverPeer. +func newPeerConfig(sp *serverPeer) *peer.Config { + return &peer.Config{ + Listeners: peer.MessageListeners{ + OnVersion: sp.OnVersion, + OnMemPool: sp.OnMemPool, + OnTx: sp.OnTx, + OnBlock: sp.OnBlock, + OnInv: sp.OnInv, + OnHeaders: sp.OnHeaders, + OnGetData: sp.OnGetData, + OnGetBlocks: sp.OnGetBlocks, + OnGetHeaders: sp.OnGetHeaders, + OnFilterAdd: sp.OnFilterAdd, + OnFilterClear: sp.OnFilterClear, + OnFilterLoad: sp.OnFilterLoad, + OnGetAddr: sp.OnGetAddr, + OnAddr: sp.OnAddr, + OnRead: sp.OnRead, + OnWrite: sp.OnWrite, + + // Note: The reference client currently bans peers that send alerts + // not signed with its key. We could verify against their key, but + // since the reference client is currently unwilling to support + // other implementations' alert messages, we will not relay theirs. + OnAlert: nil, + }, + NewestBlock: sp.newestBlock, + BestLocalAddress: sp.server.addrManager.GetBestLocalAddress, + HostToNetAddress: sp.server.addrManager.HostToNetAddress, + Proxy: cfg.Proxy, + UserAgentName: userAgentName, + UserAgentVersion: userAgentVersion, + ChainParams: sp.server.chainParams, + Services: sp.server.services, + DisableRelayTx: cfg.BlocksOnly, + ProtocolVersion: 70011, + } +} + +// listenHandler is the main listener which accepts incoming connections for the +// server. It must be run as a goroutine. +func (s *server) listenHandler(listener net.Listener) { + srvrLog.Infof("Server listening on %s", listener.Addr()) + for atomic.LoadInt32(&s.shutdown) == 0 { + conn, err := listener.Accept() + if err != nil { + // Only log the error if we're not forcibly shutting down. + if atomic.LoadInt32(&s.shutdown) == 0 { + srvrLog.Errorf("Can't accept connection: %v", err) + } + continue + } + sp := newServerPeer(s, false) + sp.Peer = peer.NewInboundPeer(newPeerConfig(sp)) + go s.peerDoneHandler(sp) + if err := sp.Connect(conn); err != nil { + if atomic.LoadInt32(&s.shutdown) == 0 { + srvrLog.Errorf("Can't accept connection: %v", err) + } + continue + } + } + s.wg.Done() + srvrLog.Tracef("Listener handler done for %s", listener.Addr()) +} + +// seedFromDNS uses DNS seeding to populate the address manager with peers. +func (s *server) seedFromDNS() { + // Nothing to do if DNS seeding is disabled. + if cfg.DisableDNSSeed { + return + } + + for _, seeder := range activeNetParams.DNSSeeds { + go func(seeder string) { + randSource := mrand.New(mrand.NewSource(time.Now().UnixNano())) + + seedpeers, err := dnsDiscover(seeder) + if err != nil { + discLog.Infof("DNS discovery failed on seed %s: %v", seeder, err) + return + } + numPeers := len(seedpeers) + + discLog.Infof("%d addresses found from DNS seed %s", numPeers, seeder) + + if numPeers == 0 { + return + } + addresses := make([]*wire.NetAddress, len(seedpeers)) + // if this errors then we have *real* problems + intPort, _ := strconv.Atoi(activeNetParams.DefaultPort) + for i, peer := range seedpeers { + addresses[i] = new(wire.NetAddress) + addresses[i].SetAddress(peer, uint16(intPort)) + // bitcoind seeds with addresses from + // a time randomly selected between 3 + // and 7 days ago. + addresses[i].Timestamp = time.Now().Add(-1 * + time.Second * time.Duration(secondsIn3Days+ + randSource.Int31n(secondsIn4Days))) + } + + // Bitcoind uses a lookup of the dns seeder here. This + // is rather strange since the values looked up by the + // DNS seed lookups will vary quite a lot. + // to replicate this behaviour we put all addresses as + // having come from the first one. + s.addrManager.AddAddresses(addresses, addresses[0]) + }(seeder) + } +} + +// newOutboundPeer initializes a new outbound peer and setups the message +// listeners. +func (s *server) newOutboundPeer(addr string, persistent bool) *serverPeer { + sp := newServerPeer(s, persistent) + p, err := peer.NewOutboundPeer(newPeerConfig(sp), addr) + if err != nil { + srvrLog.Errorf("Cannot create outbound peer %s: %v", addr, err) + return nil + } + sp.Peer = p + go s.peerDoneHandler(sp) + return sp +} + +// peerConnHandler handles peer connections. It must be run in a goroutine. +func (s *server) peerConnHandler(sp *serverPeer) { + err := s.establishConn(sp) + if err != nil { + srvrLog.Debugf("Failed to connect to %s: %v", sp.Addr(), err) + sp.Disconnect() + } +} + +// peerDoneHandler handles peer disconnects by notifiying the server that it's +// done. +func (s *server) peerDoneHandler(sp *serverPeer) { + sp.WaitForDisconnect() + s.donePeers <- sp + + // Only tell block manager we are gone if we ever told it we existed. + if sp.VersionKnown() { + s.blockManager.DonePeer(sp) + } + close(sp.quit) +} + +// establishConn establishes a connection to the peer. +func (s *server) establishConn(sp *serverPeer) error { + srvrLog.Debugf("Attempting to connect to %s", sp.Addr()) + conn, err := btcdDial("tcp", sp.Addr()) + if err != nil { + return err + } + if err := sp.Connect(conn); err != nil { + return err + } + srvrLog.Debugf("Connected to %s", sp.Addr()) + s.addrManager.Attempt(sp.NA()) + return nil +} + +// retryConn retries connection to the peer after the given duration. It must +// be run as a goroutine. +func (s *server) retryConn(sp *serverPeer, initialAttempt bool) { + retryDuration := connectionRetryInterval + for { + if initialAttempt { + retryDuration = 0 + initialAttempt = false + } else { + srvrLog.Debugf("Retrying connection to %s in %s", sp.Addr(), + retryDuration) + } + select { + case <-time.After(retryDuration): + err := s.establishConn(sp) + if err != nil { + retryDuration += connectionRetryInterval + if retryDuration > maxConnectionRetryInterval { + retryDuration = maxConnectionRetryInterval + } + continue + } + return + + case <-sp.quit: + return + + case <-s.quit: + return + } + } +} + +// peerHandler is used to handle peer operations such as adding and removing +// peers to and from the server, banning peers, and broadcasting messages to +// peers. It must be run in a goroutine. +func (s *server) peerHandler() { + // Start the address manager and block manager, both of which are needed + // by peers. This is done here since their lifecycle is closely tied + // to this handler and rather than adding more channels to sychronize + // things, it's easier and slightly faster to simply start and stop them + // in this handler. + s.addrManager.Start() + s.blockManager.Start() + + srvrLog.Tracef("Starting peer handler") + + state := &peerState{ + pendingPeers: make(map[string]*serverPeer), + inboundPeers: make(map[int32]*serverPeer), + persistentPeers: make(map[int32]*serverPeer), + outboundPeers: make(map[int32]*serverPeer), + banned: make(map[string]time.Time), + maxOutboundPeers: defaultMaxOutbound, + outboundGroups: make(map[string]int), + } + if cfg.MaxPeers < state.maxOutboundPeers { + state.maxOutboundPeers = cfg.MaxPeers + } + // Add peers discovered through DNS to the address manager. + s.seedFromDNS() + + // Start up persistent peers. + permanentPeers := cfg.ConnectPeers + if len(permanentPeers) == 0 { + permanentPeers = cfg.AddPeers + } + for _, addr := range permanentPeers { + sp := s.newOutboundPeer(addr, true) + if sp != nil { + go s.retryConn(sp, true) + } + } + + // if nothing else happens, wake us up soon. + time.AfterFunc(10*time.Second, func() { s.wakeup <- struct{}{} }) + +out: + for { + select { + // New peers connected to the server. + case p := <-s.newPeers: + s.handleAddPeerMsg(state, p) + + // Disconnected peers. + case p := <-s.donePeers: + s.handleDonePeerMsg(state, p) + + // Block accepted in mainchain or orphan, update peer height. + case umsg := <-s.peerHeightsUpdate: + s.handleUpdatePeerHeights(state, umsg) + + // Peer to ban. + case p := <-s.banPeers: + s.handleBanPeerMsg(state, p) + + // New inventory to potentially be relayed to other peers. + case invMsg := <-s.relayInv: + s.handleRelayInvMsg(state, invMsg) + + // Message to broadcast to all connected peers except those + // which are excluded by the message. + case bmsg := <-s.broadcast: + s.handleBroadcastMsg(state, &bmsg) + + // Used by timers below to wake us back up. + case <-s.wakeup: + // this page left intentionally blank + + case qmsg := <-s.query: + s.handleQuery(state, qmsg) + + case <-s.quit: + // Disconnect all peers on server shutdown. + state.forAllPeers(func(sp *serverPeer) { + srvrLog.Tracef("Shutdown peer %s", sp) + sp.Disconnect() + }) + break out + } + + // Don't try to connect to more peers when running on the + // simulation test network. The simulation network is only + // intended to connect to specified peers and actively avoid + // advertising and connecting to discovered peers. + if cfg.SimNet { + continue + } + + // Only try connect to more peers if we actually need more. + if !state.NeedMoreOutbound() || len(cfg.ConnectPeers) > 0 || + atomic.LoadInt32(&s.shutdown) != 0 { + state.forPendingPeers(func(sp *serverPeer) { + srvrLog.Tracef("Shutdown peer %s", sp) + sp.Disconnect() + }) + continue + } + tries := 0 + for state.NeedMoreOutbound() && + state.NeedMoreTries() && + atomic.LoadInt32(&s.shutdown) == 0 { + addr := s.addrManager.GetAddress("any") + if addr == nil { + break + } + key := addrmgr.GroupKey(addr.NetAddress()) + // Address will not be invalid, local or unroutable + // because addrmanager rejects those on addition. + // Just check that we don't already have an address + // in the same group so that we are not connecting + // to the same network segment at the expense of + // others. + if state.outboundGroups[key] != 0 { + break + } + + // Check that we don't have a pending connection to this addr. + addrStr := addrmgr.NetAddressKey(addr.NetAddress()) + if _, ok := state.pendingPeers[addrStr]; ok { + continue + } + + tries++ + // After 100 bad tries exit the loop and we'll try again + // later. + if tries > 100 { + break + } + + // XXX if we have limited that address skip + + // only allow recent nodes (10mins) after we failed 30 + // times + if tries < 30 && time.Now().Sub(addr.LastAttempt()) < 10*time.Minute { + continue + } + + // allow nondefault ports after 50 failed tries. + if fmt.Sprintf("%d", addr.NetAddress().Port) != + activeNetParams.DefaultPort && tries < 50 { + continue + } + + tries = 0 + sp := s.newOutboundPeer(addrStr, false) + if sp != nil { + go s.peerConnHandler(sp) + state.pendingPeers[sp.Addr()] = sp + } + } + + // We need more peers, wake up in ten seconds and try again. + if state.NeedMoreOutbound() { + time.AfterFunc(10*time.Second, func() { + s.wakeup <- struct{}{} + }) + } + } + + s.blockManager.Stop() + s.addrManager.Stop() + + // Drain channels before exiting so nothing is left waiting around + // to send. +cleanup: + for { + select { + case <-s.newPeers: + case <-s.donePeers: + case <-s.peerHeightsUpdate: + case <-s.relayInv: + case <-s.broadcast: + case <-s.wakeup: + case <-s.query: + default: + break cleanup + } + } + s.wg.Done() + srvrLog.Tracef("Peer handler done") +} + +// AddPeer adds a new peer that has already been connected to the server. +func (s *server) AddPeer(sp *serverPeer) { + s.newPeers <- sp +} + +// BanPeer bans a peer that has already been connected to the server by ip. +func (s *server) BanPeer(sp *serverPeer) { + s.banPeers <- sp +} + +// RelayInventory relays the passed inventory vector to all connected peers +// that are not already known to have it. +func (s *server) RelayInventory(invVect *wire.InvVect, data interface{}) { + s.relayInv <- relayMsg{invVect: invVect, data: data} +} + +// BroadcastMessage sends msg to all peers currently connected to the server +// except those in the passed peers to exclude. +func (s *server) BroadcastMessage(msg wire.Message, exclPeers ...*serverPeer) { + // XXX: Need to determine if this is an alert that has already been + // broadcast and refrain from broadcasting again. + bmsg := broadcastMsg{message: msg, excludePeers: exclPeers} + s.broadcast <- bmsg +} + +// ConnectedCount returns the number of currently connected peers. +func (s *server) ConnectedCount() int32 { + replyChan := make(chan int32) + + s.query <- getConnCountMsg{reply: replyChan} + + return <-replyChan +} + +// AddedNodeInfo returns an array of btcjson.GetAddedNodeInfoResult structures +// describing the persistent (added) nodes. +func (s *server) AddedNodeInfo() []*serverPeer { + replyChan := make(chan []*serverPeer) + s.query <- getAddedNodesMsg{reply: replyChan} + return <-replyChan +} + +// Peers returns an array of all connected peers. +func (s *server) Peers() []*serverPeer { + replyChan := make(chan []*serverPeer) + + s.query <- getPeersMsg{reply: replyChan} + + return <-replyChan +} + +// DisconnectNodeByAddr disconnects a peer by target address. Both outbound and +// inbound nodes will be searched for the target node. An error message will +// be returned if the peer was not found. +func (s *server) DisconnectNodeByAddr(addr string) error { + replyChan := make(chan error) + + s.query <- disconnectNodeMsg{ + cmp: func(sp *serverPeer) bool { return sp.Addr() == addr }, + reply: replyChan, + } + + return <-replyChan +} + +// DisconnectNodeByID disconnects a peer by target node id. Both outbound and +// inbound nodes will be searched for the target node. An error message will be +// returned if the peer was not found. +func (s *server) DisconnectNodeByID(id int32) error { + replyChan := make(chan error) + + s.query <- disconnectNodeMsg{ + cmp: func(sp *serverPeer) bool { return sp.ID() == id }, + reply: replyChan, + } + + return <-replyChan +} + +// RemoveNodeByAddr removes a peer from the list of persistent peers if +// present. An error will be returned if the peer was not found. +func (s *server) RemoveNodeByAddr(addr string) error { + replyChan := make(chan error) + + s.query <- removeNodeMsg{ + cmp: func(sp *serverPeer) bool { return sp.Addr() == addr }, + reply: replyChan, + } + + return <-replyChan +} + +// RemoveNodeByID removes a peer by node ID from the list of persistent peers +// if present. An error will be returned if the peer was not found. +func (s *server) RemoveNodeByID(id int32) error { + replyChan := make(chan error) + + s.query <- removeNodeMsg{ + cmp: func(sp *serverPeer) bool { return sp.ID() == id }, + reply: replyChan, + } + + return <-replyChan +} + +// ConnectNode adds `addr' as a new outbound peer. If permanent is true then the +// peer will be persistent and reconnect if the connection is lost. +// It is an error to call this with an already existing peer. +func (s *server) ConnectNode(addr string, permanent bool) error { + replyChan := make(chan error) + + s.query <- connectNodeMsg{addr: addr, permanent: permanent, reply: replyChan} + + return <-replyChan +} + +// AddBytesSent adds the passed number of bytes to the total bytes sent counter +// for the server. It is safe for concurrent access. +func (s *server) AddBytesSent(bytesSent uint64) { + atomic.AddUint64(&s.bytesSent, bytesSent) +} + +// AddBytesReceived adds the passed number of bytes to the total bytes received +// counter for the server. It is safe for concurrent access. +func (s *server) AddBytesReceived(bytesReceived uint64) { + atomic.AddUint64(&s.bytesReceived, bytesReceived) +} + +// NetTotals returns the sum of all bytes received and sent across the network +// for all peers. It is safe for concurrent access. +func (s *server) NetTotals() (uint64, uint64) { + return atomic.LoadUint64(&s.bytesReceived), + atomic.LoadUint64(&s.bytesSent) +} + +// UpdatePeerHeights updates the heights of all peers who have have announced +// the latest connected main chain block, or a recognized orphan. These height +// updates allow us to dynamically refresh peer heights, ensuring sync peer +// selection has access to the latest block heights for each peer. +func (s *server) UpdatePeerHeights(latestBlkSha *wire.ShaHash, latestHeight int32, updateSource *serverPeer) { + s.peerHeightsUpdate <- updatePeerHeightsMsg{ + newSha: latestBlkSha, + newHeight: latestHeight, + originPeer: updateSource, + } +} + +// rebroadcastHandler keeps track of user submitted inventories that we have +// sent out but have not yet made it into a block. We periodically rebroadcast +// them in case our peers restarted or otherwise lost track of them. +func (s *server) rebroadcastHandler() { + // Wait 5 min before first tx rebroadcast. + timer := time.NewTimer(5 * time.Minute) + pendingInvs := make(map[wire.InvVect]interface{}) + +out: + for { + select { + case riv := <-s.modifyRebroadcastInv: + switch msg := riv.(type) { + // Incoming InvVects are added to our map of RPC txs. + case broadcastInventoryAdd: + pendingInvs[*msg.invVect] = msg.data + + // When an InvVect has been added to a block, we can + // now remove it, if it was present. + case broadcastInventoryDel: + if _, ok := pendingInvs[*msg]; ok { + delete(pendingInvs, *msg) + } + } + + case <-timer.C: + // Any inventory we have has not made it into a block + // yet. We periodically resubmit them until they have. + for iv, data := range pendingInvs { + ivCopy := iv + s.RelayInventory(&ivCopy, data) + } + + // Process at a random time up to 30mins (in seconds) + // in the future. + timer.Reset(time.Second * + time.Duration(randomUint16Number(1800))) + + case <-s.quit: + break out + } + } + + timer.Stop() + + // Drain channels before exiting so nothing is left waiting around + // to send. +cleanup: + for { + select { + case <-s.modifyRebroadcastInv: + default: + break cleanup + } + } + s.wg.Done() +} + +// Start begins accepting connections from peers. +func (s *server) Start() { + // Already started? + if atomic.AddInt32(&s.started, 1) != 1 { + return + } + + srvrLog.Trace("Starting server") + + // Start all the listeners. There will not be any if listening is + // disabled. + for _, listener := range s.listeners { + s.wg.Add(1) + go s.listenHandler(listener) + } + + // Start the peer handler which in turn starts the address and block + // managers. + s.wg.Add(1) + go s.peerHandler() + + if s.nat != nil { + s.wg.Add(1) + go s.upnpUpdateThread() + } + + if !cfg.DisableRPC { + s.wg.Add(1) + + // Start the rebroadcastHandler, which ensures user tx received by + // the RPC server are rebroadcast until being included in a block. + go s.rebroadcastHandler() + + s.rpcServer.Start() + } + + // Start the CPU miner if generation is enabled. + if cfg.Generate { + s.cpuMiner.Start() + } +} + +// Stop gracefully shuts down the server by stopping and disconnecting all +// peers and the main listener. +func (s *server) Stop() error { + // Make sure this only happens once. + if atomic.AddInt32(&s.shutdown, 1) != 1 { + srvrLog.Infof("Server is already in the process of shutting down") + return nil + } + + srvrLog.Warnf("Server shutting down") + + // Stop all the listeners. There will not be any listeners if + // listening is disabled. + for _, listener := range s.listeners { + err := listener.Close() + if err != nil { + return err + } + } + + // Stop the CPU miner if needed + s.cpuMiner.Stop() + + // Shutdown the RPC server if it's not disabled. + if !cfg.DisableRPC { + s.rpcServer.Stop() + } + + // Signal the remaining goroutines to quit. + close(s.quit) + return nil +} + +// WaitForShutdown blocks until the main listener and peer handlers are stopped. +func (s *server) WaitForShutdown() { + s.wg.Wait() +} + +// ScheduleShutdown schedules a server shutdown after the specified duration. +// It also dynamically adjusts how often to warn the server is going down based +// on remaining duration. +func (s *server) ScheduleShutdown(duration time.Duration) { + // Don't schedule shutdown more than once. + if atomic.AddInt32(&s.shutdownSched, 1) != 1 { + return + } + srvrLog.Warnf("Server shutdown in %v", duration) + go func() { + remaining := duration + tickDuration := dynamicTickDuration(remaining) + done := time.After(remaining) + ticker := time.NewTicker(tickDuration) + out: + for { + select { + case <-done: + ticker.Stop() + s.Stop() + break out + case <-ticker.C: + remaining = remaining - tickDuration + if remaining < time.Second { + continue + } + + // Change tick duration dynamically based on remaining time. + newDuration := dynamicTickDuration(remaining) + if tickDuration != newDuration { + tickDuration = newDuration + ticker.Stop() + ticker = time.NewTicker(tickDuration) + } + srvrLog.Warnf("Server shutdown in %v", remaining) + } + } + }() +} + +// parseListeners splits the list of listen addresses passed in addrs into +// IPv4 and IPv6 slices and returns them. This allows easy creation of the +// listeners on the correct interface "tcp4" and "tcp6". It also properly +// detects addresses which apply to "all interfaces" and adds the address to +// both slices. +func parseListeners(addrs []string) ([]string, []string, bool, error) { + ipv4ListenAddrs := make([]string, 0, len(addrs)*2) + ipv6ListenAddrs := make([]string, 0, len(addrs)*2) + haveWildcard := false + + for _, addr := range addrs { + host, _, err := net.SplitHostPort(addr) + if err != nil { + // Shouldn't happen due to already being normalized. + return nil, nil, false, err + } + + // Empty host or host of * on plan9 is both IPv4 and IPv6. + if host == "" || (host == "*" && runtime.GOOS == "plan9") { + ipv4ListenAddrs = append(ipv4ListenAddrs, addr) + ipv6ListenAddrs = append(ipv6ListenAddrs, addr) + haveWildcard = true + continue + } + + // Strip IPv6 zone id if present since net.ParseIP does not + // handle it. + zoneIndex := strings.LastIndex(host, "%") + if zoneIndex > 0 { + host = host[:zoneIndex] + } + + // Parse the IP. + ip := net.ParseIP(host) + if ip == nil { + return nil, nil, false, fmt.Errorf("'%s' is not a "+ + "valid IP address", host) + } + + // To4 returns nil when the IP is not an IPv4 address, so use + // this determine the address type. + if ip.To4() == nil { + ipv6ListenAddrs = append(ipv6ListenAddrs, addr) + } else { + ipv4ListenAddrs = append(ipv4ListenAddrs, addr) + } + } + return ipv4ListenAddrs, ipv6ListenAddrs, haveWildcard, nil +} + +func (s *server) upnpUpdateThread() { + // Go off immediately to prevent code duplication, thereafter we renew + // lease every 15 minutes. + timer := time.NewTimer(0 * time.Second) + lport, _ := strconv.ParseInt(activeNetParams.DefaultPort, 10, 16) + first := true +out: + for { + select { + case <-timer.C: + // TODO(oga) pick external port more cleverly + // TODO(oga) know which ports we are listening to on an external net. + // TODO(oga) if specific listen port doesn't work then ask for wildcard + // listen port? + // XXX this assumes timeout is in seconds. + listenPort, err := s.nat.AddPortMapping("tcp", int(lport), int(lport), + "btcd listen port", 20*60) + if err != nil { + srvrLog.Warnf("can't add UPnP port mapping: %v", err) + } + if first && err == nil { + // TODO(oga): look this up periodically to see if upnp domain changed + // and so did ip. + externalip, err := s.nat.GetExternalAddress() + if err != nil { + srvrLog.Warnf("UPnP can't get external address: %v", err) + continue out + } + na := wire.NewNetAddressIPPort(externalip, uint16(listenPort), + s.services) + err = s.addrManager.AddLocalAddress(na, addrmgr.UpnpPrio) + if err != nil { + // XXX DeletePortMapping? + } + srvrLog.Warnf("Successfully bound via UPnP to %s", addrmgr.NetAddressKey(na)) + first = false + } + timer.Reset(time.Minute * 15) + case <-s.quit: + break out + } + } + + timer.Stop() + + if err := s.nat.DeletePortMapping("tcp", int(lport), int(lport)); err != nil { + srvrLog.Warnf("unable to remove UPnP port mapping: %v", err) + } else { + srvrLog.Debugf("succesfully disestablished UPnP port mapping") + } + + s.wg.Done() +} + +// newServer returns a new btcd server configured to listen on addr for the +// bitcoin network type specified by chainParams. Use start to begin accepting +// connections from peers. +func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Params) (*server, error) { + services := defaultServices + if cfg.NoPeerBloomFilters { + services &^= wire.SFNodeBloom + } + + amgr := addrmgr.New(cfg.DataDir, btcdLookup) + + var listeners []net.Listener + var nat NAT + if !cfg.DisableListen { + ipv4Addrs, ipv6Addrs, wildcard, err := + parseListeners(listenAddrs) + if err != nil { + return nil, err + } + listeners = make([]net.Listener, 0, len(ipv4Addrs)+len(ipv6Addrs)) + discover := true + if len(cfg.ExternalIPs) != 0 { + discover = false + // if this fails we have real issues. + port, _ := strconv.ParseUint( + activeNetParams.DefaultPort, 10, 16) + + for _, sip := range cfg.ExternalIPs { + eport := uint16(port) + host, portstr, err := net.SplitHostPort(sip) + if err != nil { + // no port, use default. + host = sip + } else { + port, err := strconv.ParseUint( + portstr, 10, 16) + if err != nil { + srvrLog.Warnf("Can not parse "+ + "port from %s for "+ + "externalip: %v", sip, + err) + continue + } + eport = uint16(port) + } + na, err := amgr.HostToNetAddress(host, eport, + services) + if err != nil { + srvrLog.Warnf("Not adding %s as "+ + "externalip: %v", sip, err) + continue + } + + err = amgr.AddLocalAddress(na, addrmgr.ManualPrio) + if err != nil { + amgrLog.Warnf("Skipping specified external IP: %v", err) + } + } + } else if discover && cfg.Upnp { + nat, err = Discover() + if err != nil { + srvrLog.Warnf("Can't discover upnp: %v", err) + } + // nil nat here is fine, just means no upnp on network. + } + + // TODO(oga) nonstandard port... + if wildcard { + port, err := + strconv.ParseUint(activeNetParams.DefaultPort, + 10, 16) + if err != nil { + // I can't think of a cleaner way to do this... + goto nowc + } + addrs, err := net.InterfaceAddrs() + for _, a := range addrs { + ip, _, err := net.ParseCIDR(a.String()) + if err != nil { + continue + } + na := wire.NewNetAddressIPPort(ip, + uint16(port), services) + if discover { + err = amgr.AddLocalAddress(na, addrmgr.InterfacePrio) + if err != nil { + amgrLog.Debugf("Skipping local address: %v", err) + } + } + } + } + nowc: + + for _, addr := range ipv4Addrs { + listener, err := net.Listen("tcp4", addr) + if err != nil { + srvrLog.Warnf("Can't listen on %s: %v", addr, + err) + continue + } + listeners = append(listeners, listener) + + if discover { + if na, err := amgr.DeserializeNetAddress(addr); err == nil { + err = amgr.AddLocalAddress(na, addrmgr.BoundPrio) + if err != nil { + amgrLog.Warnf("Skipping bound address: %v", err) + } + } + } + } + + for _, addr := range ipv6Addrs { + listener, err := net.Listen("tcp6", addr) + if err != nil { + srvrLog.Warnf("Can't listen on %s: %v", addr, + err) + continue + } + listeners = append(listeners, listener) + if discover { + if na, err := amgr.DeserializeNetAddress(addr); err == nil { + err = amgr.AddLocalAddress(na, addrmgr.BoundPrio) + if err != nil { + amgrLog.Debugf("Skipping bound address: %v", err) + } + } + } + } + + if len(listeners) == 0 { + return nil, errors.New("no valid listen address") + } + } + + s := server{ + listeners: listeners, + chainParams: chainParams, + addrManager: amgr, + newPeers: make(chan *serverPeer, cfg.MaxPeers), + donePeers: make(chan *serverPeer, cfg.MaxPeers), + banPeers: make(chan *serverPeer, cfg.MaxPeers), + retryPeers: make(chan *serverPeer, cfg.MaxPeers), + wakeup: make(chan struct{}), + query: make(chan interface{}), + relayInv: make(chan relayMsg, cfg.MaxPeers), + broadcast: make(chan broadcastMsg, cfg.MaxPeers), + quit: make(chan struct{}), + modifyRebroadcastInv: make(chan interface{}), + peerHeightsUpdate: make(chan updatePeerHeightsMsg), + nat: nat, + db: db, + timeSource: blockchain.NewMedianTime(), + services: services, + sigCache: txscript.NewSigCache(cfg.SigCacheMaxSize), + } + + // Create the transaction and address indexes if needed. + // + // CAUTION: the txindex needs to be first in the indexes array because + // the addrindex uses data from the txindex during catchup. If the + // addrindex is run first, it may not have the transactions from the + // current block indexed. + var indexes []indexers.Indexer + if cfg.TxIndex || cfg.AddrIndex { + // Enable transaction index if address index is enabled since it + // requires it. + if !cfg.TxIndex { + indxLog.Infof("Transaction index enabled because it " + + "is required by the address index") + cfg.TxIndex = true + } else { + indxLog.Info("Transaction index is enabled") + } + + s.txIndex = indexers.NewTxIndex(db) + indexes = append(indexes, s.txIndex) + } + if cfg.AddrIndex { + indxLog.Info("Address index is enabled") + s.addrIndex = indexers.NewAddrIndex(db, chainParams) + indexes = append(indexes, s.addrIndex) + } + + // Create an index manager if any of the optional indexes are enabled. + var indexManager blockchain.IndexManager + if len(indexes) > 0 { + indexManager = indexers.NewManager(db, indexes) + } + bm, err := newBlockManager(&s, indexManager) + if err != nil { + return nil, err + } + s.blockManager = bm + + txC := mempoolConfig{ + Policy: mempoolPolicy{ + DisableRelayPriority: cfg.NoRelayPriority, + FreeTxRelayLimit: cfg.FreeTxRelayLimit, + MaxOrphanTxs: cfg.MaxOrphanTxs, + MaxOrphanTxSize: defaultMaxOrphanTxSize, + MaxSigOpsPerTx: blockchain.MaxSigOpsPerBlock / 5, + MinRelayTxFee: cfg.minRelayTxFee, + }, + FetchUtxoView: s.blockManager.chain.FetchUtxoView, + Chain: s.blockManager.chain, + SigCache: s.sigCache, + TimeSource: s.timeSource, + AddrIndex: s.addrIndex, + } + s.txMemPool = newTxMemPool(&txC) + + // Create the mining policy based on the configuration options. + // NOTE: The CPU miner relies on the mempool, so the mempool has to be + // created before calling the function to create the CPU miner. + policy := mining.Policy{ + BlockMinSize: cfg.BlockMinSize, + BlockMaxSize: cfg.BlockMaxSize, + BlockPrioritySize: cfg.BlockPrioritySize, + TxMinFreeFee: cfg.minRelayTxFee, + } + s.cpuMiner = newCPUMiner(&policy, &s) + + if !cfg.DisableRPC { + s.rpcServer, err = newRPCServer(cfg.RPCListeners, &policy, &s) + if err != nil { + return nil, err + } + } + + return &s, nil +} + +// dynamicTickDuration is a convenience function used to dynamically choose a +// tick duration based on remaining time. It is primarily used during +// server shutdown to make shutdown warnings more frequent as the shutdown time +// approaches. +func dynamicTickDuration(remaining time.Duration) time.Duration { + switch { + case remaining <= time.Second*5: + return time.Second + case remaining <= time.Second*15: + return time.Second * 5 + case remaining <= time.Minute: + return time.Second * 15 + case remaining <= time.Minute*5: + return time.Minute + case remaining <= time.Minute*15: + return time.Minute * 5 + case remaining <= time.Hour: + return time.Minute * 15 + } + return time.Hour +} diff --git a/vendor/github.com/btcsuite/btcd/service_windows.go b/vendor/github.com/btcsuite/btcd/service_windows.go new file mode 100644 index 0000000000000000000000000000000000000000..cb4212bc1ad00aea4cbcb2176e50678d173b32bb --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/service_windows.go @@ -0,0 +1,327 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "path/filepath" + "time" + + "github.com/btcsuite/winsvc/eventlog" + "github.com/btcsuite/winsvc/mgr" + "github.com/btcsuite/winsvc/svc" +) + +const ( + // svcName is the name of btcd service. + svcName = "btcdsvc" + + // svcDisplayName is the service name that will be shown in the windows + // services list. Not the svcName is the "real" name which is used + // to control the service. This is only for display purposes. + svcDisplayName = "Btcd Service" + + // svcDesc is the description of the service. + svcDesc = "Downloads and stays synchronized with the bitcoin block " + + "chain and provides chain services to applications." +) + +// elog is used to send messages to the Windows event log. +var elog *eventlog.Log + +// logServiceStartOfDay logs information about btcd when the main server has +// been started to the Windows event log. +func logServiceStartOfDay(srvr *server) { + var message string + message += fmt.Sprintf("Version %s\n", version()) + message += fmt.Sprintf("Configuration directory: %s\n", btcdHomeDir) + message += fmt.Sprintf("Configuration file: %s\n", cfg.ConfigFile) + message += fmt.Sprintf("Data directory: %s\n", cfg.DataDir) + + elog.Info(1, message) +} + +// btcdService houses the main service handler which handles all service +// updates and launching btcdMain. +type btcdService struct{} + +// Execute is the main entry point the winsvc package calls when receiving +// information from the Windows service control manager. It launches the +// long-running btcdMain (which is the real meat of btcd), handles service +// change requests, and notifies the service control manager of changes. +func (s *btcdService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (bool, uint32) { + // Service start is pending. + const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown + changes <- svc.Status{State: svc.StartPending} + + // Start btcdMain in a separate goroutine so the service can start + // quickly. Shutdown (along with a potential error) is reported via + // doneChan. serverChan is notified with the main server instance once + // it is started so it can be gracefully stopped. + doneChan := make(chan error) + serverChan := make(chan *server) + go func() { + err := btcdMain(serverChan) + doneChan <- err + }() + + // Service is now started. + changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} + + var mainServer *server +loop: + for { + select { + case c := <-r: + switch c.Cmd { + case svc.Interrogate: + changes <- c.CurrentStatus + + case svc.Stop, svc.Shutdown: + // Service stop is pending. Don't accept any + // more commands while pending. + changes <- svc.Status{State: svc.StopPending} + + // Stop the main server gracefully when it is + // already setup or just break out and allow + // the service to exit immediately if it's not + // setup yet. Note that calling Stop will cause + // btcdMain to exit in the goroutine above which + // will in turn send a signal (and a potential + // error) to doneChan. + if mainServer != nil { + mainServer.Stop() + } else { + break loop + } + + default: + elog.Error(1, fmt.Sprintf("Unexpected control "+ + "request #%d.", c)) + } + + case srvr := <-serverChan: + mainServer = srvr + logServiceStartOfDay(mainServer) + + case err := <-doneChan: + if err != nil { + elog.Error(1, err.Error()) + } + break loop + } + } + + // Service is now stopped. + changes <- svc.Status{State: svc.Stopped} + return false, 0 +} + +// installService attempts to install the btcd service. Typically this should +// be done by the msi installer, but it is provided here since it can be useful +// for development. +func installService() error { + // Get the path of the current executable. This is needed because + // os.Args[0] can vary depending on how the application was launched. + // For example, under cmd.exe it will only be the name of the app + // without the path or extension, but under mingw it will be the full + // path including the extension. + exePath, err := filepath.Abs(os.Args[0]) + if err != nil { + return err + } + if filepath.Ext(exePath) == "" { + exePath += ".exe" + } + + // Connect to the windows service manager. + serviceManager, err := mgr.Connect() + if err != nil { + return err + } + defer serviceManager.Disconnect() + + // Ensure the service doesn't already exist. + service, err := serviceManager.OpenService(svcName) + if err == nil { + service.Close() + return fmt.Errorf("service %s already exists", svcName) + } + + // Install the service. + service, err = serviceManager.CreateService(svcName, exePath, mgr.Config{ + DisplayName: svcDisplayName, + Description: svcDesc, + }) + if err != nil { + return err + } + defer service.Close() + + // Support events to the event log using the standard "standard" Windows + // EventCreate.exe message file. This allows easy logging of custom + // messges instead of needing to create our own message catalog. + eventlog.Remove(svcName) + eventsSupported := uint32(eventlog.Error | eventlog.Warning | eventlog.Info) + err = eventlog.InstallAsEventCreate(svcName, eventsSupported) + if err != nil { + return err + } + + return nil +} + +// removeService attempts to uninstall the btcd service. Typically this should +// be done by the msi uninstaller, but it is provided here since it can be +// useful for development. Not the eventlog entry is intentionally not removed +// since it would invalidate any existing event log messages. +func removeService() error { + // Connect to the windows service manager. + serviceManager, err := mgr.Connect() + if err != nil { + return err + } + defer serviceManager.Disconnect() + + // Ensure the service exists. + service, err := serviceManager.OpenService(svcName) + if err != nil { + return fmt.Errorf("service %s is not installed", svcName) + } + defer service.Close() + + // Remove the service. + err = service.Delete() + if err != nil { + return err + } + + return nil +} + +// startService attempts to start the btcd service. +func startService() error { + // Connect to the windows service manager. + serviceManager, err := mgr.Connect() + if err != nil { + return err + } + defer serviceManager.Disconnect() + + service, err := serviceManager.OpenService(svcName) + if err != nil { + return fmt.Errorf("could not access service: %v", err) + } + defer service.Close() + + err = service.Start(os.Args) + if err != nil { + return fmt.Errorf("could not start service: %v", err) + } + + return nil +} + +// controlService allows commands which change the status of the service. It +// also waits for up to 10 seconds for the service to change to the passed +// state. +func controlService(c svc.Cmd, to svc.State) error { + // Connect to the windows service manager. + serviceManager, err := mgr.Connect() + if err != nil { + return err + } + defer serviceManager.Disconnect() + + service, err := serviceManager.OpenService(svcName) + if err != nil { + return fmt.Errorf("could not access service: %v", err) + } + defer service.Close() + + status, err := service.Control(c) + if err != nil { + return fmt.Errorf("could not send control=%d: %v", c, err) + } + + // Send the control message. + timeout := time.Now().Add(10 * time.Second) + for status.State != to { + if timeout.Before(time.Now()) { + return fmt.Errorf("timeout waiting for service to go "+ + "to state=%d", to) + } + time.Sleep(300 * time.Millisecond) + status, err = service.Query() + if err != nil { + return fmt.Errorf("could not retrieve service "+ + "status: %v", err) + } + } + + return nil +} + +// performServiceCommand attempts to run one of the supported service commands +// provided on the command line via the service command flag. An appropriate +// error is returned if an invalid command is specified. +func performServiceCommand(command string) error { + var err error + switch command { + case "install": + err = installService() + + case "remove": + err = removeService() + + case "start": + err = startService() + + case "stop": + err = controlService(svc.Stop, svc.Stopped) + + default: + err = fmt.Errorf("invalid service command [%s]", command) + } + + return err +} + +// serviceMain checks whether we're being invoked as a service, and if so uses +// the service control manager to start the long-running server. A flag is +// returned to the caller so the application can determine whether to exit (when +// running as a service) or launch in normal interactive mode. +func serviceMain() (bool, error) { + // Don't run as a service if we're running interactively (or that can't + // be determined due to an error). + isInteractive, err := svc.IsAnInteractiveSession() + if err != nil { + return false, err + } + if isInteractive { + return false, nil + } + + elog, err = eventlog.Open(svcName) + if err != nil { + return false, err + } + defer elog.Close() + + err = svc.Run(svcName, &btcdService{}) + if err != nil { + elog.Error(1, fmt.Sprintf("Service start failed: %v", err)) + return true, err + } + + return true, nil +} + +// Set windows specific functions to real functions. +func init() { + runServiceCommand = performServiceCommand + winServiceMain = serviceMain +} diff --git a/vendor/github.com/btcsuite/btcd/signal.go b/vendor/github.com/btcsuite/btcd/signal.go new file mode 100644 index 0000000000000000000000000000000000000000..0c527b276117841149a6f751afab40cb3b9a709e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/signal.go @@ -0,0 +1,82 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "os" + "os/signal" +) + +// interruptChannel is used to receive SIGINT (Ctrl+C) signals. +var interruptChannel chan os.Signal + +// addHandlerChannel is used to add an interrupt handler to the list of handlers +// to be invoked on SIGINT (Ctrl+C) signals. +var addHandlerChannel = make(chan func()) + +// mainInterruptHandler listens for SIGINT (Ctrl+C) signals on the +// interruptChannel and invokes the registered interruptCallbacks accordingly. +// It also listens for callback registration. It must be run as a goroutine. +func mainInterruptHandler() { + // interruptCallbacks is a list of callbacks to invoke when a + // SIGINT (Ctrl+C) is received. + var interruptCallbacks []func() + + // isShutdown is a flag which is used to indicate whether or not + // the shutdown signal has already been received and hence any future + // attempts to add a new interrupt handler should invoke them + // immediately. + var isShutdown bool + + for { + select { + case <-interruptChannel: + // Ignore more than one shutdown signal. + if isShutdown { + btcdLog.Infof("Received SIGINT (Ctrl+C). " + + "Already shutting down...") + continue + } + + isShutdown = true + btcdLog.Infof("Received SIGINT (Ctrl+C). Shutting down...") + + // Run handlers in LIFO order. + for i := range interruptCallbacks { + idx := len(interruptCallbacks) - 1 - i + callback := interruptCallbacks[idx] + callback() + } + + // Signal the main goroutine to shutdown. + go func() { + shutdownChannel <- struct{}{} + }() + + case handler := <-addHandlerChannel: + // The shutdown signal has already been received, so + // just invoke and new handlers immediately. + if isShutdown { + handler() + } + + interruptCallbacks = append(interruptCallbacks, handler) + } + } +} + +// addInterruptHandler adds a handler to call when a SIGINT (Ctrl+C) is +// received. +func addInterruptHandler(handler func()) { + // Create the channel and start the main interrupt handler which invokes + // all other callbacks and exits if not already done. + if interruptChannel == nil { + interruptChannel = make(chan os.Signal, 1) + signal.Notify(interruptChannel, os.Interrupt) + go mainInterruptHandler() + } + + addHandlerChannel <- handler +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/README.md b/vendor/github.com/btcsuite/btcd/txscript/README.md new file mode 100644 index 0000000000000000000000000000000000000000..71838edfac84b6a1c753d4d7421cd9ddc356380d --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/README.md @@ -0,0 +1,68 @@ +txscript +======== + +[] +(https://travis-ci.org/btcsuite/btcd) [![ISC License] +(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[] +(http://godoc.org/github.com/btcsuite/btcd/txscript) + +Package txscript implements the bitcoin transaction script language. There is +a comprehensive test suite. + +This package has intentionally been designed so it can be used as a standalone +package for any projects needing to use or validate bitcoin transaction scripts. + +## Bitcoin Scripts + +Bitcoin provides a stack-based, FORTH-like language for the scripts in +the bitcoin transactions. This language is not turing complete +although it is still fairly powerful. A description of the language +can be found at https://en.bitcoin.it/wiki/Script + +## Installation and Updating + +```bash +$ go get -u github.com/btcsuite/btcd/txscript +``` + +## Examples + +* [Standard Pay-to-pubkey-hash Script] + (http://godoc.org/github.com/btcsuite/btcd/txscript#example-PayToAddrScript) + 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. + +* [Extracting Details from Standard Scripts] + (http://godoc.org/github.com/btcsuite/btcd/txscript#example-ExtractPkScriptAddrs) + Demonstrates extracting information from a standard public key script. + +* [Manually Signing a Transaction Output] + (http://godoc.org/github.com/btcsuite/btcd/txscript#example-SignTxOutput) + Demonstrates manually creating and signing a redeem transaction. + +## GPG Verification Key + +All official release tags are signed by Conformal so users can ensure the code +has not been tampered with and is coming from the btcsuite developers. To +verify the signature perform the following: + +- Download the public key from the Conformal website at + https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt + +- Import the public key into your GPG keyring: + ```bash + gpg --import GIT-GPG-KEY-conformal.txt + ``` + +- Verify the release tag with the following command where `TAG_NAME` is a + placeholder for the specific tag: + ```bash + git tag -v TAG_NAME + ``` + +## License + +Package txscript is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/vendor/github.com/btcsuite/btcd/txscript/consensus.go b/vendor/github.com/btcsuite/btcd/txscript/consensus.go new file mode 100644 index 0000000000000000000000000000000000000000..1aec50283e2f92a797777314b09f79cbb8d537c1 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/consensus.go @@ -0,0 +1,14 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +const ( + // LockTimeThreshold is the number below which a lock time is + // interpreted to be a block number. Since an average of one block + // is generated per 10 minutes, this allows blocks for about 9,512 + // years. However, if the field is interpreted as a timestamp, given + // the lock time is a uint32, the max is sometime around 2106. + LockTimeThreshold uint32 = 5e8 // Tue Nov 5 00:53:20 1985 UTC +) diff --git a/vendor/github.com/btcsuite/btcd/txscript/data/LICENSE b/vendor/github.com/btcsuite/btcd/txscript/data/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..30d808c5884bb9e81ffd24001704c4b0aabe5d53 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/data/LICENSE @@ -0,0 +1,8 @@ +The json files in this directory come from the bitcoind project +(https://github.com/bitcoin/bitcoin) and is released under the following +license: + + Copyright (c) 2012-2014 The Bitcoin Core developers + Distributed under the MIT/X11 software license, see the accompanying + file COPYING or http://www.opensource.org/licenses/mit-license.php. + diff --git a/vendor/github.com/btcsuite/btcd/txscript/data/script_invalid.json b/vendor/github.com/btcsuite/btcd/txscript/data/script_invalid.json new file mode 100644 index 0000000000000000000000000000000000000000..7ce7e0879cb6516d3e47cdb69d28b4c1dc611066 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/data/script_invalid.json @@ -0,0 +1,814 @@ +[ +["Format is: [scriptSig, scriptPubKey, flags, ... comments]"], +["It is evaluated as if there was a crediting coinbase transaction with two 0"], +["pushes as scriptSig, and one output of 0 satoshi and given scriptPubKey,"], +["followed by a spending transaction which spends this output as only input (and"], +["correct prevout hash), using the given scriptSig. All nLockTimes are 0, all"], +["nSequences are max."], + +["", "DEPTH", "P2SH,STRICTENC", "Test the test: we should have an empty stack after scriptSig evaluation"], +[" ", "DEPTH", "P2SH,STRICTENC", "and multiple spaces should not change that."], +[" ", "DEPTH", "P2SH,STRICTENC"], +[" ", "DEPTH", "P2SH,STRICTENC"], + +["", "", "P2SH,STRICTENC"], +["", "NOP", "P2SH,STRICTENC"], +["", "NOP DEPTH", "P2SH,STRICTENC"], +["NOP", "", "P2SH,STRICTENC"], +["NOP", "DEPTH", "P2SH,STRICTENC"], +["NOP","NOP", "P2SH,STRICTENC"], +["NOP","NOP DEPTH", "P2SH,STRICTENC"], + +["DEPTH", "", "P2SH,STRICTENC"], + +["0x4c01","0x01 NOP", "P2SH,STRICTENC", "PUSHDATA1 with not enough bytes"], +["0x4d0200ff","0x01 NOP", "P2SH,STRICTENC", "PUSHDATA2 with not enough bytes"], +["0x4e03000000ffff","0x01 NOP", "P2SH,STRICTENC", "PUSHDATA4 with not enough bytes"], + +["1", "IF 0x50 ENDIF 1", "P2SH,STRICTENC", "0x50 is reserved"], +["0x52", "0x5f ADD 0x60 EQUAL", "P2SH,STRICTENC", "0x51 through 0x60 push 1 through 16 onto stack"], +["0","NOP", "P2SH,STRICTENC"], +["1", "IF VER ELSE 1 ENDIF", "P2SH,STRICTENC", "VER non-functional"], +["0", "IF VERIF ELSE 1 ENDIF", "P2SH,STRICTENC", "VERIF illegal everywhere"], +["0", "IF ELSE 1 ELSE VERIF ENDIF", "P2SH,STRICTENC", "VERIF illegal everywhere"], +["0", "IF VERNOTIF ELSE 1 ENDIF", "P2SH,STRICTENC", "VERNOTIF illegal everywhere"], +["0", "IF ELSE 1 ELSE VERNOTIF ENDIF", "P2SH,STRICTENC", "VERNOTIF illegal everywhere"], + +["1 IF", "1 ENDIF", "P2SH,STRICTENC", "IF/ENDIF can't span scriptSig/scriptPubKey"], +["1 IF 0 ENDIF", "1 ENDIF", "P2SH,STRICTENC"], +["1 ELSE 0 ENDIF", "1", "P2SH,STRICTENC"], +["0 NOTIF", "123", "P2SH,STRICTENC"], + +["0", "DUP IF ENDIF", "P2SH,STRICTENC"], +["0", "IF 1 ENDIF", "P2SH,STRICTENC"], +["0", "DUP IF ELSE ENDIF", "P2SH,STRICTENC"], +["0", "IF 1 ELSE ENDIF", "P2SH,STRICTENC"], +["0", "NOTIF ELSE 1 ENDIF", "P2SH,STRICTENC"], + +["0 1", "IF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC"], +["0 0", "IF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC"], +["1 0", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC"], +["0 1", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC"], + +["0 0", "NOTIF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC"], +["0 1", "NOTIF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC"], +["1 1", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC"], +["0 0", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC"], + +["1", "IF RETURN ELSE ELSE 1 ENDIF", "P2SH,STRICTENC", "Multiple ELSEs"], +["1", "IF 1 ELSE ELSE RETURN ENDIF", "P2SH,STRICTENC"], + +["1", "ENDIF", "P2SH,STRICTENC", "Malformed IF/ELSE/ENDIF sequence"], +["1", "ELSE ENDIF", "P2SH,STRICTENC"], +["1", "ENDIF ELSE", "P2SH,STRICTENC"], +["1", "ENDIF ELSE IF", "P2SH,STRICTENC"], +["1", "IF ELSE ENDIF ELSE", "P2SH,STRICTENC"], +["1", "IF ELSE ENDIF ELSE ENDIF", "P2SH,STRICTENC"], +["1", "IF ENDIF ENDIF", "P2SH,STRICTENC"], +["1", "IF ELSE ELSE ENDIF ENDIF", "P2SH,STRICTENC"], + +["1", "RETURN", "P2SH,STRICTENC"], +["1", "DUP IF RETURN ENDIF", "P2SH,STRICTENC"], + +["1", "RETURN 'data'", "P2SH,STRICTENC", "canonical prunable txout format"], +["0 IF", "RETURN ENDIF 1", "P2SH,STRICTENC", "still prunable because IF/ENDIF can't span scriptSig/scriptPubKey"], + +["0", "VERIFY 1", "P2SH,STRICTENC"], +["1", "VERIFY", "P2SH,STRICTENC"], +["1", "VERIFY 0", "P2SH,STRICTENC"], + +["1 TOALTSTACK", "FROMALTSTACK 1", "P2SH,STRICTENC", "alt stack not shared between sig/pubkey"], + +["IFDUP", "DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["DROP", "DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["DUP", "DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["1", "DUP 1 ADD 2 EQUALVERIFY 0 EQUAL", "P2SH,STRICTENC"], +["NOP", "NIP", "P2SH,STRICTENC"], +["NOP", "1 NIP", "P2SH,STRICTENC"], +["NOP", "1 0 NIP", "P2SH,STRICTENC"], +["NOP", "OVER 1", "P2SH,STRICTENC"], +["1", "OVER", "P2SH,STRICTENC"], +["0 1", "OVER DEPTH 3 EQUALVERIFY", "P2SH,STRICTENC"], +["19 20 21", "PICK 19 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC"], +["NOP", "0 PICK", "P2SH,STRICTENC"], +["1", "-1 PICK", "P2SH,STRICTENC"], +["19 20 21", "0 PICK 20 EQUALVERIFY DEPTH 3 EQUAL", "P2SH,STRICTENC"], +["19 20 21", "1 PICK 21 EQUALVERIFY DEPTH 3 EQUAL", "P2SH,STRICTENC"], +["19 20 21", "2 PICK 22 EQUALVERIFY DEPTH 3 EQUAL", "P2SH,STRICTENC"], +["NOP", "0 ROLL", "P2SH,STRICTENC"], +["1", "-1 ROLL", "P2SH,STRICTENC"], +["19 20 21", "0 ROLL 20 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC"], +["19 20 21", "1 ROLL 21 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC"], +["19 20 21", "2 ROLL 22 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC"], +["NOP", "ROT 1", "P2SH,STRICTENC"], +["NOP", "1 ROT 1", "P2SH,STRICTENC"], +["NOP", "1 2 ROT 1", "P2SH,STRICTENC"], +["NOP", "0 1 2 ROT", "P2SH,STRICTENC"], +["NOP", "SWAP 1", "P2SH,STRICTENC"], +["1", "SWAP 1", "P2SH,STRICTENC"], +["0 1", "SWAP 1 EQUALVERIFY", "P2SH,STRICTENC"], +["NOP", "TUCK 1", "P2SH,STRICTENC"], +["1", "TUCK 1", "P2SH,STRICTENC"], +["1 0", "TUCK DEPTH 3 EQUALVERIFY SWAP 2DROP", "P2SH,STRICTENC"], +["NOP", "2DUP 1", "P2SH,STRICTENC"], +["1", "2DUP 1", "P2SH,STRICTENC"], +["NOP", "3DUP 1", "P2SH,STRICTENC"], +["1", "3DUP 1", "P2SH,STRICTENC"], +["1 2", "3DUP 1", "P2SH,STRICTENC"], +["NOP", "2OVER 1", "P2SH,STRICTENC"], +["1", "2 3 2OVER 1", "P2SH,STRICTENC"], +["NOP", "2SWAP 1", "P2SH,STRICTENC"], +["1", "2 3 2SWAP 1", "P2SH,STRICTENC"], + +["'a' 'b'", "CAT", "P2SH,STRICTENC", "CAT disabled"], +["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "P2SH,STRICTENC", "CAT disabled"], +["'abc' 1 1", "SUBSTR", "P2SH,STRICTENC", "SUBSTR disabled"], +["'abc' 1 1 0", "IF SUBSTR ELSE 1 ENDIF", "P2SH,STRICTENC", "SUBSTR disabled"], +["'abc' 2 0", "IF LEFT ELSE 1 ENDIF", "P2SH,STRICTENC", "LEFT disabled"], +["'abc' 2 0", "IF RIGHT ELSE 1 ENDIF", "P2SH,STRICTENC", "RIGHT disabled"], + +["NOP", "SIZE 1", "P2SH,STRICTENC"], + +["'abc'", "IF INVERT ELSE 1 ENDIF", "P2SH,STRICTENC", "INVERT disabled"], +["1 2 0 IF AND ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "AND disabled"], +["1 2 0 IF OR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "OR disabled"], +["1 2 0 IF XOR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "XOR disabled"], +["2 0 IF 2MUL ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "2MUL disabled"], +["2 0 IF 2DIV ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "2DIV disabled"], +["2 2 0 IF MUL ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "MUL disabled"], +["2 2 0 IF DIV ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DIV disabled"], +["2 2 0 IF MOD ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "MOD disabled"], +["2 2 0 IF LSHIFT ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "LSHIFT disabled"], +["2 2 0 IF RSHIFT ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "RSHIFT disabled"], + +["", "EQUAL NOT", "P2SH,STRICTENC", "EQUAL must error when there are no stack items"], +["0", "EQUAL NOT", "P2SH,STRICTENC", "EQUAL must error when there are not 2 stack items"], +["0 1","EQUAL", "P2SH,STRICTENC"], +["1 1 ADD", "0 EQUAL", "P2SH,STRICTENC"], +["11 1 ADD 12 SUB", "11 EQUAL", "P2SH,STRICTENC"], + +["2147483648 0 ADD", "NOP", "P2SH,STRICTENC", "arithmetic operands must be in range [-2^31...2^31] "], +["-2147483648 0 ADD", "NOP", "P2SH,STRICTENC", "arithmetic operands must be in range [-2^31...2^31] "], +["2147483647 DUP ADD", "4294967294 NUMEQUAL", "P2SH,STRICTENC", "NUMEQUAL must be in numeric range"], +["'abcdef' NOT", "0 EQUAL", "P2SH,STRICTENC", "NOT is an arithmetic operand"], + +["2 DUP MUL", "4 EQUAL", "P2SH,STRICTENC", "disabled"], +["2 DUP DIV", "1 EQUAL", "P2SH,STRICTENC", "disabled"], +["2 2MUL", "4 EQUAL", "P2SH,STRICTENC", "disabled"], +["2 2DIV", "1 EQUAL", "P2SH,STRICTENC", "disabled"], +["7 3 MOD", "1 EQUAL", "P2SH,STRICTENC", "disabled"], +["2 2 LSHIFT", "8 EQUAL", "P2SH,STRICTENC", "disabled"], +["2 1 RSHIFT", "1 EQUAL", "P2SH,STRICTENC", "disabled"], + +["1","NOP1 CHECKLOCKTIMEVERIFY NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 2 EQUAL", "P2SH,STRICTENC"], +["'NOP_1_to_10' NOP1 CHECKLOCKTIMEVERIFY NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_11' EQUAL", "P2SH,STRICTENC"], + +["Ensure 100% coverage of discouraged NOPS"], +["1", "NOP1", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], +["1", "CHECKLOCKTIMEVERIFY", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP3", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP4", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP5", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP6", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP7", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP8", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP9", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP10", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], + +["NOP10", "1", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "Discouraged NOP10 in scriptSig"], + +["1 0x01 0xb9", "HASH160 0x14 0x15727299b05b45fdaf9ac9ecf7565cfe27c3e567 EQUAL", + "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "Discouraged NOP10 in redeemScript"], + +["0x50","1", "P2SH,STRICTENC", "opcode 0x50 is reserved"], +["1", "IF 0xba ELSE 1 ENDIF", "P2SH,STRICTENC", "opcodes above NOP10 invalid if executed"], +["1", "IF 0xbb ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xbc ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xbd ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xbe ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xbf ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xc0 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xc1 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xc2 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xc3 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xc4 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xc5 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xc6 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xc7 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xc8 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xc9 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xca ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xcb ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xcc ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xcd ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xce ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xcf ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xd0 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xd1 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xd2 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xd3 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xd4 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xd5 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xd6 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xd7 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xd8 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xd9 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xda ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xdb ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xdc ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xdd ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xde ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xdf ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xe0 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xe1 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xe2 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xe3 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xe4 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xe5 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xe6 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xe7 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xe8 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xe9 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xea ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xeb ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xec ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xed ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xee ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xef ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xf0 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xf1 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xf2 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xf3 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xf4 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xf5 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xf6 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xf7 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xf8 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xf9 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xfa ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xfb ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xfc ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xfd ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xfe ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 0xff ELSE 1 ENDIF", "P2SH,STRICTENC"], + +["1 IF 1 ELSE", "0xff ENDIF", "P2SH,STRICTENC", "invalid because scriptSig and scriptPubKey are processed separately"], + +["NOP", "RIPEMD160", "P2SH,STRICTENC"], +["NOP", "SHA1", "P2SH,STRICTENC"], +["NOP", "SHA256", "P2SH,STRICTENC"], +["NOP", "HASH160", "P2SH,STRICTENC"], +["NOP", "HASH256", "P2SH,STRICTENC"], + +["NOP", +"'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'", +"P2SH,STRICTENC", +">520 byte push"], +["0", +"IF 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' ENDIF 1", +"P2SH,STRICTENC", +">520 byte push in non-executed IF branch"], +["1", +"0x61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"P2SH,STRICTENC", +">201 opcodes executed. 0x61 is NOP"], +["0", +"IF 0x6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 ENDIF 1", +"P2SH,STRICTENC", +">201 opcodes including non-executed IF branch. 0x61 is NOP"], +["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 2 3 4 5 6 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"P2SH,STRICTENC", +">1,000 stack size (0x6f is 3DUP)"], +["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 TOALTSTACK 2 TOALTSTACK 3 4 5 6 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"P2SH,STRICTENC", +">1,000 stack+altstack size"], +["NOP", +"0 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f 2DUP 0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"P2SH,STRICTENC", +"10,001-byte scriptPubKey"], + +["NOP1","NOP10", "P2SH,STRICTENC"], + +["1","VER", "P2SH,STRICTENC", "OP_VER is reserved"], +["1","VERIF", "P2SH,STRICTENC", "OP_VERIF is reserved"], +["1","VERNOTIF", "P2SH,STRICTENC", "OP_VERNOTIF is reserved"], +["1","RESERVED", "P2SH,STRICTENC", "OP_RESERVED is reserved"], +["1","RESERVED1", "P2SH,STRICTENC", "OP_RESERVED1 is reserved"], +["1","RESERVED2", "P2SH,STRICTENC", "OP_RESERVED2 is reserved"], +["1","0xba", "P2SH,STRICTENC", "0xba == OP_NOP10 + 1"], + +["2147483648", "1ADD 1", "P2SH,STRICTENC", "We cannot do math on 5-byte integers"], +["2147483648", "NEGATE 1", "P2SH,STRICTENC", "We cannot do math on 5-byte integers"], +["-2147483648", "1ADD 1", "P2SH,STRICTENC", "Because we use a sign bit, -2147483648 is also 5 bytes"], +["2147483647", "1ADD 1SUB 1", "P2SH,STRICTENC", "We cannot do math on 5-byte integers, even if the result is 4-bytes"], +["2147483648", "1SUB 1", "P2SH,STRICTENC", "We cannot do math on 5-byte integers, even if the result is 4-bytes"], + +["2147483648 1", "BOOLOR 1", "P2SH,STRICTENC", "We cannot do BOOLOR on 5-byte integers (but we can still do IF etc)"], +["2147483648 1", "BOOLAND 1", "P2SH,STRICTENC", "We cannot do BOOLAND on 5-byte integers"], + +["1", "1 ENDIF", "P2SH,STRICTENC", "ENDIF without IF"], +["1", "IF 1", "P2SH,STRICTENC", "IF without ENDIF"], +["1 IF 1", "ENDIF", "P2SH,STRICTENC", "IFs don't carry over"], + +["NOP", "IF 1 ENDIF", "P2SH,STRICTENC", "The following tests check the if(stack.size() < N) tests in each opcode"], +["NOP", "NOTIF 1 ENDIF", "P2SH,STRICTENC", "They are here to catch copy-and-paste errors"], +["NOP", "VERIFY 1", "P2SH,STRICTENC", "Most of them are duplicated elsewhere,"], + +["NOP", "TOALTSTACK 1", "P2SH,STRICTENC", "but, hey, more is always better, right?"], +["1", "FROMALTSTACK", "P2SH,STRICTENC"], +["1", "2DROP 1", "P2SH,STRICTENC"], +["1", "2DUP", "P2SH,STRICTENC"], +["1 1", "3DUP", "P2SH,STRICTENC"], +["1 1 1", "2OVER", "P2SH,STRICTENC"], +["1 1 1 1 1", "2ROT", "P2SH,STRICTENC"], +["1 1 1", "2SWAP", "P2SH,STRICTENC"], +["NOP", "IFDUP 1", "P2SH,STRICTENC"], +["NOP", "DROP 1", "P2SH,STRICTENC"], +["NOP", "DUP 1", "P2SH,STRICTENC"], +["1", "NIP", "P2SH,STRICTENC"], +["1", "OVER", "P2SH,STRICTENC"], +["1 1 1 3", "PICK", "P2SH,STRICTENC"], +["0", "PICK 1", "P2SH,STRICTENC"], +["1 1 1 3", "ROLL", "P2SH,STRICTENC"], +["0", "ROLL 1", "P2SH,STRICTENC"], +["1 1", "ROT", "P2SH,STRICTENC"], +["1", "SWAP", "P2SH,STRICTENC"], +["1", "TUCK", "P2SH,STRICTENC"], + +["NOP", "SIZE 1", "P2SH,STRICTENC"], + +["1", "EQUAL 1", "P2SH,STRICTENC"], +["1", "EQUALVERIFY 1", "P2SH,STRICTENC"], + +["NOP", "1ADD 1", "P2SH,STRICTENC"], +["NOP", "1SUB 1", "P2SH,STRICTENC"], +["NOP", "NEGATE 1", "P2SH,STRICTENC"], +["NOP", "ABS 1", "P2SH,STRICTENC"], +["NOP", "NOT 1", "P2SH,STRICTENC"], +["NOP", "0NOTEQUAL 1", "P2SH,STRICTENC"], + +["1", "ADD", "P2SH,STRICTENC"], +["1", "SUB", "P2SH,STRICTENC"], +["1", "BOOLAND", "P2SH,STRICTENC"], +["1", "BOOLOR", "P2SH,STRICTENC"], +["1", "NUMEQUAL", "P2SH,STRICTENC"], +["1", "NUMEQUALVERIFY 1", "P2SH,STRICTENC"], +["1", "NUMNOTEQUAL", "P2SH,STRICTENC"], +["1", "LESSTHAN", "P2SH,STRICTENC"], +["1", "GREATERTHAN", "P2SH,STRICTENC"], +["1", "LESSTHANOREQUAL", "P2SH,STRICTENC"], +["1", "GREATERTHANOREQUAL", "P2SH,STRICTENC"], +["1", "MIN", "P2SH,STRICTENC"], +["1", "MAX", "P2SH,STRICTENC"], +["1 1", "WITHIN", "P2SH,STRICTENC"], + +["NOP", "RIPEMD160 1", "P2SH,STRICTENC"], +["NOP", "SHA1 1", "P2SH,STRICTENC"], +["NOP", "SHA256 1", "P2SH,STRICTENC"], +["NOP", "HASH160 1", "P2SH,STRICTENC"], +["NOP", "HASH256 1", "P2SH,STRICTENC"], + +["Increase CHECKSIG and CHECKMULTISIG negative test coverage"], +["", "CHECKSIG NOT", "STRICTENC", "CHECKSIG must error when there are no stack items"], +["0", "CHECKSIG NOT", "STRICTENC", "CHECKSIG must error when there are not 2 stack items"], +["", "CHECKMULTISIG NOT", "STRICTENC", "CHECKMULTISIG must error when there are no stack items"], +["", "-1 CHECKMULTISIG NOT", "STRICTENC", "CHECKMULTISIG must error when the specified number of pubkeys is negative"], +["", "1 CHECKMULTISIG NOT", "STRICTENC", "CHECKMULTISIG must error when there are not enough pubkeys on the stack"], +["", "-1 0 CHECKMULTISIG NOT", "STRICTENC", "CHECKMULTISIG must error when the specified number of signatures is negative"], +["", "1 'pk1' 1 CHECKMULTISIG NOT", "STRICTENC", "CHECKMULTISIG must error when there are not enough signatures on the stack"], +["", "'dummy' 'sig1' 1 'pk1' 1 CHECKMULTISIG IF 1 ENDIF", "", "CHECKMULTISIG must push false to stack when signature is invalid when NOT in strict enc mode"], + +["", +"0 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG", +"P2SH,STRICTENC", +"202 CHECKMULTISIGS, fails due to 201 op limit"], + +["1", +"0 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY", +"P2SH,STRICTENC"], + +["", +"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG", +"P2SH,STRICTENC", +"Fails due to 201 sig op limit"], + +["1", +"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY", +"P2SH,STRICTENC"], + + +["0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21", "21 CHECKMULTISIG 1", "P2SH,STRICTENC", "nPubKeys > 20"], +["0 'sig' 1 0", "CHECKMULTISIG 1", "P2SH,STRICTENC", "nSigs > nPubKeys"], + + +["NOP 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "P2SH,STRICTENC", "Tests for Script.IsPushOnly()"], +["NOP1 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "P2SH,STRICTENC"], + +["0 0x01 0x50", "HASH160 0x14 0xece424a6bb6ddf4db592c0faed60685047a361b1 EQUAL", "P2SH,STRICTENC", "OP_RESERVED in P2SH should fail"], +["0 0x01 VER", "HASH160 0x14 0x0f4d7845db968f2a81b530b6f3c1d6246d4c7e01 EQUAL", "P2SH,STRICTENC", "OP_VER in P2SH should fail"], + +["0x00", "'00' EQUAL", "P2SH,STRICTENC", "Basic OP_0 execution"], + +["MINIMALDATA enforcement for PUSHDATAs"], + +["0x4c 0x00", "DROP 1", "MINIMALDATA", "Empty vector minimally represented by OP_0"], +["0x01 0x81", "DROP 1", "MINIMALDATA", "-1 minimally represented by OP_1NEGATE"], +["0x01 0x01", "DROP 1", "MINIMALDATA", "1 to 16 minimally represented by OP_1 to OP_16"], +["0x01 0x02", "DROP 1", "MINIMALDATA"], +["0x01 0x03", "DROP 1", "MINIMALDATA"], +["0x01 0x04", "DROP 1", "MINIMALDATA"], +["0x01 0x05", "DROP 1", "MINIMALDATA"], +["0x01 0x06", "DROP 1", "MINIMALDATA"], +["0x01 0x07", "DROP 1", "MINIMALDATA"], +["0x01 0x08", "DROP 1", "MINIMALDATA"], +["0x01 0x09", "DROP 1", "MINIMALDATA"], +["0x01 0x0a", "DROP 1", "MINIMALDATA"], +["0x01 0x0b", "DROP 1", "MINIMALDATA"], +["0x01 0x0c", "DROP 1", "MINIMALDATA"], +["0x01 0x0d", "DROP 1", "MINIMALDATA"], +["0x01 0x0e", "DROP 1", "MINIMALDATA"], +["0x01 0x0f", "DROP 1", "MINIMALDATA"], +["0x01 0x10", "DROP 1", "MINIMALDATA"], + +["0x4c 0x48 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "DROP 1", "MINIMALDATA", + "PUSHDATA1 of 72 bytes minimally represented by direct push"], + +["0x4d 0xFF00 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "DROP 1", "MINIMALDATA", + "PUSHDATA2 of 255 bytes minimally represented by PUSHDATA1"], + +["0x4e 0x00010000 0x11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "DROP 1", "MINIMALDATA", + "PUSHDATA4 of 256 bytes minimally represented by PUSHDATA2"], + + +["MINIMALDATA enforcement for numeric arguments"], + +["0x01 0x00", "NOT DROP 1", "MINIMALDATA", "numequals 0"], +["0x02 0x0000", "NOT DROP 1", "MINIMALDATA", "numequals 0"], +["0x01 0x80", "NOT DROP 1", "MINIMALDATA", "0x80 (negative zero) numequals 0"], +["0x02 0x0080", "NOT DROP 1", "MINIMALDATA", "numequals 0"], +["0x02 0x0500", "NOT DROP 1", "MINIMALDATA", "numequals 5"], +["0x03 0x050000", "NOT DROP 1", "MINIMALDATA", "numequals 5"], +["0x02 0x0580", "NOT DROP 1", "MINIMALDATA", "numequals -5"], +["0x03 0x050080", "NOT DROP 1", "MINIMALDATA", "numequals -5"], +["0x03 0xff7f80", "NOT DROP 1", "MINIMALDATA", "Minimal encoding is 0xffff"], +["0x03 0xff7f00", "NOT DROP 1", "MINIMALDATA", "Minimal encoding is 0xff7f"], +["0x04 0xffff7f80", "NOT DROP 1", "MINIMALDATA", "Minimal encoding is 0xffffff"], +["0x04 0xffff7f00", "NOT DROP 1", "MINIMALDATA", "Minimal encoding is 0xffff7f"], + +["Test every numeric-accepting opcode for correct handling of the numeric minimal encoding rule"], + +["1 0x02 0x0000", "PICK DROP", "MINIMALDATA"], +["1 0x02 0x0000", "ROLL DROP 1", "MINIMALDATA"], +["0x02 0x0000", "1ADD DROP 1", "MINIMALDATA"], +["0x02 0x0000", "1SUB DROP 1", "MINIMALDATA"], +["0x02 0x0000", "NEGATE DROP 1", "MINIMALDATA"], +["0x02 0x0000", "ABS DROP 1", "MINIMALDATA"], +["0x02 0x0000", "NOT DROP 1", "MINIMALDATA"], +["0x02 0x0000", "0NOTEQUAL DROP 1", "MINIMALDATA"], + +["0 0x02 0x0000", "ADD DROP 1", "MINIMALDATA"], +["0x02 0x0000 0", "ADD DROP 1", "MINIMALDATA"], +["0 0x02 0x0000", "SUB DROP 1", "MINIMALDATA"], +["0x02 0x0000 0", "SUB DROP 1", "MINIMALDATA"], +["0 0x02 0x0000", "BOOLAND DROP 1", "MINIMALDATA"], +["0x02 0x0000 0", "BOOLAND DROP 1", "MINIMALDATA"], +["0 0x02 0x0000", "BOOLOR DROP 1", "MINIMALDATA"], +["0x02 0x0000 0", "BOOLOR DROP 1", "MINIMALDATA"], +["0 0x02 0x0000", "NUMEQUAL DROP 1", "MINIMALDATA"], +["0x02 0x0000 1", "NUMEQUAL DROP 1", "MINIMALDATA"], +["0 0x02 0x0000", "NUMEQUALVERIFY 1", "MINIMALDATA"], +["0x02 0x0000 0", "NUMEQUALVERIFY 1", "MINIMALDATA"], +["0 0x02 0x0000", "NUMNOTEQUAL DROP 1", "MINIMALDATA"], +["0x02 0x0000 0", "NUMNOTEQUAL DROP 1", "MINIMALDATA"], +["0 0x02 0x0000", "LESSTHAN DROP 1", "MINIMALDATA"], +["0x02 0x0000 0", "LESSTHAN DROP 1", "MINIMALDATA"], +["0 0x02 0x0000", "GREATERTHAN DROP 1", "MINIMALDATA"], +["0x02 0x0000 0", "GREATERTHAN DROP 1", "MINIMALDATA"], +["0 0x02 0x0000", "LESSTHANOREQUAL DROP 1", "MINIMALDATA"], +["0x02 0x0000 0", "LESSTHANOREQUAL DROP 1", "MINIMALDATA"], +["0 0x02 0x0000", "GREATERTHANOREQUAL DROP 1", "MINIMALDATA"], +["0x02 0x0000 0", "GREATERTHANOREQUAL DROP 1", "MINIMALDATA"], +["0 0x02 0x0000", "MIN DROP 1", "MINIMALDATA"], +["0x02 0x0000 0", "MIN DROP 1", "MINIMALDATA"], +["0 0x02 0x0000", "MAX DROP 1", "MINIMALDATA"], +["0x02 0x0000 0", "MAX DROP 1", "MINIMALDATA"], + +["0x02 0x0000 0 0", "WITHIN DROP 1", "MINIMALDATA"], +["0 0x02 0x0000 0", "WITHIN DROP 1", "MINIMALDATA"], +["0 0 0x02 0x0000", "WITHIN DROP 1", "MINIMALDATA"], + +["0 0 0x02 0x0000", "CHECKMULTISIG DROP 1", "MINIMALDATA"], +["0 0x02 0x0000 0", "CHECKMULTISIG DROP 1", "MINIMALDATA"], +["0 0x02 0x0000 0 1", "CHECKMULTISIG DROP 1", "MINIMALDATA"], +["0 0 0x02 0x0000", "CHECKMULTISIGVERIFY 1", "MINIMALDATA"], +["0 0x02 0x0000 0", "CHECKMULTISIGVERIFY 1", "MINIMALDATA"], + + +["Order of CHECKMULTISIG evaluation tests, inverted by swapping the order of"], +["pubkeys/signatures so they fail due to the STRICTENC rules on validly encoded"], +["signatures and pubkeys."], +[ + "0 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501", + "2 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 0 2 CHECKMULTISIG NOT", + "STRICTENC", + "2-of-2 CHECKMULTISIG NOT with the first pubkey invalid, and both signatures validly encoded." +], +[ + "0 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501 1", + "2 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 2 CHECKMULTISIG NOT", + "STRICTENC", + "2-of-2 CHECKMULTISIG NOT with both pubkeys valid, but first signature invalid." +], + +["Increase DERSIG test coverage"], +["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "DERSIG", "Overly long signature is incorrectly encoded for DERSIG"], +["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "DERSIG", "Missing S is incorrectly encoded for DERSIG"], +["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "S with invalid S length is incorrectly encoded for DERSIG"], +["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Non-integer R is incorrectly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Non-integer S is incorrectly encoded for DERSIG"], +["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Zero-length R is incorrectly encoded for DERSIG"], +["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "DERSIG", "Zero-length S is incorrectly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "Negative S is incorrectly encoded for DERSIG"], + +["Automatically generated test cases"], +[ + "0x47 0x304402200a5c6163f07b8c3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001", + "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "", + "P2PK, bad sig" +], +[ + "0x47 0x3044022034bb0494b50b8ef130e2185bb220265b9284ef5b4b8a8da4d8415df489c83b5102206259a26d9cc0a125ac26af6153b17c02956855ebe1467412f066e402f5f05d1201 0x21 0x03363d90d446b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640", + "DUP HASH160 0x14 0xc0834c0c158f53be706d234c38fd52de7eece656 EQUALVERIFY CHECKSIG", + "", + "P2PKH, bad pubkey" +], +[ + "0x47 0x304402204710a85181663b32d25c70ec2bbd14adff5ddfff6cb50d09e155ef5f541fc86c0220056b0cc949be9386ecc5f6c2ac0493269031dbb185781db90171b54ac127790201", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", + "", + "P2PK anyonecanpay marked with normal hashtype" +], +[ + "0x47 0x3044022003fef42ed6c7be8917441218f525a60e2431be978e28b7aca4d7a532cc413ae8022067a1f82c74e8d69291b90d148778405c6257bbcfc2353cc38a3e1f22bf44254601 0x23 0x210279be667ef9dcbbac54a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", + "HASH160 0x14 0x23b0ad3477f2178bc0b3eed26e4e6316f4e83aa1 EQUAL", + "P2SH", + "P2SH(P2PK), bad redeemscript" +], +[ + "0x47 0x304402204e2eb034be7b089534ac9e798cf6a2c79f38bcb34d1b179efd6f2de0841735db022071461beb056b5a7be1819da6a3e3ce3662831ecc298419ca101eb6887b5dd6a401 0x19 0x76a9147cf9c846cd4882efec4bf07e44ebdad495c94f4b88ac", + "HASH160 0x14 0x2df519943d5acc0ef5222091f9dfe3543f489a82 EQUAL", + "P2SH", + "P2SH(P2PKH), bad sig" +], +[ + "0 0x47 0x3044022051254b9fb476a52d85530792b578f86fea70ec1ffb4393e661bcccb23d8d63d3022076505f94a403c86097841944e044c70c2045ce90e36de51f7e9d3828db98a07501 0x47 0x304402200a358f750934b3feb822f1966bfcd8bbec9eeaa3a8ca941e11ee5960e181fa01022050bf6b5a8e7750f70354ae041cb68a7bade67ec6c3ab19eb359638974410626e01 0", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", + "", + "3-of-3, 2 sigs" +], +[ + "0 0x47 0x304402205b7d2c2f177ae76cfbbf14d589c113b0b35db753d305d5562dd0b61cbf366cfb02202e56f93c4f08a27f986cd424ffc48a462c3202c4902104d4d0ff98ed28f4bf8001 0 0x4c69 0x52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f515082103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff464053ae", + "HASH160 0x14 0xc9e4a896d149702d0d1695434feddd52e24ad78d EQUAL", + "P2SH", + "P2SH(2-of-3), 1 sig" +], +[ + "0x47 0x304402200060558477337b9022e70534f1fea71a318caf836812465a2509931c5e7c4987022078ec32bd50ac9e03a349ba953dfd9fe1c8d2dd8bdb1d38ddca844d3d5c78c11801", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "P2PK with too much R padding" +], +[ + "0x48 0x304502202de8c03fc525285c9c535631019a5f2af7c6454fa9eb392a3756a4917c420edd02210046130bf2baf7cfc065067c8b9e33a066d9c15edcea9feb0ca2d233e3597925b401", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "P2PK with too much S padding" +], +[ + "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "P2PK with too little R padding" +], +[ + "0x47 0x30440220005ece1335e7f757a1a1f476a7fb5bd90964e8a022489f890614a04acfb734c002206c12b8294a6513c7710e8c82d3c23d75cdbfe83200eb7efb495701958501a5d601", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "DERSIG", + "P2PK NOT with bad sig with too much R padding" +], +[ + "0x47 0x30440220005ece1335e7f657a1a1f476a7fb5bd90964e8a022489f890614a04acfb734c002206c12b8294a6513c7710e8c82d3c23d75cdbfe83200eb7efb495701958501a5d601", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "", + "P2PK NOT with too much R padding but no DERSIG" +], +[ + "0x47 0x30440220005ece1335e7f657a1a1f476a7fb5bd90964e8a022489f890614a04acfb734c002206c12b8294a6513c7710e8c82d3c23d75cdbfe83200eb7efb495701958501a5d601", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "DERSIG", + "P2PK NOT with too much R padding" +], +[ + "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "BIP66 example 1, with DERSIG" +], +[ + "0x47 0x304402208e43c0b91f7c1e5bc58e41c8185f8a6086e111b0090187968a86f2822462d3c902200a58f4076b1133b18ff1dc83ee51676e44c60cc608d9534e0df5ace0424fc0be01", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "BIP66 example 2, without DERSIG" +], +[ + "0x47 0x304402208e43c0b91f7c1e5bc58e41c8185f8a6086e111b0090187968a86f2822462d3c902200a58f4076b1133b18ff1dc83ee51676e44c60cc608d9534e0df5ace0424fc0be01", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "BIP66 example 2, with DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "BIP66 example 3, without DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "BIP66 example 3, with DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "BIP66 example 5, without DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "BIP66 example 5, with DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "BIP66 example 6, with DERSIG" +], +[ + "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0x47 0x3044022027c2714269ca5aeecc4d70edc88ba5ee0e3da4986e9216028f489ab4f1b8efce022022bd545b4951215267e4c5ceabd4c5350331b2e4a0b6494c56f361fa5a57a1a201", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "DERSIG", + "BIP66 example 7, with DERSIG" +], +[ + "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0x47 0x3044022079ea80afd538d9ada421b5101febeb6bc874e01dde5bca108c1d0479aec339a4022004576db8f66130d1df686ccf00935703689d69cf539438da1edab208b0d63c4801", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "BIP66 example 8, without DERSIG" +], +[ + "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0x47 0x3044022079ea80afd538d9ada421b5101febeb6bc874e01dde5bca108c1d0479aec339a4022004576db8f66130d1df686ccf00935703689d69cf539438da1edab208b0d63c4801", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "BIP66 example 8, with DERSIG" +], +[ + "0 0 0x47 0x3044022081aa9d436f2154e8b6d600516db03d78de71df685b585a9807ead4210bd883490220534bb6bdf318a419ac0749660b60e78d17d515558ef369bf872eff405b676b2e01", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "", + "BIP66 example 9, without DERSIG" +], +[ + "0 0 0x47 0x3044022081aa9d436f2154e8b6d600516db03d78de71df685b585a9807ead4210bd883490220534bb6bdf318a419ac0749660b60e78d17d515558ef369bf872eff405b676b2e01", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "DERSIG", + "BIP66 example 9, with DERSIG" +], +[ + "0 0 0x47 0x30440220da6f441dc3b4b2c84cfa8db0cd5b34ed92c9e01686de5a800d40498b70c0dcac02207c2cf91b0c32b860c4cd4994be36cfb84caf8bb7c3a8e4d96a31b2022c5299c501", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "BIP66 example 10, with DERSIG" +], +[ + "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "", + "BIP66 example 11, without DERSIG" +], +[ + "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "DERSIG", + "BIP66 example 11, with DERSIG" +], +[ + "0x48 0x304402203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022054e1c258c2981cdfba5df1f46661fb6541c44f77ca0092f3600331abfffb12510101", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "DERSIG", + "P2PK with multi-byte hashtype, with DERSIG" +], +[ + "0x48 0x304502203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022100ab1e3da73d67e32045a20e0b999e049978ea8d6ee5480d485fcf2ce0d03b2ef001", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "LOW_S", + "P2PK with high S" +], +[ + "0x47 0x3044022057292e2d4dfe775becdd0a9e6547997c728cdf35390f6a017da56d654d374e4902206b643be2fc53763b4e284845bfea2c597d2dc7759941dce937636c9d341b71ed01", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "STRICTENC", + "P2PK with hybrid pubkey" +], +[ + "0x47 0x30440220035d554e3153c14950c9993f41c496607a8e24093db0595be7bf875cf64fcf1f02204731c8c4e5daf15e706cec19cdd8f2c5b1d05490e11dab8465ed426569b6e92101", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG NOT", + "", + "P2PK NOT with hybrid pubkey but no STRICTENC" +], +[ + "0x47 0x30440220035d554e3153c14950c9993f41c496607a8e24093db0595be7bf875cf64fcf1f02204731c8c4e5daf15e706cec19cdd8f2c5b1d05490e11dab8465ed426569b6e92101", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG NOT", + "STRICTENC", + "P2PK NOT with hybrid pubkey" +], +[ + "0x47 0x30440220035d554e3153c04950c9993f41c496607a8e24093db0595be7bf875cf64fcf1f02204731c8c4e5daf15e706cec19cdd8f2c5b1d05490e11dab8465ed426569b6e92101", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG NOT", + "STRICTENC", + "P2PK NOT with invalid hybrid pubkey" +], +[ + "0 0x47 0x3044022079c7824d6c868e0e1a273484e28c2654a27d043c8a27f49f52cb72efed0759090220452bbbf7089574fa082095a4fc1b3a16bafcf97a3a34d745fafc922cce66b27201", + "1 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 2 CHECKMULTISIG", + "STRICTENC", + "1-of-2 with the first 1 hybrid pubkey" +], +[ + "0x47 0x304402206177d513ec2cda444c021a1f4f656fc4c72ba108ae063e157eb86dc3575784940220666fc66702815d0e5413bb9b1df22aed44f5f1efb8b99d41dd5dc9a5be6d205205", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", + "STRICTENC", + "P2PK with undefined hashtype" +], +[ + "0x47 0x304402207409b5b320296e5e2136a7b281a7f803028ca4ca44e2b83eebd46932677725de02202d4eea1c8d3c98e6f42614f54764e6e5e6542e213eb4d079737e9a8b6e9812ec05", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG NOT", + "STRICTENC", + "P2PK NOT with invalid sig and undefined hashtype" +], +[ + "1 0x47 0x3044022051254b9fb476a52d85530792b578f86fea70ec1ffb4393e661bcccb23d8d63d3022076505f94a403c86097841944e044c70c2045ce90e36de51f7e9d3828db98a07501 0x47 0x304402200a358f750934b3feb822f1966bfcd8bbec9eeaa3a8ca941e11ee5960e181fa01022050bf6b5a8e7750f70354ae041cb68a7bade67ec6c3ab19eb359638974410626e01 0x47 0x304402200955d031fff71d8653221e85e36c3c85533d2312fc3045314b19650b7ae2f81002202a6bb8505e36201909d0921f01abff390ae6b7ff97bbf959f98aedeb0a56730901", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", + "NULLDUMMY", + "3-of-3 with nonzero dummy" +], +[ + "1 0x47 0x304402201bb2edab700a5d020236df174fefed78087697143731f659bea59642c759c16d022061f42cdbae5bcd3e8790f20bf76687443436e94a634321c16a72aa54cbc7c2ea01 0x47 0x304402204bb4a64f2a6e5c7fb2f07fef85ee56fde5e6da234c6a984262307a20e99842d702206f8303aaba5e625d223897e2ffd3f88ef1bcffef55f38dc3768e5f2e94c923f901 0x47 0x3044022040c2809b71fffb155ec8b82fe7a27f666bd97f941207be4e14ade85a1249dd4d02204d56c85ec525dd18e29a0533d5ddf61b6b1bb32980c2f63edf951aebf7a27bfe01", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG NOT", + "NULLDUMMY", + "3-of-3 NOT with invalid sig with nonzero dummy" +], +[ + "0 0x47 0x304402200abeb4bd07f84222f474aed558cfbdfc0b4e96cde3c2935ba7098b1ff0bd74c302204a04c1ca67b2a20abee210cf9a21023edccbbf8024b988812634233115c6b73901 DUP", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 2 CHECKMULTISIG", + "SIGPUSHONLY", + "2-of-2 with two identical keys and sigs pushed using OP_DUP" +], +[ + "0x47 0x304402203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022054e1c258c2981cdfba5df1f46661fb6541c44f77ca0092f3600331abfffb125101 0x23 0x2103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640ac", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "", + "P2SH(P2PK) with non-push scriptSig but no SIGPUSHONLY" +], +[ + "0x47 0x304402203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022054e1c258c2981cdfba5df1f46661fb6541c44f77ca0092f3600331abfffb125101 0x23 0x2103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640ac", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "SIGPUSHONLY", + "P2SH(P2PK) with non-push scriptSig" +], +[ + "0 0x47 0x304402205451ce65ad844dbb978b8bdedf5082e33b43cae8279c30f2c74d9e9ee49a94f802203fe95a7ccf74da7a232ee523ef4a53cb4d14bdd16289680cdb97a63819b8f42f01 0x46 0x304402205451ce65ad844dbb978b8bdedf5082e33b43cae8279c30f2c74d9e9ee49a94f802203fe95a7ccf74da7a232ee523ef4a53cb4d14bdd16289680cdb97a63819b8f42f", + "2 0x21 0x02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5 0x21 0x02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5 0x21 0x02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5 3 CHECKMULTISIG", + "P2SH,STRICTENC", + "2-of-3 with one valid and one invalid signature due to parse error, nSigs > validSigs" +], +[ + "11 0x47 0x304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001", + "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "CLEANSTACK,P2SH", + "P2PK with unnecessary input" +], +[ + "11 0x47 0x304402202f7505132be14872581f35d74b759212d9da40482653f1ffa3116c3294a4a51702206adbf347a2240ca41c66522b1a22a41693610b76a8e7770645dc721d1635854f01 0x43 0x410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + "HASH160 0x14 0x31edc23bdafda4639e669f89ad6b2318dd79d032 EQUAL", + "CLEANSTACK,P2SH", + "P2SH with unnecessary input" +], + +["The End"] +] diff --git a/vendor/github.com/btcsuite/btcd/txscript/data/script_valid.json b/vendor/github.com/btcsuite/btcd/txscript/data/script_valid.json new file mode 100644 index 0000000000000000000000000000000000000000..e5f0d17b0473e29871679e7acfc79194db41c75e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/data/script_valid.json @@ -0,0 +1,911 @@ +[ +["Format is: [scriptSig, scriptPubKey, flags, ... comments]"], +["It is evaluated as if there was a crediting coinbase transaction with two 0"], +["pushes as scriptSig, and one output of 0 satoshi and given scriptPubKey,"], +["followed by a spending transaction which spends this output as only input (and"], +["correct prevout hash), using the given scriptSig. All nLockTimes are 0, all"], +["nSequences are max."], + +["", "DEPTH 0 EQUAL", "P2SH,STRICTENC", "Test the test: we should have an empty stack after scriptSig evaluation"], +[" ", "DEPTH 0 EQUAL", "P2SH,STRICTENC", "and multiple spaces should not change that."], +[" ", "DEPTH 0 EQUAL", "P2SH,STRICTENC"], +[" ", "DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["1 2", "2 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC", "Similarly whitespace around and between symbols"], +["1 2", "2 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC"], +[" 1 2", "2 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC"], +["1 2 ", "2 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC"], +[" 1 2 ", "2 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC"], + +["1", "", "P2SH,STRICTENC"], +["0x02 0x01 0x00", "", "P2SH,STRICTENC", "all bytes are significant, not only the last one"], +["0x09 0x00000000 0x00000000 0x10", "", "P2SH,STRICTENC", "equals zero when cast to Int64"], + +["0x01 0x0b", "11 EQUAL", "P2SH,STRICTENC", "push 1 byte"], +["0x02 0x417a", "'Az' EQUAL", "P2SH,STRICTENC"], +["0x4b 0x417a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a", + "'Azzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' EQUAL", "P2SH,STRICTENC", "push 75 bytes"], + +["0x4c 0x01 0x07","7 EQUAL", "P2SH,STRICTENC", "0x4c is OP_PUSHDATA1"], +["0x4d 0x0100 0x08","8 EQUAL", "P2SH,STRICTENC", "0x4d is OP_PUSHDATA2"], +["0x4e 0x01000000 0x09","9 EQUAL", "P2SH,STRICTENC", "0x4e is OP_PUSHDATA4"], + +["0x4c 0x00","0 EQUAL", "P2SH,STRICTENC"], +["0x4d 0x0000","0 EQUAL", "P2SH,STRICTENC"], +["0x4e 0x00000000","0 EQUAL", "P2SH,STRICTENC"], +["0x4f 1000 ADD","999 EQUAL", "P2SH,STRICTENC"], +["0", "IF 0x50 ENDIF 1", "P2SH,STRICTENC", "0x50 is reserved (ok if not executed)"], +["0x51", "0x5f ADD 0x60 EQUAL", "P2SH,STRICTENC", "0x51 through 0x60 push 1 through 16 onto stack"], +["1","NOP", "P2SH,STRICTENC"], +["0", "IF VER ELSE 1 ENDIF", "P2SH,STRICTENC", "VER non-functional (ok if not executed)"], +["0", "IF RESERVED RESERVED1 RESERVED2 ELSE 1 ENDIF", "P2SH,STRICTENC", "RESERVED ok in un-executed IF"], + +["1", "DUP IF ENDIF", "P2SH,STRICTENC"], +["1", "IF 1 ENDIF", "P2SH,STRICTENC"], +["1", "DUP IF ELSE ENDIF", "P2SH,STRICTENC"], +["1", "IF 1 ELSE ENDIF", "P2SH,STRICTENC"], +["0", "IF ELSE 1 ENDIF", "P2SH,STRICTENC"], + +["1 1", "IF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC"], +["1 0", "IF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC"], +["1 1", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC"], +["0 0", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC"], + +["1 0", "NOTIF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC"], +["1 1", "NOTIF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC"], +["1 0", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC"], +["0 1", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC"], + +["0", "IF 0 ELSE 1 ELSE 0 ENDIF", "P2SH,STRICTENC", "Multiple ELSE's are valid and executed inverts on each ELSE encountered"], +["1", "IF 1 ELSE 0 ELSE ENDIF", "P2SH,STRICTENC"], +["1", "IF ELSE 0 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["1", "IF 1 ELSE 0 ELSE 1 ENDIF ADD 2 EQUAL", "P2SH,STRICTENC"], +["'' 1", "IF SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ENDIF 0x14 0x68ca4fec736264c13b859bac43d5173df6871682 EQUAL", "P2SH,STRICTENC"], + +["1", "NOTIF 0 ELSE 1 ELSE 0 ENDIF", "P2SH,STRICTENC", "Multiple ELSE's are valid and execution inverts on each ELSE encountered"], +["0", "NOTIF 1 ELSE 0 ELSE ENDIF", "P2SH,STRICTENC"], +["0", "NOTIF ELSE 0 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "NOTIF 1 ELSE 0 ELSE 1 ENDIF ADD 2 EQUAL", "P2SH,STRICTENC"], +["'' 0", "NOTIF SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ENDIF 0x14 0x68ca4fec736264c13b859bac43d5173df6871682 EQUAL", "P2SH,STRICTENC"], + +["0", "IF 1 IF RETURN ELSE RETURN ELSE RETURN ENDIF ELSE 1 IF 1 ELSE RETURN ELSE 1 ENDIF ELSE RETURN ENDIF ADD 2 EQUAL", "P2SH,STRICTENC", "Nested ELSE ELSE"], +["1", "NOTIF 0 NOTIF RETURN ELSE RETURN ELSE RETURN ENDIF ELSE 0 NOTIF 1 ELSE RETURN ELSE 1 ENDIF ELSE RETURN ENDIF ADD 2 EQUAL", "P2SH,STRICTENC"], + +["0", "IF RETURN ENDIF 1", "P2SH,STRICTENC", "RETURN only works if executed"], + +["1 1", "VERIFY", "P2SH,STRICTENC"], +["1 0x05 0x01 0x00 0x00 0x00 0x00", "VERIFY", "P2SH,STRICTENC", "values >4 bytes can be cast to boolean"], +["1 0x01 0x80", "IF 0 ENDIF", "P2SH,STRICTENC", "negative 0 is false"], + +["10 0 11 TOALTSTACK DROP FROMALTSTACK", "ADD 21 EQUAL", "P2SH,STRICTENC"], +["'gavin_was_here' TOALTSTACK 11 FROMALTSTACK", "'gavin_was_here' EQUALVERIFY 11 EQUAL", "P2SH,STRICTENC"], + +["0 IFDUP", "DEPTH 1 EQUALVERIFY 0 EQUAL", "P2SH,STRICTENC"], +["1 IFDUP", "DEPTH 2 EQUALVERIFY 1 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC"], +["0x05 0x0100000000 IFDUP", "DEPTH 2 EQUALVERIFY 0x05 0x0100000000 EQUAL", "P2SH,STRICTENC", "IFDUP dups non ints"], +["0 DROP", "DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["0", "DUP 1 ADD 1 EQUALVERIFY 0 EQUAL", "P2SH,STRICTENC"], +["0 1", "NIP", "P2SH,STRICTENC"], +["1 0", "OVER DEPTH 3 EQUALVERIFY", "P2SH,STRICTENC"], +["22 21 20", "0 PICK 20 EQUALVERIFY DEPTH 3 EQUAL", "P2SH,STRICTENC"], +["22 21 20", "1 PICK 21 EQUALVERIFY DEPTH 3 EQUAL", "P2SH,STRICTENC"], +["22 21 20", "2 PICK 22 EQUALVERIFY DEPTH 3 EQUAL", "P2SH,STRICTENC"], +["22 21 20", "0 ROLL 20 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC"], +["22 21 20", "1 ROLL 21 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC"], +["22 21 20", "2 ROLL 22 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC"], +["22 21 20", "ROT 22 EQUAL", "P2SH,STRICTENC"], +["22 21 20", "ROT DROP 20 EQUAL", "P2SH,STRICTENC"], +["22 21 20", "ROT DROP DROP 21 EQUAL", "P2SH,STRICTENC"], +["22 21 20", "ROT ROT 21 EQUAL", "P2SH,STRICTENC"], +["22 21 20", "ROT ROT ROT 20 EQUAL", "P2SH,STRICTENC"], +["25 24 23 22 21 20", "2ROT 24 EQUAL", "P2SH,STRICTENC"], +["25 24 23 22 21 20", "2ROT DROP 25 EQUAL", "P2SH,STRICTENC"], +["25 24 23 22 21 20", "2ROT 2DROP 20 EQUAL", "P2SH,STRICTENC"], +["25 24 23 22 21 20", "2ROT 2DROP DROP 21 EQUAL", "P2SH,STRICTENC"], +["25 24 23 22 21 20", "2ROT 2DROP 2DROP 22 EQUAL", "P2SH,STRICTENC"], +["25 24 23 22 21 20", "2ROT 2DROP 2DROP DROP 23 EQUAL", "P2SH,STRICTENC"], +["25 24 23 22 21 20", "2ROT 2ROT 22 EQUAL", "P2SH,STRICTENC"], +["25 24 23 22 21 20", "2ROT 2ROT 2ROT 20 EQUAL", "P2SH,STRICTENC"], +["1 0", "SWAP 1 EQUALVERIFY 0 EQUAL", "P2SH,STRICTENC"], +["0 1", "TUCK DEPTH 3 EQUALVERIFY SWAP 2DROP", "P2SH,STRICTENC"], +["13 14", "2DUP ROT EQUALVERIFY EQUAL", "P2SH,STRICTENC"], +["-1 0 1 2", "3DUP DEPTH 7 EQUALVERIFY ADD ADD 3 EQUALVERIFY 2DROP 0 EQUALVERIFY", "P2SH,STRICTENC"], +["1 2 3 5", "2OVER ADD ADD 8 EQUALVERIFY ADD ADD 6 EQUAL", "P2SH,STRICTENC"], +["1 3 5 7", "2SWAP ADD 4 EQUALVERIFY ADD 12 EQUAL", "P2SH,STRICTENC"], +["0", "SIZE 0 EQUAL", "P2SH,STRICTENC"], +["1", "SIZE 1 EQUAL", "P2SH,STRICTENC"], +["127", "SIZE 1 EQUAL", "P2SH,STRICTENC"], +["128", "SIZE 2 EQUAL", "P2SH,STRICTENC"], +["32767", "SIZE 2 EQUAL", "P2SH,STRICTENC"], +["32768", "SIZE 3 EQUAL", "P2SH,STRICTENC"], +["8388607", "SIZE 3 EQUAL", "P2SH,STRICTENC"], +["8388608", "SIZE 4 EQUAL", "P2SH,STRICTENC"], +["2147483647", "SIZE 4 EQUAL", "P2SH,STRICTENC"], +["2147483648", "SIZE 5 EQUAL", "P2SH,STRICTENC"], +["549755813887", "SIZE 5 EQUAL", "P2SH,STRICTENC"], +["549755813888", "SIZE 6 EQUAL", "P2SH,STRICTENC"], +["9223372036854775807", "SIZE 8 EQUAL", "P2SH,STRICTENC"], +["-1", "SIZE 1 EQUAL", "P2SH,STRICTENC"], +["-127", "SIZE 1 EQUAL", "P2SH,STRICTENC"], +["-128", "SIZE 2 EQUAL", "P2SH,STRICTENC"], +["-32767", "SIZE 2 EQUAL", "P2SH,STRICTENC"], +["-32768", "SIZE 3 EQUAL", "P2SH,STRICTENC"], +["-8388607", "SIZE 3 EQUAL", "P2SH,STRICTENC"], +["-8388608", "SIZE 4 EQUAL", "P2SH,STRICTENC"], +["-2147483647", "SIZE 4 EQUAL", "P2SH,STRICTENC"], +["-2147483648", "SIZE 5 EQUAL", "P2SH,STRICTENC"], +["-549755813887", "SIZE 5 EQUAL", "P2SH,STRICTENC"], +["-549755813888", "SIZE 6 EQUAL", "P2SH,STRICTENC"], +["-9223372036854775807", "SIZE 8 EQUAL", "P2SH,STRICTENC"], +["'abcdefghijklmnopqrstuvwxyz'", "SIZE 26 EQUAL", "P2SH,STRICTENC"], + +["42", "SIZE 1 EQUALVERIFY 42 EQUAL", "P2SH,STRICTENC", "SIZE does not consume argument"], + +["2 -2 ADD", "0 EQUAL", "P2SH,STRICTENC"], +["2147483647 -2147483647 ADD", "0 EQUAL", "P2SH,STRICTENC"], +["-1 -1 ADD", "-2 EQUAL", "P2SH,STRICTENC"], + +["0 0","EQUAL", "P2SH,STRICTENC"], +["1 1 ADD", "2 EQUAL", "P2SH,STRICTENC"], +["1 1ADD", "2 EQUAL", "P2SH,STRICTENC"], +["111 1SUB", "110 EQUAL", "P2SH,STRICTENC"], +["111 1 ADD 12 SUB", "100 EQUAL", "P2SH,STRICTENC"], +["0 ABS", "0 EQUAL", "P2SH,STRICTENC"], +["16 ABS", "16 EQUAL", "P2SH,STRICTENC"], +["-16 ABS", "-16 NEGATE EQUAL", "P2SH,STRICTENC"], +["0 NOT", "NOP", "P2SH,STRICTENC"], +["1 NOT", "0 EQUAL", "P2SH,STRICTENC"], +["11 NOT", "0 EQUAL", "P2SH,STRICTENC"], +["0 0NOTEQUAL", "0 EQUAL", "P2SH,STRICTENC"], +["1 0NOTEQUAL", "1 EQUAL", "P2SH,STRICTENC"], +["111 0NOTEQUAL", "1 EQUAL", "P2SH,STRICTENC"], +["-111 0NOTEQUAL", "1 EQUAL", "P2SH,STRICTENC"], +["1 1 BOOLAND", "NOP", "P2SH,STRICTENC"], +["1 0 BOOLAND", "NOT", "P2SH,STRICTENC"], +["0 1 BOOLAND", "NOT", "P2SH,STRICTENC"], +["0 0 BOOLAND", "NOT", "P2SH,STRICTENC"], +["16 17 BOOLAND", "NOP", "P2SH,STRICTENC"], +["1 1 BOOLOR", "NOP", "P2SH,STRICTENC"], +["1 0 BOOLOR", "NOP", "P2SH,STRICTENC"], +["0 1 BOOLOR", "NOP", "P2SH,STRICTENC"], +["0 0 BOOLOR", "NOT", "P2SH,STRICTENC"], +["16 17 BOOLOR", "NOP", "P2SH,STRICTENC"], +["11 10 1 ADD", "NUMEQUAL", "P2SH,STRICTENC"], +["11 10 1 ADD", "NUMEQUALVERIFY 1", "P2SH,STRICTENC"], +["11 10 1 ADD", "NUMNOTEQUAL NOT", "P2SH,STRICTENC"], +["111 10 1 ADD", "NUMNOTEQUAL", "P2SH,STRICTENC"], +["11 10", "LESSTHAN NOT", "P2SH,STRICTENC"], +["4 4", "LESSTHAN NOT", "P2SH,STRICTENC"], +["10 11", "LESSTHAN", "P2SH,STRICTENC"], +["-11 11", "LESSTHAN", "P2SH,STRICTENC"], +["-11 -10", "LESSTHAN", "P2SH,STRICTENC"], +["11 10", "GREATERTHAN", "P2SH,STRICTENC"], +["4 4", "GREATERTHAN NOT", "P2SH,STRICTENC"], +["10 11", "GREATERTHAN NOT", "P2SH,STRICTENC"], +["-11 11", "GREATERTHAN NOT", "P2SH,STRICTENC"], +["-11 -10", "GREATERTHAN NOT", "P2SH,STRICTENC"], +["11 10", "LESSTHANOREQUAL NOT", "P2SH,STRICTENC"], +["4 4", "LESSTHANOREQUAL", "P2SH,STRICTENC"], +["10 11", "LESSTHANOREQUAL", "P2SH,STRICTENC"], +["-11 11", "LESSTHANOREQUAL", "P2SH,STRICTENC"], +["-11 -10", "LESSTHANOREQUAL", "P2SH,STRICTENC"], +["11 10", "GREATERTHANOREQUAL", "P2SH,STRICTENC"], +["4 4", "GREATERTHANOREQUAL", "P2SH,STRICTENC"], +["10 11", "GREATERTHANOREQUAL NOT", "P2SH,STRICTENC"], +["-11 11", "GREATERTHANOREQUAL NOT", "P2SH,STRICTENC"], +["-11 -10", "GREATERTHANOREQUAL NOT", "P2SH,STRICTENC"], +["1 0 MIN", "0 NUMEQUAL", "P2SH,STRICTENC"], +["0 1 MIN", "0 NUMEQUAL", "P2SH,STRICTENC"], +["-1 0 MIN", "-1 NUMEQUAL", "P2SH,STRICTENC"], +["0 -2147483647 MIN", "-2147483647 NUMEQUAL", "P2SH,STRICTENC"], +["2147483647 0 MAX", "2147483647 NUMEQUAL", "P2SH,STRICTENC"], +["0 100 MAX", "100 NUMEQUAL", "P2SH,STRICTENC"], +["-100 0 MAX", "0 NUMEQUAL", "P2SH,STRICTENC"], +["0 -2147483647 MAX", "0 NUMEQUAL", "P2SH,STRICTENC"], +["0 0 1", "WITHIN", "P2SH,STRICTENC"], +["1 0 1", "WITHIN NOT", "P2SH,STRICTENC"], +["0 -2147483647 2147483647", "WITHIN", "P2SH,STRICTENC"], +["-1 -100 100", "WITHIN", "P2SH,STRICTENC"], +["11 -100 100", "WITHIN", "P2SH,STRICTENC"], +["-2147483647 -100 100", "WITHIN NOT", "P2SH,STRICTENC"], +["2147483647 -100 100", "WITHIN NOT", "P2SH,STRICTENC"], + +["2147483647 2147483647 SUB", "0 EQUAL", "P2SH,STRICTENC"], +["2147483647 DUP ADD", "4294967294 EQUAL", "P2SH,STRICTENC", ">32 bit EQUAL is valid"], +["2147483647 NEGATE DUP ADD", "-4294967294 EQUAL", "P2SH,STRICTENC"], + +["''", "RIPEMD160 0x14 0x9c1185a5c5e9fc54612808977ee8f548b2258d31 EQUAL", "P2SH,STRICTENC"], +["'a'", "RIPEMD160 0x14 0x0bdc9d2d256b3ee9daae347be6f4dc835a467ffe EQUAL", "P2SH,STRICTENC"], +["'abcdefghijklmnopqrstuvwxyz'", "RIPEMD160 0x14 0xf71c27109c692c1b56bbdceb5b9d2865b3708dbc EQUAL", "P2SH,STRICTENC"], +["''", "SHA1 0x14 0xda39a3ee5e6b4b0d3255bfef95601890afd80709 EQUAL", "P2SH,STRICTENC"], +["'a'", "SHA1 0x14 0x86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 EQUAL", "P2SH,STRICTENC"], +["'abcdefghijklmnopqrstuvwxyz'", "SHA1 0x14 0x32d10c7b8cf96570ca04ce37f2a19d84240d3a89 EQUAL", "P2SH,STRICTENC"], +["''", "SHA256 0x20 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 EQUAL", "P2SH,STRICTENC"], +["'a'", "SHA256 0x20 0xca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb EQUAL", "P2SH,STRICTENC"], +["'abcdefghijklmnopqrstuvwxyz'", "SHA256 0x20 0x71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 EQUAL", "P2SH,STRICTENC"], +["''", "DUP HASH160 SWAP SHA256 RIPEMD160 EQUAL", "P2SH,STRICTENC"], +["''", "DUP HASH256 SWAP SHA256 SHA256 EQUAL", "P2SH,STRICTENC"], +["''", "NOP HASH160 0x14 0xb472a266d0bd89c13706a4132ccfb16f7c3b9fcb EQUAL", "P2SH,STRICTENC"], +["'a'", "HASH160 NOP 0x14 0x994355199e516ff76c4fa4aab39337b9d84cf12b EQUAL", "P2SH,STRICTENC"], +["'abcdefghijklmnopqrstuvwxyz'", "HASH160 0x4c 0x14 0xc286a1af0947f58d1ad787385b1c2c4a976f9e71 EQUAL", "P2SH,STRICTENC"], +["''", "HASH256 0x20 0x5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456 EQUAL", "P2SH,STRICTENC"], +["'a'", "HASH256 0x20 0xbf5d3affb73efd2ec6c36ad3112dd933efed63c4e1cbffcfa88e2759c144f2d8 EQUAL", "P2SH,STRICTENC"], +["'abcdefghijklmnopqrstuvwxyz'", "HASH256 0x4c 0x20 0xca139bc10c2f660da42666f72e89a225936fc60f193c161124a672050c434671 EQUAL", "P2SH,STRICTENC"], + + +["1","NOP1 CHECKLOCKTIMEVERIFY NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 1 EQUAL", "P2SH,STRICTENC"], +["'NOP_1_to_10' NOP1 CHECKLOCKTIMEVERIFY NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_10' EQUAL", "P2SH,STRICTENC"], + +["1", "NOP", "P2SH,STRICTENC,DISCOURAGE_UPGRADABLE_NOPS", "Discourage NOPx flag allows OP_NOP"], + +["0", "IF NOP10 ENDIF 1", "P2SH,STRICTENC,DISCOURAGE_UPGRADABLE_NOPS", + "Discouraged NOPs are allowed if not executed"], + +["0", "IF 0xba ELSE 1 ENDIF", "P2SH,STRICTENC", "opcodes above NOP10 invalid if executed"], +["0", "IF 0xbb ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xbc ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xbd ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xbe ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xbf ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xc0 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xc1 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xc2 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xc3 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xc4 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xc5 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xc6 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xc7 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xc8 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xc9 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xca ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xcb ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xcc ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xcd ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xce ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xcf ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xd0 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xd1 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xd2 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xd3 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xd4 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xd5 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xd6 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xd7 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xd8 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xd9 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xda ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xdb ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xdc ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xdd ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xde ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xdf ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xe0 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xe1 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xe2 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xe3 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xe4 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xe5 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xe6 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xe7 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xe8 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xe9 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xea ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xeb ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xec ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xed ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xee ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xef ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xf0 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xf1 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xf2 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xf3 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xf4 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xf5 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xf6 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xf7 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xf8 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xf9 ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xfa ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xfb ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xfc ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xfd ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xfe ELSE 1 ENDIF", "P2SH,STRICTENC"], +["0", "IF 0xff ELSE 1 ENDIF", "P2SH,STRICTENC"], + +["NOP", +"'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'", +"P2SH,STRICTENC", +"520 byte push"], +["1", +"0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"P2SH,STRICTENC", +"201 opcodes executed. 0x61 is NOP"], +["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"P2SH,STRICTENC", +"1,000 stack size (0x6f is 3DUP)"], +["1 TOALTSTACK 2 TOALTSTACK 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 2 3 4 5 6 7 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"P2SH,STRICTENC", +"1,000 stack size (altstack cleared between scriptSig/scriptPubKey)"], +["'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f 2DUP 0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"P2SH,STRICTENC", +"Max-size (10,000-byte), max-push(520 bytes), max-opcodes(201), max stack size(1,000 items). 0x6f is 3DUP, 0x61 is NOP"], + +["0", +"IF 0x5050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050 ENDIF 1", +"P2SH,STRICTENC", +">201 opcodes, but RESERVED (0x50) doesn't count towards opcode limit."], + +["NOP","1", "P2SH,STRICTENC"], + +["1", "0x01 0x01 EQUAL", "P2SH,STRICTENC", "The following is useful for checking implementations of BN_bn2mpi"], +["127", "0x01 0x7F EQUAL", "P2SH,STRICTENC"], +["128", "0x02 0x8000 EQUAL", "P2SH,STRICTENC", "Leave room for the sign bit"], +["32767", "0x02 0xFF7F EQUAL", "P2SH,STRICTENC"], +["32768", "0x03 0x008000 EQUAL", "P2SH,STRICTENC"], +["8388607", "0x03 0xFFFF7F EQUAL", "P2SH,STRICTENC"], +["8388608", "0x04 0x00008000 EQUAL", "P2SH,STRICTENC"], +["2147483647", "0x04 0xFFFFFF7F EQUAL", "P2SH,STRICTENC"], +["2147483648", "0x05 0x0000008000 EQUAL", "P2SH,STRICTENC"], +["549755813887", "0x05 0xFFFFFFFF7F EQUAL", "P2SH,STRICTENC"], +["549755813888", "0x06 0xFFFFFFFF7F EQUAL", "P2SH,STRICTENC"], +["9223372036854775807", "0x08 0xFFFFFFFFFFFFFF7F EQUAL", "P2SH,STRICTENC"], +["-1", "0x01 0x81 EQUAL", "P2SH,STRICTENC", "Numbers are little-endian with the MSB being a sign bit"], +["-127", "0x01 0xFF EQUAL", "P2SH,STRICTENC"], +["-128", "0x02 0x8080 EQUAL", "P2SH,STRICTENC"], +["-32767", "0x02 0xFFFF EQUAL", "P2SH,STRICTENC"], +["-32768", "0x03 0x008080 EQUAL", "P2SH,STRICTENC"], +["-8388607", "0x03 0xFFFFFF EQUAL", "P2SH,STRICTENC"], +["-8388608", "0x04 0x00008080 EQUAL", "P2SH,STRICTENC"], +["-2147483647", "0x04 0xFFFFFFFF EQUAL", "P2SH,STRICTENC"], +["-2147483648", "0x05 0x0000008080 EQUAL", "P2SH,STRICTENC"], +["-4294967295", "0x05 0xFFFFFFFF80 EQUAL", "P2SH,STRICTENC"], +["-549755813887", "0x05 0xFFFFFFFFFF EQUAL", "P2SH,STRICTENC"], +["-549755813888", "0x06 0x000000008080 EQUAL", "P2SH,STRICTENC"], +["-9223372036854775807", "0x08 0xFFFFFFFFFFFFFFFF EQUAL", "P2SH,STRICTENC"], + +["2147483647", "1ADD 2147483648 EQUAL", "P2SH,STRICTENC", "We can do math on 4-byte integers, and compare 5-byte ones"], +["2147483647", "1ADD 1", "P2SH,STRICTENC"], +["-2147483647", "1ADD 1", "P2SH,STRICTENC"], + +["1", "0x02 0x0100 EQUAL NOT", "P2SH,STRICTENC", "Not the same byte array..."], +["1", "0x02 0x0100 NUMEQUAL", "P2SH,STRICTENC", "... but they are numerically equal"], +["11", "0x4c 0x03 0x0b0000 NUMEQUAL", "P2SH,STRICTENC"], +["0", "0x01 0x80 EQUAL NOT", "P2SH,STRICTENC"], +["0", "0x01 0x80 NUMEQUAL", "P2SH,STRICTENC", "Zero numerically equals negative zero"], +["0", "0x02 0x0080 NUMEQUAL", "P2SH,STRICTENC"], +["0x03 0x000080", "0x04 0x00000080 NUMEQUAL", "P2SH,STRICTENC"], +["0x03 0x100080", "0x04 0x10000080 NUMEQUAL", "P2SH,STRICTENC"], +["0x03 0x100000", "0x04 0x10000000 NUMEQUAL", "P2SH,STRICTENC"], + +["NOP", "NOP 1", "P2SH,STRICTENC", "The following tests check the if(stack.size() < N) tests in each opcode"], +["1", "IF 1 ENDIF", "P2SH,STRICTENC", "They are here to catch copy-and-paste errors"], +["0", "NOTIF 1 ENDIF", "P2SH,STRICTENC", "Most of them are duplicated elsewhere,"], +["1", "VERIFY 1", "P2SH,STRICTENC", "but, hey, more is always better, right?"], + +["0", "TOALTSTACK 1", "P2SH,STRICTENC"], +["1", "TOALTSTACK FROMALTSTACK", "P2SH,STRICTENC"], +["0 0", "2DROP 1", "P2SH,STRICTENC"], +["0 1", "2DUP", "P2SH,STRICTENC"], +["0 0 1", "3DUP", "P2SH,STRICTENC"], +["0 1 0 0", "2OVER", "P2SH,STRICTENC"], +["0 1 0 0 0 0", "2ROT", "P2SH,STRICTENC"], +["0 1 0 0", "2SWAP", "P2SH,STRICTENC"], +["1", "IFDUP", "P2SH,STRICTENC"], +["NOP", "DEPTH 1", "P2SH,STRICTENC"], +["0", "DROP 1", "P2SH,STRICTENC"], +["1", "DUP", "P2SH,STRICTENC"], +["0 1", "NIP", "P2SH,STRICTENC"], +["1 0", "OVER", "P2SH,STRICTENC"], +["1 0 0 0 3", "PICK", "P2SH,STRICTENC"], +["1 0", "PICK", "P2SH,STRICTENC"], +["1 0 0 0 3", "ROLL", "P2SH,STRICTENC"], +["1 0", "ROLL", "P2SH,STRICTENC"], +["1 0 0", "ROT", "P2SH,STRICTENC"], +["1 0", "SWAP", "P2SH,STRICTENC"], +["0 1", "TUCK", "P2SH,STRICTENC"], + +["1", "SIZE", "P2SH,STRICTENC"], + +["0 0", "EQUAL", "P2SH,STRICTENC"], +["0 0", "EQUALVERIFY 1", "P2SH,STRICTENC"], +["0 0 1", "EQUAL EQUAL", "P2SH,STRICTENC", "OP_0 and bools must have identical byte representations"], + +["0", "1ADD", "P2SH,STRICTENC"], +["2", "1SUB", "P2SH,STRICTENC"], +["-1", "NEGATE", "P2SH,STRICTENC"], +["-1", "ABS", "P2SH,STRICTENC"], +["0", "NOT", "P2SH,STRICTENC"], +["-1", "0NOTEQUAL", "P2SH,STRICTENC"], + +["1 0", "ADD", "P2SH,STRICTENC"], +["1 0", "SUB", "P2SH,STRICTENC"], +["-1 -1", "BOOLAND", "P2SH,STRICTENC"], +["-1 0", "BOOLOR", "P2SH,STRICTENC"], +["0 0", "NUMEQUAL", "P2SH,STRICTENC"], +["0 0", "NUMEQUALVERIFY 1", "P2SH,STRICTENC"], +["-1 0", "NUMNOTEQUAL", "P2SH,STRICTENC"], +["-1 0", "LESSTHAN", "P2SH,STRICTENC"], +["1 0", "GREATERTHAN", "P2SH,STRICTENC"], +["0 0", "LESSTHANOREQUAL", "P2SH,STRICTENC"], +["0 0", "GREATERTHANOREQUAL", "P2SH,STRICTENC"], +["-1 0", "MIN", "P2SH,STRICTENC"], +["1 0", "MAX", "P2SH,STRICTENC"], +["-1 -1 0", "WITHIN", "P2SH,STRICTENC"], + +["0", "RIPEMD160", "P2SH,STRICTENC"], +["0", "SHA1", "P2SH,STRICTENC"], +["0", "SHA256", "P2SH,STRICTENC"], +["0", "HASH160", "P2SH,STRICTENC"], +["0", "HASH256", "P2SH,STRICTENC"], +["NOP", "CODESEPARATOR 1", "P2SH,STRICTENC"], + +["NOP", "NOP1 1", "P2SH,STRICTENC"], +["NOP", "CHECKLOCKTIMEVERIFY 1", "P2SH,STRICTENC"], +["NOP", "NOP3 1", "P2SH,STRICTENC"], +["NOP", "NOP4 1", "P2SH,STRICTENC"], +["NOP", "NOP5 1", "P2SH,STRICTENC"], +["NOP", "NOP6 1", "P2SH,STRICTENC"], +["NOP", "NOP7 1", "P2SH,STRICTENC"], +["NOP", "NOP8 1", "P2SH,STRICTENC"], +["NOP", "NOP9 1", "P2SH,STRICTENC"], +["NOP", "NOP10 1", "P2SH,STRICTENC"], + +["", "0 0 0 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "CHECKMULTISIG is allowed to have zero keys and/or sigs"], +["", "0 0 0 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 0 1 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "Zero sigs means no sigs are checked"], +["", "0 0 0 1 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], + +["", "0 0 0 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "CHECKMULTISIG is allowed to have zero keys and/or sigs"], +["", "0 0 0 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 0 1 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "Zero sigs means no sigs are checked"], +["", "0 0 0 1 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], + +["", "0 0 'a' 'b' 2 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "Test from up to 20 pubkeys, all not checked"], +["", "0 0 'a' 'b' 'c' 3 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 4 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 5 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 6 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 7 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 8 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 9 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 10 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 11 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 12 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 13 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 14 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 15 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 16 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 17 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 18 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 19 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 1 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 2 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 3 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 4 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 5 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 6 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 7 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 8 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 9 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 10 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 11 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 12 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 13 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 14 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 15 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 16 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 17 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 18 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 19 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC"], + +["", +"0 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG", +"P2SH,STRICTENC", +"nOpCount is incremented by the number of keys evaluated in addition to the usual one op per op. In this case we have zero keys, so we can execute 201 CHECKMULTISIGS"], + +["1", +"0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY", +"P2SH,STRICTENC"], + +["", +"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG", +"P2SH,STRICTENC", +"Even though there are no signatures being checked nOpCount is incremented by the number of keys."], + +["1", +"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY", +"P2SH,STRICTENC"], + +["0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "P2SH,STRICTENC", "Very basic P2SH"], +["0x4c 0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "P2SH,STRICTENC"], + +["0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", +"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL", +"P2SH,STRICTENC", +"Basic PUSH signedness check"], + +["0x4c 0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", +"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL", +"P2SH,STRICTENC", +"Basic PUSHDATA1 signedness check"], + +["all PUSHDATA forms are equivalent"], + +["0x4c 0x4b 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "0x4b 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 EQUAL", "", "PUSHDATA1 of 75 bytes equals direct push of it"], +["0x4d 0xFF00 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "0x4c 0xFF 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 EQUAL", "", "PUSHDATA2 of 255 bytes equals PUSHDATA1 of it"], + +["0x00", "SIZE 0 EQUAL", "P2SH,STRICTENC", "Basic OP_0 execution"], + +["Numeric pushes"], + +["0x01 0x81", "0x4f EQUAL", "", "OP1_NEGATE pushes 0x81"], +["0x01 0x01", "0x51 EQUAL", "", "OP_1 pushes 0x01"], +["0x01 0x02", "0x52 EQUAL", "", "OP_2 pushes 0x02"], +["0x01 0x03", "0x53 EQUAL", "", "OP_3 pushes 0x03"], +["0x01 0x04", "0x54 EQUAL", "", "OP_4 pushes 0x04"], +["0x01 0x05", "0x55 EQUAL", "", "OP_5 pushes 0x05"], +["0x01 0x06", "0x56 EQUAL", "", "OP_6 pushes 0x06"], +["0x01 0x07", "0x57 EQUAL", "", "OP_7 pushes 0x07"], +["0x01 0x08", "0x58 EQUAL", "", "OP_8 pushes 0x08"], +["0x01 0x09", "0x59 EQUAL", "", "OP_9 pushes 0x09"], +["0x01 0x0a", "0x5a EQUAL", "", "OP_10 pushes 0x0a"], +["0x01 0x0b", "0x5b EQUAL", "", "OP_11 pushes 0x0b"], +["0x01 0x0c", "0x5c EQUAL", "", "OP_12 pushes 0x0c"], +["0x01 0x0d", "0x5d EQUAL", "", "OP_13 pushes 0x0d"], +["0x01 0x0e", "0x5e EQUAL", "", "OP_14 pushes 0x0e"], +["0x01 0x0f", "0x5f EQUAL", "", "OP_15 pushes 0x0f"], +["0x01 0x10", "0x60 EQUAL", "", "OP_16 pushes 0x10"], + +["Equivalency of different numeric encodings"], + +["0x02 0x8000", "128 NUMEQUAL", "", "0x8000 equals 128"], +["0x01 0x00", "0 NUMEQUAL", "", "0x00 numequals 0"], +["0x01 0x80", "0 NUMEQUAL", "", "0x80 (negative zero) numequals 0"], +["0x02 0x0080", "0 NUMEQUAL", "", "0x0080 numequals 0"], +["0x02 0x0500", "5 NUMEQUAL", "", "0x0500 numequals 5"], +["0x03 0xff7f80", "0x02 0xffff NUMEQUAL", "", ""], +["0x03 0xff7f00", "0x02 0xff7f NUMEQUAL", "", ""], +["0x04 0xffff7f80", "0x03 0xffffff NUMEQUAL", "", ""], +["0x04 0xffff7f00", "0x03 0xffff7f NUMEQUAL", "", ""], + +["Unevaluated non-minimal pushes are ignored"], + +["0 IF 0x4c 0x00 ENDIF 1", "", "MINIMALDATA", "non-minimal PUSHDATA1 ignored"], +["0 IF 0x4d 0x0000 ENDIF 1", "", "MINIMALDATA", "non-minimal PUSHDATA2 ignored"], +["0 IF 0x4c 0x00000000 ENDIF 1", "", "MINIMALDATA", "non-minimal PUSHDATA4 ignored"], +["0 IF 0x01 0x81 ENDIF 1", "", "MINIMALDATA", "1NEGATE equiv"], +["0 IF 0x01 0x01 ENDIF 1", "", "MINIMALDATA", "OP_1 equiv"], +["0 IF 0x01 0x02 ENDIF 1", "", "MINIMALDATA", "OP_2 equiv"], +["0 IF 0x01 0x03 ENDIF 1", "", "MINIMALDATA", "OP_3 equiv"], +["0 IF 0x01 0x04 ENDIF 1", "", "MINIMALDATA", "OP_4 equiv"], +["0 IF 0x01 0x05 ENDIF 1", "", "MINIMALDATA", "OP_5 equiv"], +["0 IF 0x01 0x06 ENDIF 1", "", "MINIMALDATA", "OP_6 equiv"], +["0 IF 0x01 0x07 ENDIF 1", "", "MINIMALDATA", "OP_7 equiv"], +["0 IF 0x01 0x08 ENDIF 1", "", "MINIMALDATA", "OP_8 equiv"], +["0 IF 0x01 0x09 ENDIF 1", "", "MINIMALDATA", "OP_9 equiv"], +["0 IF 0x01 0x0a ENDIF 1", "", "MINIMALDATA", "OP_10 equiv"], +["0 IF 0x01 0x0b ENDIF 1", "", "MINIMALDATA", "OP_11 equiv"], +["0 IF 0x01 0x0c ENDIF 1", "", "MINIMALDATA", "OP_12 equiv"], +["0 IF 0x01 0x0d ENDIF 1", "", "MINIMALDATA", "OP_13 equiv"], +["0 IF 0x01 0x0e ENDIF 1", "", "MINIMALDATA", "OP_14 equiv"], +["0 IF 0x01 0x0f ENDIF 1", "", "MINIMALDATA", "OP_15 equiv"], +["0 IF 0x01 0x10 ENDIF 1", "", "MINIMALDATA", "OP_16 equiv"], + +["Numeric minimaldata rules are only applied when a stack item is numerically evaluated; the push itself is allowed"], + +["0x01 0x00", "1", "MINIMALDATA"], +["0x01 0x80", "1", "MINIMALDATA"], +["0x02 0x0180", "1", "MINIMALDATA"], +["0x02 0x0100", "1", "MINIMALDATA"], +["0x02 0x0200", "1", "MINIMALDATA"], +["0x02 0x0300", "1", "MINIMALDATA"], +["0x02 0x0400", "1", "MINIMALDATA"], +["0x02 0x0500", "1", "MINIMALDATA"], +["0x02 0x0600", "1", "MINIMALDATA"], +["0x02 0x0700", "1", "MINIMALDATA"], +["0x02 0x0800", "1", "MINIMALDATA"], +["0x02 0x0900", "1", "MINIMALDATA"], +["0x02 0x0a00", "1", "MINIMALDATA"], +["0x02 0x0b00", "1", "MINIMALDATA"], +["0x02 0x0c00", "1", "MINIMALDATA"], +["0x02 0x0d00", "1", "MINIMALDATA"], +["0x02 0x0e00", "1", "MINIMALDATA"], +["0x02 0x0f00", "1", "MINIMALDATA"], +["0x02 0x1000", "1", "MINIMALDATA"], + +["Valid version of the 'Test every numeric-accepting opcode for correct handling of the numeric minimal encoding rule' script_invalid test"], + +["1 0x02 0x0000", "PICK DROP", ""], +["1 0x02 0x0000", "ROLL DROP 1", ""], +["0x02 0x0000", "1ADD DROP 1", ""], +["0x02 0x0000", "1SUB DROP 1", ""], +["0x02 0x0000", "NEGATE DROP 1", ""], +["0x02 0x0000", "ABS DROP 1", ""], +["0x02 0x0000", "NOT DROP 1", ""], +["0x02 0x0000", "0NOTEQUAL DROP 1", ""], + +["0 0x02 0x0000", "ADD DROP 1", ""], +["0x02 0x0000 0", "ADD DROP 1", ""], +["0 0x02 0x0000", "SUB DROP 1", ""], +["0x02 0x0000 0", "SUB DROP 1", ""], +["0 0x02 0x0000", "BOOLAND DROP 1", ""], +["0x02 0x0000 0", "BOOLAND DROP 1", ""], +["0 0x02 0x0000", "BOOLOR DROP 1", ""], +["0x02 0x0000 0", "BOOLOR DROP 1", ""], +["0 0x02 0x0000", "NUMEQUAL DROP 1", ""], +["0x02 0x0000 1", "NUMEQUAL DROP 1", ""], +["0 0x02 0x0000", "NUMEQUALVERIFY 1", ""], +["0x02 0x0000 0", "NUMEQUALVERIFY 1", ""], +["0 0x02 0x0000", "NUMNOTEQUAL DROP 1", ""], +["0x02 0x0000 0", "NUMNOTEQUAL DROP 1", ""], +["0 0x02 0x0000", "LESSTHAN DROP 1", ""], +["0x02 0x0000 0", "LESSTHAN DROP 1", ""], +["0 0x02 0x0000", "GREATERTHAN DROP 1", ""], +["0x02 0x0000 0", "GREATERTHAN DROP 1", ""], +["0 0x02 0x0000", "LESSTHANOREQUAL DROP 1", ""], +["0x02 0x0000 0", "LESSTHANOREQUAL DROP 1", ""], +["0 0x02 0x0000", "GREATERTHANOREQUAL DROP 1", ""], +["0x02 0x0000 0", "GREATERTHANOREQUAL DROP 1", ""], +["0 0x02 0x0000", "MIN DROP 1", ""], +["0x02 0x0000 0", "MIN DROP 1", ""], +["0 0x02 0x0000", "MAX DROP 1", ""], +["0x02 0x0000 0", "MAX DROP 1", ""], + +["0x02 0x0000 0 0", "WITHIN DROP 1", ""], +["0 0x02 0x0000 0", "WITHIN DROP 1", ""], +["0 0 0x02 0x0000", "WITHIN DROP 1", ""], + +["0 0 0x02 0x0000", "CHECKMULTISIG DROP 1", ""], +["0 0x02 0x0000 0", "CHECKMULTISIG DROP 1", ""], +["0 0x02 0x0000 0 1", "CHECKMULTISIG DROP 1", ""], +["0 0 0x02 0x0000", "CHECKMULTISIGVERIFY 1", ""], +["0 0x02 0x0000 0", "CHECKMULTISIGVERIFY 1", ""], + +["While not really correctly DER encoded, the empty signature is allowed by"], +["STRICTENC to provide a compact way to provide a delibrately invalid signature."], +["0", "0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 CHECKSIG NOT", "STRICTENC"], +["0 0", "1 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 1 CHECKMULTISIG NOT", "STRICTENC"], + +["CHECKMULTISIG evaluation order tests. CHECKMULTISIG evaluates signatures and"], +["pubkeys in a specific order, and will exit early if the number of signatures"], +["left to check is greater than the number of keys left. As STRICTENC fails the"], +["script when it reaches an invalidly encoded signature or pubkey, we can use it"], +["to test the exact order in which signatures and pubkeys are evaluated by"], +["distinguishing CHECKMULTISIG returning false on the stack and the script as a"], +["whole failing."], +["See also the corresponding inverted versions of these tests in script_invalid.json"], +[ + "0 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501", + "2 0 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 2 CHECKMULTISIG NOT", + "STRICTENC", + "2-of-2 CHECKMULTISIG NOT with the second pubkey invalid, and both signatures validly encoded. Valid pubkey fails, and CHECKMULTISIG exits early, prior to evaluation of second invalid pubkey." +], +[ + "0 0 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501", + "2 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 2 CHECKMULTISIG NOT", + "STRICTENC", + "2-of-2 CHECKMULTISIG NOT with both pubkeys valid, but second signature invalid. Valid pubkey fails, and CHECKMULTISIG exits early, prior to evaluation of second invalid signature." +], + +["Increase test coverage for DERSIG"], +["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "Overly long signature is correctly encoded"], +["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "Missing S is correctly encoded"], +["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "", "S with invalid S length is correctly encoded"], +["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Non-integer R is correctly encoded"], +["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Non-integer S is correctly encoded"], +["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Zero-length R is correctly encoded"], +["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "", "Zero-length S is correctly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "", "Negative S is correctly encoded"], + +["Automatically generated test cases"], +[ + "0x47 0x304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001", + "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "", + "P2PK" +], +[ + "0x47 0x304402206e05a6fe23c59196ffe176c9ddc31e73a9885638f9d1328d47c0c703863b8876022076feb53811aa5b04e0e79f938eb19906cc5e67548bc555a8e8b8b0fc603d840c01 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508", + "DUP HASH160 0x14 0x1018853670f9f3b0582c5b9ee8ce93764ac32b93 EQUALVERIFY CHECKSIG", + "", + "P2PKH" +], +[ + "0x47 0x304402204710a85181663b32d25c70ec2bbd14adff5ddfff6cb50d09e155ef5f541fc86c0220056b0cc949be9386ecc5f6c2ac0493269031dbb185781db90171b54ac127790281", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", + "", + "P2PK anyonecanpay" +], +[ + "0x47 0x3044022003fef42ed6c7be8917441218f525a60e2431be978e28b7aca4d7a532cc413ae8022067a1f82c74e8d69291b90d148778405c6257bbcfc2353cc38a3e1f22bf44254601 0x23 0x210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", + "HASH160 0x14 0x23b0ad3477f2178bc0b3eed26e4e6316f4e83aa1 EQUAL", + "P2SH", + "P2SH(P2PK)" +], +[ + "0x47 0x304402204e2eb034be7b089534ac9e798cf6a2c79f38bcb34d1b179efd6f2de0841735db022071461beb056b5a7be1819da6a3e3ce3662831ecc298419ca101eb6887b5dd6a401 0x19 0x76a9147cf9c846cd4882efec4bf07e44ebdad495c94f4b88ac", + "HASH160 0x14 0x2df519943d5acc0ef5222091f9dfe3543f489a82 EQUAL", + "", + "P2SH(P2PKH), bad sig but no VERIFY_P2SH" +], +[ + "0 0x47 0x3044022051254b9fb476a52d85530792b578f86fea70ec1ffb4393e661bcccb23d8d63d3022076505f94a403c86097841944e044c70c2045ce90e36de51f7e9d3828db98a07501 0x47 0x304402200a358f750934b3feb822f1966bfcd8bbec9eeaa3a8ca941e11ee5960e181fa01022050bf6b5a8e7750f70354ae041cb68a7bade67ec6c3ab19eb359638974410626e01 0x47 0x304402200955d031fff71d8653221e85e36c3c85533d2312fc3045314b19650b7ae2f81002202a6bb8505e36201909d0921f01abff390ae6b7ff97bbf959f98aedeb0a56730901", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", + "", + "3-of-3" +], +[ + "0 0x47 0x304402205b7d2c2f177ae76cfbbf14d589c113b0b35db753d305d5562dd0b61cbf366cfb02202e56f93c4f08a27f986cd424ffc48a462c3202c4902104d4d0ff98ed28f4bf8001 0x47 0x30440220563e5b3b1fc11662a84bc5ea2a32cc3819703254060ba30d639a1aaf2d5068ad0220601c1f47ddc76d93284dd9ed68f7c9974c4a0ea7cbe8a247d6bc3878567a5fca01 0x4c69 0x52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f515082103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff464053ae", + "HASH160 0x14 0xc9e4a896d149702d0d1695434feddd52e24ad78d EQUAL", + "P2SH", + "P2SH(2-of-3)" +], +[ + "0x47 0x304402200060558477337b9022e70534f1fea71a318caf836812465a2509931c5e7c4987022078ec32bd50ac9e03a349ba953dfd9fe1c8d2dd8bdb1d38ddca844d3d5c78c11801", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "P2PK with too much R padding but no DERSIG" +], +[ + "0x48 0x304502202de8c03fc525285c9c535631019a5f2af7c6454fa9eb392a3756a4917c420edd02210046130bf2baf7cfc065067c8b9e33a066d9c15edcea9feb0ca2d233e3597925b401", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "P2PK with too much S padding but no DERSIG" +], +[ + "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "P2PK with too little R padding but no DERSIG" +], +[ + "0x47 0x30440220005ece1335e7f757a1a1f476a7fb5bd90964e8a022489f890614a04acfb734c002206c12b8294a6513c7710e8c82d3c23d75cdbfe83200eb7efb495701958501a5d601", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "", + "P2PK NOT with bad sig with too much R padding but no DERSIG" +], +[ + "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "BIP66 example 1, without DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "BIP66 example 4, without DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "BIP66 example 4, with DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "BIP66 example 6, without DERSIG" +], +[ + "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0x47 0x3044022027c2714269ca5aeecc4d70edc88ba5ee0e3da4986e9216028f489ab4f1b8efce022022bd545b4951215267e4c5ceabd4c5350331b2e4a0b6494c56f361fa5a57a1a201", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "", + "BIP66 example 7, without DERSIG" +], +[ + "0 0 0x47 0x30440220da6f441dc3b4b2c84cfa8db0cd5b34ed92c9e01686de5a800d40498b70c0dcac02207c2cf91b0c32b860c4cd4994be36cfb84caf8bb7c3a8e4d96a31b2022c5299c501", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "BIP66 example 10, without DERSIG" +], +[ + "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "BIP66 example 12, without DERSIG" +], +[ + "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "BIP66 example 12, with DERSIG" +], +[ + "0x48 0x304402203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022054e1c258c2981cdfba5df1f46661fb6541c44f77ca0092f3600331abfffb12510101", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "", + "P2PK with multi-byte hashtype, without DERSIG" +], +[ + "0x48 0x304502203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022100ab1e3da73d67e32045a20e0b999e049978ea8d6ee5480d485fcf2ce0d03b2ef001", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "", + "P2PK with high S but no LOW_S" +], +[ + "0x47 0x3044022057292e2d4dfe775becdd0a9e6547997c728cdf35390f6a017da56d654d374e4902206b643be2fc53763b4e284845bfea2c597d2dc7759941dce937636c9d341b71ed01", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "", + "P2PK with hybrid pubkey but no STRICTENC" +], +[ + "0x47 0x30440220035d554e3153c04950c9993f41c496607a8e24093db0595be7bf875cf64fcf1f02204731c8c4e5daf15e706cec19cdd8f2c5b1d05490e11dab8465ed426569b6e92101", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG NOT", + "", + "P2PK NOT with invalid hybrid pubkey but no STRICTENC" +], +[ + "0 0x47 0x304402202e79441ad1baf5a07fb86bae3753184f6717d9692680947ea8b6e8b777c69af1022079a262e13d868bb5a0964fefe3ba26942e1b0669af1afb55ef3344bc9d4fc4c401", + "1 0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 2 CHECKMULTISIG", + "", + "1-of-2 with the second 1 hybrid pubkey and no STRICTENC" +], +[ + "0 0x47 0x304402202e79441ad1baf5a07fb86bae3753184f6717d9692680947ea8b6e8b777c69af1022079a262e13d868bb5a0964fefe3ba26942e1b0669af1afb55ef3344bc9d4fc4c401", + "1 0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 2 CHECKMULTISIG", + "STRICTENC", + "1-of-2 with the second 1 hybrid pubkey" +], +[ + "0x47 0x304402206177d513ec2cda444c021a1f4f656fc4c72ba108ae063e157eb86dc3575784940220666fc66702815d0e5413bb9b1df22aed44f5f1efb8b99d41dd5dc9a5be6d205205", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", + "", + "P2PK with undefined hashtype but no STRICTENC" +], +[ + "0x47 0x304402207409b5b320296e5e2136a7b281a7f803028ca4ca44e2b83eebd46932677725de02202d4eea1c8d3c98e6f42614f54764e6e5e6542e213eb4d079737e9a8b6e9812ec05", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG NOT", + "", + "P2PK NOT with invalid sig and undefined hashtype but no STRICTENC" +], +[ + "1 0x47 0x3044022051254b9fb476a52d85530792b578f86fea70ec1ffb4393e661bcccb23d8d63d3022076505f94a403c86097841944e044c70c2045ce90e36de51f7e9d3828db98a07501 0x47 0x304402200a358f750934b3feb822f1966bfcd8bbec9eeaa3a8ca941e11ee5960e181fa01022050bf6b5a8e7750f70354ae041cb68a7bade67ec6c3ab19eb359638974410626e01 0x47 0x304402200955d031fff71d8653221e85e36c3c85533d2312fc3045314b19650b7ae2f81002202a6bb8505e36201909d0921f01abff390ae6b7ff97bbf959f98aedeb0a56730901", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", + "", + "3-of-3 with nonzero dummy but no NULLDUMMY" +], +[ + "1 0x47 0x304402201bb2edab700a5d020236df174fefed78087697143731f659bea59642c759c16d022061f42cdbae5bcd3e8790f20bf76687443436e94a634321c16a72aa54cbc7c2ea01 0x47 0x304402204bb4a64f2a6e5c7fb2f07fef85ee56fde5e6da234c6a984262307a20e99842d702206f8303aaba5e625d223897e2ffd3f88ef1bcffef55f38dc3768e5f2e94c923f901 0x47 0x3044022040c2809b71fffb155ec8b82fe7a27f666bd97f941207be4e14ade85a1249dd4d02204d56c85ec525dd18e29a0533d5ddf61b6b1bb32980c2f63edf951aebf7a27bfe01", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG NOT", + "", + "3-of-3 NOT with invalid sig and nonzero dummy but no NULLDUMMY" +], +[ + "0 0x47 0x304402200abeb4bd07f84222f474aed558cfbdfc0b4e96cde3c2935ba7098b1ff0bd74c302204a04c1ca67b2a20abee210cf9a21023edccbbf8024b988812634233115c6b73901 DUP", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 2 CHECKMULTISIG", + "", + "2-of-2 with two identical keys and sigs pushed using OP_DUP but no SIGPUSHONLY" +], +[ + "0 0x47 0x304402200abeb4bd07f84222f474aed558cfbdfc0b4e96cde3c2935ba7098b1ff0bd74c302204a04c1ca67b2a20abee210cf9a21023edccbbf8024b988812634233115c6b73901 0x47 0x304402200abeb4bd07f84222f474aed558cfbdfc0b4e96cde3c2935ba7098b1ff0bd74c302204a04c1ca67b2a20abee210cf9a21023edccbbf8024b988812634233115c6b73901", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 2 CHECKMULTISIG", + "SIGPUSHONLY", + "2-of-2 with two identical keys and sigs pushed" +], +[ + "11 0x47 0x304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001", + "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "P2SH", + "P2PK with unnecessary input but no CLEANSTACK" +], +[ + "11 0x47 0x304402202f7505132be14872581f35d74b759212d9da40482653f1ffa3116c3294a4a51702206adbf347a2240ca41c66522b1a22a41693610b76a8e7770645dc721d1635854f01 0x43 0x410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + "HASH160 0x14 0x31edc23bdafda4639e669f89ad6b2318dd79d032 EQUAL", + "P2SH", + "P2SH with unnecessary input but no CLEANSTACK" +], +[ + "0x47 0x304402202f7505132be14872581f35d74b759212d9da40482653f1ffa3116c3294a4a51702206adbf347a2240ca41c66522b1a22a41693610b76a8e7770645dc721d1635854f01 0x43 0x410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + "HASH160 0x14 0x31edc23bdafda4639e669f89ad6b2318dd79d032 EQUAL", + "CLEANSTACK,P2SH", + "P2SH with CLEANSTACK" +], + +["The End"] +] diff --git a/vendor/github.com/btcsuite/btcd/txscript/data/sighash.json b/vendor/github.com/btcsuite/btcd/txscript/data/sighash.json new file mode 100644 index 0000000000000000000000000000000000000000..d66a56ac35bdba08148bcb5db94c11a6df107097 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/data/sighash.json @@ -0,0 +1,503 @@ +[ + ["raw_transaction, script, input_index, hashType, signature_hash (result)"], + ["907c2bc503ade11cc3b04eb2918b6f547b0630ab569273824748c87ea14b0696526c66ba740200000004ab65ababfd1f9bdd4ef073c7afc4ae00da8a66f429c917a0081ad1e1dabce28d373eab81d8628de802000000096aab5253ab52000052ad042b5f25efb33beec9f3364e8a9139e8439d9d7e26529c3c30b6c3fd89f8684cfd68ea0200000009ab53526500636a52ab599ac2fe02a526ed040000000008535300516352515164370e010000000003006300ab2ec229", "", 2, 1864164639, "31af167a6cf3f9d5f6875caa4d31704ceb0eba078d132b78dab52c3b8997317e"], + ["a0aa3126041621a6dea5b800141aa696daf28408959dfb2df96095db9fa425ad3f427f2f6103000000015360290e9c6063fa26912c2e7fb6a0ad80f1c5fea1771d42f12976092e7a85a4229fdb6e890000000001abc109f6e47688ac0e4682988785744602b8c87228fcef0695085edf19088af1a9db126e93000000000665516aac536affffffff8fe53e0806e12dfd05d67ac68f4768fdbe23fc48ace22a5aa8ba04c96d58e2750300000009ac51abac63ab5153650524aa680455ce7b000000000000499e50030000000008636a00ac526563ac5051ee030000000003abacabd2b6fe000000000003516563910fb6b5", "65", 0, -1391424484, "48d6a1bd2cd9eec54eb866fc71209418a950402b5d7e52363bfb75c98e141175"], + ["6e7e9d4b04ce17afa1e8546b627bb8d89a6a7fefd9d892ec8a192d79c2ceafc01694a6a7e7030000000953ac6a51006353636a33bced1544f797f08ceed02f108da22cd24c9e7809a446c61eb3895914508ac91f07053a01000000055163ab516affffffff11dc54eee8f9e4ff0bcf6b1a1a35b1cd10d63389571375501af7444073bcec3c02000000046aab53514a821f0ce3956e235f71e4c69d91abe1e93fb703bd33039ac567249ed339bf0ba0883ef300000000090063ab65000065ac654bec3cc504bcf499020000000005ab6a52abac64eb060100000000076a6a5351650053bbbc130100000000056a6aab53abd6e1380100000000026a51c4e509b8", "acab655151", 0, 479279909, "2a3d95b09237b72034b23f2d2bb29fa32a58ab5c6aa72f6aafdfa178ab1dd01c"], + ["73107cbd025c22ebc8c3e0a47b2a760739216a528de8d4dab5d45cbeb3051cebae73b01ca10200000007ab6353656a636affffffffe26816dffc670841e6a6c8c61c586da401df1261a330a6c6b3dd9f9a0789bc9e000000000800ac6552ac6aac51ffffffff0174a8f0010000000004ac52515100000000", "5163ac63635151ac", 1, 1190874345, "06e328de263a87b09beabe222a21627a6ea5c7f560030da31610c4611f4a46bc"], + ["e93bbf6902be872933cb987fc26ba0f914fcfc2f6ce555258554dd9939d12032a8536c8802030000000453ac5353eabb6451e074e6fef9de211347d6a45900ea5aaf2636ef7967f565dce66fa451805c5cd10000000003525253ffffffff047dc3e6020000000007516565ac656aabec9eea010000000001633e46e600000000000015080a030000000001ab00000000", "5300ac6a53ab6a", 1, -886562767, "f03aa4fc5f97e826323d0daa03343ebf8a34ed67a1ce18631f8b88e5c992e798"], + ["50818f4c01b464538b1e7e7f5ae4ed96ad23c68c830e78da9a845bc19b5c3b0b20bb82e5e9030000000763526a63655352ffffffff023b3f9c040000000008630051516a6a5163a83caf01000000000553ab65510000000000", "6aac", 0, 946795545, "746306f322de2b4b58ffe7faae83f6a72433c22f88062cdde881d4dd8a5a4e2d"], + ["a93e93440250f97012d466a6cc24839f572def241c814fe6ae94442cf58ea33eb0fdd9bcc1030000000600636a0065acffffffff5dee3a6e7e5ad6310dea3e5b3ddda1a56bf8de7d3b75889fc024b5e233ec10f80300000007ac53635253ab53ffffffff0160468b04000000000800526a5300ac526a00000000", "ac00636a53", 1, 1773442520, "5c9d3a2ce9365bb72cfabbaa4579c843bb8abf200944612cf8ae4b56a908bcbd"], + ["ce7d371f0476dda8b811d4bf3b64d5f86204725deeaa3937861869d5b2766ea7d17c57e40b0100000003535265ffffffff7e7e9188f76c34a46d0bbe856bde5cb32f089a07a70ea96e15e92abb37e479a10100000006ab6552ab655225bcab06d1c2896709f364b1e372814d842c9c671356a1aa5ca4e060462c65ae55acc02d0000000006abac0063ac5281b33e332f96beebdbc6a379ebe6aea36af115c067461eb99d22ba1afbf59462b59ae0bd0200000004ab635365be15c23801724a1704000000000965006a65ac00000052ca555572", "53ab530051ab", 1, 2030598449, "c336b2f7d3702fbbdeffc014d106c69e3413c7c71e436ba7562d8a7a2871f181"], + ["d3b7421e011f4de0f1cea9ba7458bf3486bee722519efab711a963fa8c100970cf7488b7bb0200000003525352dcd61b300148be5d05000000000000000000", "535251536aac536a", 0, -1960128125, "29aa6d2d752d3310eba20442770ad345b7f6a35f96161ede5f07b33e92053e2a"], + ["04bac8c5033460235919a9c63c42b2db884c7c8f2ed8fcd69ff683a0a2cccd9796346a04050200000003655351fcad3a2c5a7cbadeb4ec7acc9836c3f5c3e776e5c566220f7f965cf194f8ef98efb5e3530200000007526a006552526526a2f55ba5f69699ece76692552b399ba908301907c5763d28a15b08581b23179cb01eac03000000075363ab6a516351073942c2025aa98a05000000000765006aabac65abd7ffa6030000000004516a655200000000", "53ac6365ac526a", 1, 764174870, "bf5fdc314ded2372a0ad078568d76c5064bf2affbde0764c335009e56634481b"], + ["c363a70c01ab174230bbe4afe0c3efa2d7f2feaf179431359adedccf30d1f69efe0c86ed390200000002ab51558648fe0231318b04000000000151662170000000000008ac5300006a63acac00000000", "", 0, 2146479410, "191ab180b0d753763671717d051f138d4866b7cb0d1d4811472e64de595d2c70"], + ["8d437a7304d8772210a923fd81187c425fc28c17a5052571501db05c7e89b11448b36618cd02000000026a6340fec14ad2c9298fde1477f1e8325e5747b61b7e2ff2a549f3d132689560ab6c45dd43c3010000000963ac00ac000051516a447ed907a7efffebeb103988bf5f947fc688aab2c6a7914f48238cf92c337fad4a79348102000000085352ac526a5152517436edf2d80e3ef06725227c970a816b25d0b58d2cd3c187a7af2cea66d6b27ba69bf33a0300000007000063ab526553f3f0d6140386815d030000000003ab6300de138f00000000000900525153515265abac1f87040300000000036aac6500000000", "51", 3, -315779667, "b6632ac53578a741ae8c36d8b69e79f39b89913a2c781cdf1bf47a8c29d997a5"], + ["fd878840031e82fdbe1ad1d745d1185622b0060ac56638290ec4f66b1beef4450817114a2c0000000009516a63ab53650051abffffffff37b7a10322b5418bfd64fb09cd8a27ddf57731aeb1f1f920ffde7cb2dfb6cdb70300000008536a5365ac53515369ecc034f1594690dbe189094dc816d6d57ea75917de764cbf8eccce4632cbabe7e116cd0100000003515352ffffffff035777fc000000000003515200abe9140300000000050063005165bed6d10200000000076300536363ab65195e9110", "635265", 0, 1729787658, "6e3735d37a4b28c45919543aabcb732e7a3e1874db5315abb7cc6b143d62ff10"], + ["f40a750702af06efff3ea68e5d56e42bc41cdb8b6065c98f1221fe04a325a898cb61f3d7ee030000000363acacffffffffb5788174aef79788716f96af779d7959147a0c2e0e5bfb6c2dba2df5b4b97894030000000965510065535163ac6affffffff0445e6fd0200000000096aac536365526a526aa6546b000000000008acab656a6552535141a0fd010000000000c897ea030000000008526500ab526a6a631b39dba3", "00abab5163ac", 1, -1778064747, "d76d0fc0abfa72d646df888bce08db957e627f72962647016eeae5a8412354cf"], + ["a63bc673049c75211aa2c09ecc38e360eaa571435fedd2af1116b5c1fa3d0629c269ecccbf0000000008ac65ab516352ac52ffffffffbf1a76fdda7f451a5f0baff0f9ccd0fe9136444c094bb8c544b1af0fa2774b06010000000463535253ffffffff13d6b7c3ddceef255d680d87181e100864eeb11a5bb6a3528cb0d70d7ee2bbbc02000000056a0052abab951241809623313b198bb520645c15ec96bfcc74a2b0f3db7ad61d455cc32db04afc5cc702000000016309c9ae25014d9473020000000004abab6aac3bb1e803", "", 3, -232881718, "6e48f3da3a4ac07eb4043a232df9f84e110485d7c7669dd114f679c27d15b97e"], + ["4c565efe04e7d32bac03ae358d63140c1cfe95de15e30c5b84f31bb0b65bb542d637f49e0f010000000551abab536348ae32b31c7d3132030a510a1b1aacf7b7c3f19ce8dc49944ef93e5fa5fe2d356b4a73a00100000009abac635163ac00ab514c8bc57b6b844e04555c0a4f4fb426df139475cd2396ae418bc7015820e852f711519bc202000000086a00510000abac52488ff4aec72cbcfcc98759c58e20a8d2d9725aa4a80f83964e69bc4e793a4ff25cd75dc701000000086a52ac6aac5351532ec6b10802463e0200000000000553005265523e08680100000000002f39a6b0", "", 3, 70712784, "c6076b6a45e6fcfba14d3df47a34f6aadbacfba107e95621d8d7c9c0e40518ed"], + ["1233d5e703403b3b8b4dae84510ddfc126b4838dcb47d3b23df815c0b3a07b55bf3098110e010000000163c5c55528041f480f40cf68a8762d6ed3efe2bd402795d5233e5d94bf5ddee71665144898030000000965525165655151656affffffff6381667e78bb74d0880625993bec0ea3bd41396f2bcccc3cc097b240e5e92d6a01000000096363acac6a63536365ffffffff04610ad60200000000065251ab65ab52e90d680200000000046351516ae30e98010000000008abab52520063656a671856010000000004ac6aac514c84e383", "6aabab636300", 1, -114996813, "aeb8c5a62e8a0b572c28f2029db32854c0b614dbecef0eaa726abebb42eebb8d"], + ["0c69702103b25ceaed43122cc2672de84a3b9aa49872f2a5bb458e19a52f8cc75973abb9f102000000055365656aacffffffff3ffb1cf0f76d9e3397de0942038c856b0ebbea355dc9d8f2b06036e19044b0450100000000ffffffff4b7793f4169617c54b734f2cd905ed65f1ce3d396ecd15b6c426a677186ca0620200000008655263526551006a181a25b703240cce0100000000046352ab53dee22903000000000865526a6a516a51005e121602000000000852ab52ababac655200000000", "6a516aab63", 1, -2040012771, "a6e6cb69f409ec14e10dd476f39167c29e586e99bfac93a37ed2c230fcc1dbbe"], + ["fd22692802db8ae6ab095aeae3867305a954278f7c076c542f0344b2591789e7e33e4d29f4020000000151ffffffffb9409129cfed9d3226f3b6bab7a2c83f99f48d039100eeb5796f00903b0e5e5e0100000006656552ac63abd226abac0403e649000000000007abab51ac5100ac8035f10000000000095165006a63526a52510d42db030000000007635365ac6a63ab24ef5901000000000453ab6a0000000000", "536a52516aac6a", 1, 309309168, "7ca0f75e6530ec9f80d031fc3513ca4ecd67f20cb38b4dacc6a1d825c3cdbfdb"], + ["a43f85f701ffa54a3cc57177510f3ea28ecb6db0d4431fc79171cad708a6054f6e5b4f89170000000008ac6a006a536551652bebeaa2013e779c05000000000665ac5363635100000000", "ac", 0, 2028978692, "58294f0d7f2e68fe1fd30c01764fe1619bcc7961d68968944a0e263af6550437"], + ["c2b0b99001acfecf7da736de0ffaef8134a9676811602a6299ba5a2563a23bb09e8cbedf9300000000026300ffffffff042997c50300000000045252536a272437030000000007655353ab6363ac663752030000000002ab6a6d5c900000000000066a6a5265abab00000000", "52ac525163515251", 0, -894181723, "8b300032a1915a4ac05cea2f7d44c26f2a08d109a71602636f15866563eaafdc"], + ["82f9f10304c17a9d954cf3380db817814a8c738d2c811f0412284b2c791ec75515f38c4f8c020000000265ab5729ca7db1b79abee66c8a757221f29280d0681355cb522149525f36da760548dbd7080a0100000001510b477bd9ce9ad5bb81c0306273a3a7d051e053f04ecf3a1dbeda543e20601a5755c0cfae030000000451ac656affffffff71141a04134f6c292c2e0d415e6705dfd8dcee892b0d0807828d5aeb7d11f5ef0300000001520b6c6dc802a6f3dd0000000000056aab515163bfb6800300000000015300000000", "", 3, -635779440, "d55ed1e6c53510f2608716c12132a11fb5e662ec67421a513c074537eeccc34b"], + ["8edcf5a1014b604e53f0d12fe143cf4284f86dc79a634a9f17d7e9f8725f7beb95e8ffcd2403000000046aabac52ffffffff01c402b5040000000005ab6a63525100000000", "6351525251acabab6a", 0, 1520147826, "2765bbdcd3ebb8b1a316c04656b28d637f80bffbe9b040661481d3dc83eea6d6"], + ["2074bad5011847f14df5ea7b4afd80cd56b02b99634893c6e3d5aaad41ca7c8ee8e5098df003000000026a6affffffff018ad59700000000000900ac656a526551635300000000", "65635265", 0, -1804671183, "663c999a52288c9999bff36c9da2f8b78d5c61b8347538f76c164ccba9868d0a"], + ["7100b11302e554d4ef249ee416e7510a485e43b2ba4b8812d8fe5529fe33ea75f36d392c4403000000020000ffffffff3d01a37e075e9a7715a657ae1bdf1e44b46e236ad16fd2f4c74eb9bf370368810000000007636553ac536365ffffffff01db696a0400000000065200ac656aac00000000", "63005151", 0, -1210499507, "b9c3aee8515a4a3b439de1ffc9c156824bda12cb75bfe5bc863164e8fd31bd7a"], + ["02c1017802091d1cb08fec512db7b012fe4220d57a5f15f9e7676358b012786e1209bcff950100000004acab6352ffffffff799bc282724a970a6fea1828984d0aeb0f16b67776fa213cbdc4838a2f1961a3010000000951516a536552ab6aabffffffff016c7b4b03000000000865abac5253ac5352b70195ad", "65655200516a", 0, -241626954, "be567cb47170b34ff81c66c1142cb9d27f9b6898a384d6dfc4fce16b75b6cb14"], + ["cb3178520136cd294568b83bb2520f78fecc507898f4a2db2674560d72fd69b9858f75b3b502000000066aac00515100ffffffff03ab005a01000000000563526363006e3836030000000001abfbda3200000000000665ab0065006500000000", "ab516a0063006a5300", 0, 1182109299, "2149e79c3f4513da4e4378608e497dcfdfc7f27c21a826868f728abd2b8a637a"], + ["18a4b0c004702cf0e39686ac98aab78ad788308f1d484b1ddfe70dc1997148ba0e28515c310300000000ffffffff05275a52a23c59da91129093364e275da5616c4070d8a05b96df5a2080ef259500000000096aac51656a6aac53ab66e64966b3b36a07dd2bb40242dd4a3743d3026e7e1e0d9e9e18f11d068464b989661321030000000265ac383339c4fae63379cafb63b0bab2eca70e1f5fc7d857eb5c88ccd6c0465093924bba8b2a000000000300636ab5e0545402bc2c4c010000000000cd41c002000000000000000000", "abac635253656a00", 3, 2052372230, "32db877b6b1ca556c9e859442329406f0f8246706522369839979a9f7a235a32"], + ["1d9c5df20139904c582285e1ea63dec934251c0f9cf5c47e86abfb2b394ebc57417a81f67c010000000353515222ba722504800d3402000000000353656a3c0b4a0200000000000fb8d20500000000076300ab005200516462f30400000000015200000000", "ab65", 0, -210854112, "edf73e2396694e58f6b619f68595b0c1cdcb56a9b3147845b6d6afdb5a80b736"], + ["4504cb1904c7a4acf375ddae431a74de72d5436efc73312cf8e9921f431267ea6852f9714a01000000066a656a656553a2fbd587c098b3a1c5bd1d6480f730a0d6d9b537966e20efc0e352d971576d0f87df0d6d01000000016321aeec3c4dcc819f1290edb463a737118f39ab5765800547522708c425306ebfca3f396603000000055300ac656a1d09281d05bfac57b5eb17eb3fa81ffcedfbcd3a917f1be0985c944d473d2c34d245eb350300000007656a51525152ac263078d9032f470f0500000000066aac00000052e12da60200000000003488410200000000076365006300ab539981e432", "52536a52526a", 1, -31909119, "f0a2deee7fd8a3a9fad6927e763ded11c940ee47e9e6d410f94fda5001f82e0c"], + ["14bc7c3e03322ec0f1311f4327e93059c996275302554473104f3f7b46ca179bfac9ef753503000000016affffffff9d405eaeffa1ca54d9a05441a296e5cc3a3e32bb8307afaf167f7b57190b07e00300000008abab51ab5263abab45533aa242c61bca90dd15d46079a0ab0841d85df67b29ba87f2393cd764a6997c372b55030000000452005263ffffffff0250f40e02000000000651516a0063630e95ab0000000000046a5151ac00000000", "6a65005151", 0, -1460947095, "aa418d096929394c9147be8818d8c9dafe6d105945ab9cd7ec682df537b5dd79"], + ["2b3bd0dd04a1832f893bf49a776cd567ec4b43945934f4786b615d6cb850dfc0349b33301a000000000565ac000051cf80c670f6ddafab63411adb4d91a69c11d9ac588898cbfb4cb16061821cc104325c895103000000025163ffffffffa9e2d7506d2d7d53b882bd377bbcc941f7a0f23fd15d2edbef3cd9df8a4c39d10200000009ac63006a52526a5265ffffffff44c099cdf10b10ce87d4b38658d002fd6ea17ae4a970053c05401d86d6e75f99000000000963ab53526a5252ab63ffffffff035af69c01000000000100ba9b8b0400000000004cead10500000000026a520b77d667", "ab52abac526553", 3, -1955078165, "eb9ceecc3b401224cb79a44d23aa8f428e29f1405daf69b4e01910b848ef1523"], + ["35df11f004a48ba439aba878fe9df20cc935b4a761c262b1b707e6f2b33e2bb7565cd68b130000000000ffffffffb2a2f99abf64163bb57ca900500b863f40c02632dfd9ea2590854c5fb4811da90200000006ac006363636affffffffaf9d89b2a8d2670ca37c8f7c140600b81259f2e037cb4590578ec6e37af8bf200000000005abac6a655270a4751eb551f058a93301ffeda2e252b6614a1fdd0e283e1d9fe53c96c5bbaafaac57b8030000000153ffffffff020d9f3b02000000000100ed7008030000000004abac000000000000", "abac", 3, 593793071, "88fdee1c2d4aeead71d62396e28dc4d00e5a23498eea66844b9f5d26d1f21042"], + ["a08ff466049fb7619e25502ec22fedfb229eaa1fe275aa0b5a23154b318441bf547989d0510000000005ab5363636affffffff2b0e335cb5383886751cdbd993dc0720817745a6b1c9b8ab3d15547fc9aafd03000000000965656a536a52656a532b53d10584c290d3ac1ab74ab0a19201a4a039cb59dc58719821c024f6bf2eb26322b33f010000000965ac6aac0053ab6353ffffffff048decba6ebbd2db81e416e39dde1f821ba69329725e702bcdea20c5cc0ecc6402000000086363ab5351ac6551466e377b0468c0fa00000000000651ab53ac6a513461c6010000000008636a636365535100eeb3dc010000000006526a52ac516a43f362010000000005000063536500000000", "0063516a", 1, -1158911348, "f6a1ecb50bd7c2594ebecea5a1aa23c905087553e40486dade793c2f127fdfae"], + ["5ac2f17d03bc902e2bac2469907ec7d01a62b5729340bc58c343b7145b66e6b97d434b30fa000000000163ffffffff44028aa674192caa0d0b4ebfeb969c284cb16b80c312d096efd80c6c6b094cca000000000763acabac516a52ffffffff10c809106e04b10f9b43085855521270fb48ab579266e7474657c6c625062d2d030000000351636595a0a97004a1b69603000000000465ab005352ad68010000000008636a5263acac5100da7105010000000002acab90325200000000000000000000", "6a6aab516a63526353", 2, 1518400956, "f7efb74b1dcc49d316b49c632301bc46f98d333c427e55338be60c7ef0d953be"], + ["aeb2e11902dc3770c218b97f0b1960d6ee70459ecb6a95eff3f05295dc1ef4a0884f10ba460300000005516352526393e9b1b3e6ae834102d699ddd3845a1e159aa7cf7635edb5c02003f7830fee3788b795f20100000009ab006a526553ac006ad8809c570469290e0400000000050000abab00b10fd5040000000008ab655263abac53ab630b180300000000009d9993040000000002516300000000", "5351ababac6a65", 0, 1084852870, "f2286001af0b0170cbdad92693d0a5ebaa8262a4a9d66e002f6d79a8c94026d1"], + ["9860ca9a0294ff4812534def8c3a3e3db35b817e1a2ddb7f0bf673f70eab71bb79e90a2f3100000000086a636551acac5165ffffffffed4d6d3cd9ff9b2d490e0c089739121161a1445844c3e204296816ab06e0a83702000000035100ac88d0db5201c3b59a050000000005ac6a0051ab00000000", "535263ab006a526aab", 1, -962088116, "30df2473e1403e2b8e637e576825f785528d998af127d501556e5f7f5ed89a2a"], + ["4ddaa680026ec4d8060640304b86823f1ac760c260cef81d85bd847952863d629a3002b54b0200000008526365636a656aab65457861fc6c24bdc760c8b2e906b6656edaf9ed22b5f50e1fb29ec076ceadd9e8ebcb6b000000000152ffffffff033ff04f00000000000551526a00657a1d900300000000002153af040000000003006a6300000000", "ab526a53acabab", 0, 1055317633, "7f21b62267ed52462e371a917eb3542569a4049b9dfca2de3c75872b39510b26"], + ["01e76dcd02ad54cbc8c71d68eaf3fa7c883b65d74217b30ba81f1f5144ef80b706c0dc82ca000000000352ab6a078ec18bcd0514825feced2e8b8ea1ccb34429fae41c70cc0b73a2799e85603613c6870002000000086363ab6365536a53ffffffff043acea90000000000016ad20e1803000000000100fa00830200000000056352515351e864ee00000000000865535253ab6a6551d0c46672", "6a6365abacab", 0, -1420559003, "8af0b4cbdbc011be848edf4dbd2cde96f0578d662cfebc42252495387114224a"], + ["fa00b26402670b97906203434aa967ce1559d9bd097d56dbe760469e6032e7ab61accb54160100000006635163630052fffffffffe0d3f4f0f808fd9cfb162e9f0c004601acf725cd7ea5683bbdc9a9a433ef15a0200000005ab52536563d09c7bef049040f305000000000153a7c7b9020000000004ac63ab52847a2503000000000553ab00655390ed80010000000005006553ab52860671d4", "536565ab52", 0, 799022412, "40ed8e7bbbd893e15f3cce210ae02c97669818de5946ca37eefc7541116e2c78"], + ["cb5c06dc01b022ee6105ba410f0eb12b9ce5b5aa185b28532492d839a10cef33d06134b91b010000000153ffffffff02cec0530400000000005e1e4504000000000865656551acacac6a00000000", "ab53", 0, -1514251329, "136beb95459fe6b126cd6cefd54eb5d971524b0e883e41a292a78f78015cb8d5"], + ["f10a0356031cd569d652dbca8e7a4d36c8da33cdff428d003338602b7764fe2c96c505175b010000000465ac516affffffffbb54563c71136fa944ee20452d78dc87073ac2365ba07e638dce29a5d179da600000000003635152ffffffff9a411d8e2d421b1e6085540ee2809901e590940bbb41532fa38bd7a16b68cc350100000007535251635365636195df1603b61c45010000000002ab65bf6a310400000000026352fcbba10200000000016aa30b7ff0", "5351", 0, 1552495929, "9eb8adf2caecb4bf9ac59d7f46bd20e83258472db2f569ee91aba4cf5ee78e29"], + ["c3325c9b012f659466626ca8f3c61dfd36f34670abc054476b7516a1839ec43cd0870aa0c0000000000753525265005351e7e3f04b0112650500000000000363ac6300000000", "acac", 0, -68961433, "5ca70e727d91b1a42b78488af2ed551642c32d3de4712a51679f60f1456a8647"], + ["2333e54c044370a8af16b9750ac949b151522ea6029bacc9a34261599549581c7b4e5ece470000000007510052006563abffffffff80630fc0155c750ce20d0ca4a3d0c8e8d83b014a5b40f0b0be0dd4c63ac28126020000000465000000ffffffff1b5f1433d38cdc494093bb1d62d84b10abbdae57e3d04e82e600857ab3b1dc990300000003515100b76564be13e4890a908ea7508afdad92ec1b200a9a67939fadce6eb7a29eb4550a0a28cb0300000001acffffffff02926c930300000000016373800201000000000153d27ee740", "ab6365ab516a53", 3, 598653797, "2be27a686eb7940dd32c44ff3a97c1b28feb7ab9c5c0b1593b2d762361cfc2db"], + ["b500ca48011ec57c2e5252e5da6432089130603245ffbafb0e4c5ffe6090feb629207eeb0e010000000652ab6a636aab8302c9d2042b44f40500000000015278c05a050000000004ac5251524be080020000000007636aac63ac5252c93a9a04000000000965ab6553636aab5352d91f9ddb", "52005100", 0, -2024394677, "49c8a6940a461cc7225637f1e512cdd174c99f96ec05935a59637ededc77124c"], + ["f52ff64b02ee91adb01f3936cc42e41e1672778962b68cf013293d649536b519bc3271dd2c00000000020065afee11313784849a7c15f44a61cd5fd51ccfcdae707e5896d131b082dc9322a19e12858501000000036aac654e8ca882022deb7c020000000006006a515352abd3defc0000000000016300000000", "63520063", 0, 1130989496, "7f208df9a5507e98c62cebc5c1e2445eb632e95527594929b9577b53363e96f6"], + ["ab7d6f36027a7adc36a5cf7528fe4fb5d94b2c96803a4b38a83a675d7806dda62b380df86a0000000003000000ffffffff5bc00131e29e22057c04be854794b4877dda42e416a7a24706b802ff9da521b20000000007ac6a0065ac52ac957cf45501b9f06501000000000500ac6363ab25f1110b", "00526500536a635253", 0, 911316637, "5fa09d43c8aef6f6fa01c383a69a5a61a609cd06e37dce35a39dc9eae3ddfe6c"], + ["f940888f023dce6360263c850372eb145b864228fdbbb4c1186174fa83aab890ff38f8c9a90300000000ffffffff01e80ccdb081e7bbae1c776531adcbfb77f2e5a7d0e5d0d0e2e6c8758470e85f00000000020053ffffffff03b49088050000000004656a52ab428bd604000000000951630065ab63ac636a0cbacf0400000000070063ac5265ac53d6e16604", "ac63", 0, 39900215, "713ddeeefcfe04929e7b6593c792a4efbae88d2b5280d1f0835d2214eddcbad6"], + ["530ecd0b01ec302d97ef6f1b5a6420b9a239714013e20d39aa3789d191ef623fc215aa8b940200000005ac5351ab6a3823ab8202572eaa04000000000752ab6a51526563fd8a270100000000036a006581a798f0", "525153656a0063", 0, 1784562684, "fe42f73a8742676e640698222b1bd6b9c338ff1ccd766d3d88d7d3c6c6ac987e"], + ["5d781d9303acfcce964f50865ddfddab527ea971aee91234c88e184979985c00b4de15204b0100000003ab6352a009c8ab01f93c8ef2447386c434b4498538f061845862c3f9d5751ad0fce52af442b3a902000000045165ababb909c66b5a3e7c81b3c45396b944be13b8aacfc0204f3f3c105a66fa8fa6402f1b5efddb01000000096a65ac636aacab656ac3c677c402b79fa4050000000004006aab5133e35802000000000751ab635163ab0078c2e025", "6aac51636a6a005265", 0, -882306874, "551ce975d58647f10adefb3e529d9bf9cda34751627ec45e690f135ef0034b95"], + ["25ee54ef0187387564bb86e0af96baec54289ca8d15e81a507a2ed6668dc92683111dfb7a50100000004005263634cecf17d0429aa4d000000000007636a6aabab5263daa75601000000000251ab4df70a01000000000151980a890400000000065253ac6a006377fd24e3", "65ab", 0, 797877378, "069f38fd5d47abff46f04ee3ae27db03275e9aa4737fa0d2f5394779f9654845"], + ["a9c57b1a018551bcbc781b256642532bbc09967f1cbe30a227d352a19365d219d3f11649a3030000000451655352b140942203182894030000000006ab00ac6aab654add350400000000003d379505000000000553abacac00e1739d36", "5363", 0, -1069721025, "6da32416deb45a0d720a1dbe6d357886eabc44029dd5db74d50feaffbe763245"], + ["05c4fb94040f5119dc0b10aa9df054871ed23c98c890f1e931a98ffb0683dac45e98619fdc0200000007acab6a525263513e7495651c9794c4d60da835d303eb4ee6e871f8292f6ad0b32e85ef08c9dc7aa4e03c9c010000000500ab52acacfffffffffee953259cf14ced323fe8d567e4c57ba331021a1ef5ac2fa90f7789340d7c550100000007ac6aacac6a6a53ffffffff08d9dc820d00f18998af247319f9de5c0bbd52a475ea587f16101af3afab7c210100000003535363569bca7c0468e34f00000000000863536353ac51ac6584e319010000000006650052ab6a533debea030000000003ac0053ee7070020000000006ac52005253ac00000000", "6351005253", 2, 1386916157, "76c4013c40bfa1481badd9d342b6d4b8118de5ab497995fafbf73144469e5ff0"], + ["c95ab19104b63986d7303f4363ca8f5d2fa87c21e3c5d462b99f1ebcb7c402fc012f5034780000000009006aac63ac65655265ffffffffbe91afa68af40a8700fd579c86d4b706c24e47f7379dad6133de389f815ef7f501000000046aac00abffffffff1520db0d81be4c631878494668d258369f30b8f2b7a71e257764e9a27f24b48701000000076a515100535300b0a989e1164db9499845bac01d07a3a7d6d2c2a76e4c04abe68f808b6e2ef5068ce6540e0100000009ac53636a63ab65656affffffff0309aac6050000000005ab6563656a6067e8020000000003ac536aec91c8030000000009655251ab65ac6a53acc7a45bc5", "63526a65abac", 1, 512079270, "fb7eca81d816354b6aedec8cafc721d5b107336657acafd0d246049556f9e04b"], + ["ca66ae10049533c2b39f1449791bd6d3f039efe0a121ab7339d39ef05d6dcb200ec3fb2b3b020000000465006a53ffffffff534b8f97f15cc7fb4f4cea9bf798472dc93135cd5b809e4ca7fe4617a61895980100000000ddd83c1dc96f640929dd5e6f1151dab1aa669128591f153310d3993e562cc7725b6ae3d903000000046a52536582f8ccddb8086d8550f09128029e1782c3f2624419abdeaf74ecb24889cc45ac1a64492a0100000002516a4867b41502ee6ccf03000000000752acacab52ab6a4b7ba80000000000075151ab0052536300000000", "6553", 2, -62969257, "8085e904164ab9a8c20f58f0d387f6adb3df85532e11662c03b53c3df8c943cb"], + ["ba646d0b0453999f0c70cb0430d4cab0e2120457bb9128ed002b6e9500e9c7f8d7baa20abe0200000001652a4e42935b21db02b56bf6f08ef4be5adb13c38bc6a0c3187ed7f6197607ba6a2c47bc8a03000000040052516affffffffa55c3cbfc19b1667594ac8681ba5d159514b623d08ed4697f56ce8fcd9ca5b0b00000000096a6a5263ac655263ab66728c2720fdeabdfdf8d9fb2bfe88b295d3b87590e26a1e456bad5991964165f888c03a0200000006630051ac00acffffffff0176fafe0100000000070063acac65515200000000", "63", 1, 2002322280, "9db4e320208185ee70edb4764ee195deca00ba46412d5527d9700c1cf1c3d057"], + ["2ddb8f84039f983b45f64a7a79b74ff939e3b598b38f436def7edd57282d0803c7ef34968d02000000026a537eb00c4187de96e6e397c05f11915270bcc383959877868ba93bac417d9f6ed9f627a7930300000004516551abffffffffacc12f1bb67be3ae9f1d43e55fda8b885340a0df1175392a8bbd9f959ad3605003000000025163ffffffff02ff0f4700000000000070bd99040000000003ac53abf8440b42", "", 2, -393923011, "0133f1a161363b71dfb3a90065c7128c56bd0028b558b610142df79e055ab5c7"], + ["b21fc15403b4bdaa994204444b59323a7b8714dd471bd7f975a4e4b7b48787e720cbd1f5f00000000000ffffffff311533001cb85c98c1d58de0a5fbf27684a69af850d52e22197b0dc941bc6ca9030000000765ab6363ab5351a8ae2c2c7141ece9a4ff75c43b7ea9d94ec79b7e28f63e015ac584d984a526a73fe1e04e0100000007526352536a5365ffffffff02a0a9ea030000000002ab52cfc4f300000000000465525253e8e0f342", "000000", 1, 1305253970, "d1df1f4bba2484cff8a816012bb6ec91c693e8ca69fe85255e0031711081c46a"], + ["d1704d6601acf710b19fa753e307cfcee2735eada0d982b5df768573df690f460281aad12d0000000007656300005100acffffffff0232205505000000000351ab632ca1bc0300000000016300000000", "ac65ab65ab51", 0, 165179664, "40b4f03c68288bdc996011b0f0ddb4b48dc3be6762db7388bdc826113266cd6c"], + ["d2f6c096025cc909952c2400bd83ac3d532bfa8a1f8f3e73c69b1fd7b8913379793f3ce92202000000076a00ab6a53516ade5332d81d58b22ed47b2a249ab3a2cb3a6ce9a6b5a6810e18e3e1283c1a1b3bd73e3ab00300000002acabffffffff01a9b2d40500000000056352abab00dc4b7f69", "ab0065", 0, -78019184, "2ef025e907f0fa454a2b48a4f3b81346ba2b252769b5c35d742d0c8985e0bf5e"], + ["3e6db1a1019444dba461247224ad5933c997256d15c5d37ade3d700506a0ba0a57824930d7010000000852ab6500ab00ac00ffffffff03389242020000000001aba8465a0200000000086a6a636a5100ab52394e6003000000000953ac51526351000053d21d9800", "abababacab53ab65", 0, 1643661850, "1f8a3aca573a609f4aea0c69522a82fcb4e15835449da24a05886ddc601f4f6a"], + ["f821a042036ad43634d29913b77c0fc87b4af593ac86e9a816a9d83fd18dfcfc84e1e1d57102000000076a63ac52006351ffffffffbcdaf490fc75086109e2f832c8985716b3a624a422cf9412fe6227c10585d21203000000095252abab5352ac526affffffff2efed01a4b73ad46c7f7bc7fa3bc480f8e32d741252f389eaca889a2e9d2007e000000000353ac53ffffffff032ac8b3020000000009636300000063516300d3d9f2040000000006510065ac656aafa5de0000000000066352ab5300ac9042b57d", "525365", 1, 667065611, "0d17a92c8d5041ba09b506ddf9fd48993be389d000aad54f9cc2a44fcc70426b"], + ["58e3f0f704a186ef55d3919061459910df5406a9121f375e7502f3be872a449c3f2bb058380100000000f0e858da3ac57b6c973f889ad879ffb2bd645e91b774006dfa366c74e2794aafc8bbc871010000000751ac65516a515131a68f120fd88ca08687ceb4800e1e3fbfea7533d34c84fef70cc5a96b648d580369526d000000000600ac00515363f6191d5b3e460fa541a30a6e83345dedfa3ed31ad8574d46d7bbecd3c9074e6ba5287c24020000000151e3e19d6604162602010000000004005100ac71e17101000000000065b5e90300000000040053ab53f6b7d101000000000200ac00000000", "6563ab", 1, -669018604, "8221d5dfb75fc301a80e919e158e0b1d1e86ffb08870a326c89408d9bc17346b"], + ["efec1cce044a676c1a3d973f810edb5a9706eb4cf888a240f2b5fb08636bd2db482327cf500000000005ab51656a52ffffffff46ef019d7c03d9456e5134eb0a7b5408d274bd8e33e83df44fab94101f7c5b650200000009ac5100006353630051407aadf6f5aaffbd318fdbbc9cae4bd883e67d524df06bb006ce2f7c7e2725744afb76960100000005536aab53acec0d64eae09e2fa1a7c4960354230d51146cf6dc45ee8a51f489e20508a785cbe6ca86fc000000000651536a516300ffffffff014ef598020000000006636aac655265a6ae1b75", "53516a5363526563ab", 2, -1823982010, "13e8b5ab4e5b2ceeff0045c625e19898bda2d39fd7af682e2d1521303cfe1154"], + ["3c436c2501442a5b700cbc0622ee5143b34b1b8021ea7bbc29e4154ab1f5bdfb3dff9d640501000000086aab5251ac5252acffffffff0170b9a20300000000066aab6351525114b13791", "63acabab52ab51ac65", 0, -2140612788, "87ddf1f9acb6640448e955bd1968f738b4b3e073983af7b83394ab7557f5cd61"], + ["d62f183e037e0d52dcf73f9b31f70554bce4f693d36d17552d0e217041e01f15ad3840c838000000000963acac6a6a6a63ab63ffffffffabdfb395b6b4e63e02a763830f536fc09a35ff8a0cf604021c3c751fe4c88f4d0300000006ab63ab65ac53aa4d30de95a2327bccf9039fb1ad976f84e0b4a0936d82e67eafebc108993f1e57d8ae39000000000165ffffffff04364ad30500000000036a005179fd84010000000007ab636aac6363519b9023030000000008510065006563ac6acd2a4a02000000000000000000", "52", 1, 595020383, "da8405db28726dc4e0f82b61b2bfd82b1baa436b4e59300305cc3b090b157504"], + ["44c200a5021238de8de7d80e7cce905606001524e21c8d8627e279335554ca886454d692e6000000000500acac52abbb8d1dc876abb1f514e96b21c6e83f429c66accd961860dc3aed5071e153e556e6cf076d02000000056553526a51870a928d0360a580040000000004516a535290e1e302000000000851ab6a00510065acdd7fc5040000000007515363ab65636abb1ec182", "6363", 0, -785766894, "ed53cc766cf7cb8071cec9752460763b504b2183442328c5a9761eb005c69501"], + ["d682d52d034e9b062544e5f8c60f860c18f029df8b47716cabb6c1b4a4b310a0705e754556020000000400656a0016eeb88eef6924fed207fba7ddd321ff3d84f09902ff958c815a2bf2bb692eb52032c4d803000000076365ac516a520099788831f8c8eb2552389839cfb81a9dc55ecd25367acad4e03cfbb06530f8cccf82802701000000085253655300656a53ffffffff02d543200500000000056a510052ac03978b05000000000700ac51525363acfdc4f784", "", 2, -696035135, "e1a256854099907050cfee7778f2018082e735a1f1a3d91437584850a74c87bb"], + ["e8c0dec5026575ddf31343c20aeeca8770afb33d4e562aa8ee52eeda6b88806fdfd4fe0a97030000000953acabab65ab516552ffffffffdde122c2c3e9708874286465f8105f43019e837746686f442666629088a970e0010000000153ffffffff01f98eee0100000000025251fe87379a", "63", 1, 633826334, "abe441209165d25bc6d8368f2e7e7dc21019056719fef1ace45542aa2ef282e2"], + ["b288c331011c17569293c1e6448e33a64205fc9dc6e35bc756a1ac8b97d18e912ea88dc0770200000007635300ac6aacabfc3c890903a3ccf8040000000004656500ac9c65c9040000000009ab6a6aabab65abac63ac5f7702000000000365005200000000", "526a63", 0, 1574937329, "0dd1bd5c25533bf5f268aa316ce40f97452cca2061f0b126a59094ca5b65f7a0"], + ["fc0a092003cb275fa9a25a72cf85d69c19e4590bfde36c2b91cd2c9c56385f51cc545530210000000004ab530063ffffffff729b006eb6d14d6e5e32b1c376acf1c62830a5d9246da38dbdb4db9f51fd1c74020000000463636500ffffffff0ae695c6d12ab7dcb8d3d4b547b03f178c7268765d1de9af8523d244e3836b12030000000151ffffffff0115c1e20100000000066a6aabac6a6a1ff59aec", "ab0053ac", 0, 931831026, "73fe22099c826c34a74edf45591f5d7b3a888c8178cd08facdfd96a9a681261c"], + ["0fcae7e004a71a4a7c8f66e9450c0c1785268679f5f1a2ee0fb3e72413d70a9049ecff75de020000000452005251ffffffff99c8363c4b95e7ec13b8c017d7bb6e80f7c04b1187d6072961e1c2479b1dc0320200000000ffffffff7cf03b3d66ab53ed740a70c5c392b84f780fff5472aee82971ac3bfeeb09b2df0200000006ab5265636a0058e4fe9257d7c7c7e82ff187757c6eadc14cceb6664dba2de03a018095fd3006682a5b9600000000056353536a636de26b2303ff76de010000000001acdc0a2e020000000001ab0a53ed020000000007530063ab51510088417307", "ac6aacab5165535253", 2, -902160694, "eea96a48ee572aea33d75d0587ce954fcfb425531a7da39df26ef9a6635201be"], + ["612701500414271138e30a46b7a5d95c70c78cc45bf8e40491dac23a6a1b65a51af04e6b94020000000451655153ffffffffeb72dc0e49b2fad3075c19e1e6e4b387f1365dca43d510f6a02136318ddecb7f0200000003536352e115ffc4f9bae25ef5baf534a890d18106fb07055c4d7ec9553ba89ed1ac2101724e507303000000080063006563acabac2ff07f69a080cf61a9d19f868239e6a4817c0eeb6a4f33fe254045d8af2bca289a8695de0300000000430736c404d317840500000000086a00abac5351ab65306e0503000000000963ab0051536aabab6a6c8aca01000000000565516351ab5dcf960100000000016a00000000", "ab", 2, -604581431, "5ec805e74ee934aa815ca5f763425785ae390282d46b5f6ea076b6ad6255a842"], + ["6b68ba00023bb4f446365ea04d68d48539aae66f5b04e31e6b38b594d2723ab82d44512460000000000200acffffffff5dfc6febb484fff69c9eeb7c7eb972e91b6d949295571b8235b1da8955f3137b020000000851ac6352516a535325828c8a03365da801000000000800636aabac6551ab0f594d03000000000963ac536365ac63636a45329e010000000005abac53526a00000000", "005151", 0, 1317038910, "42f5ba6f5fe1e00e652a08c46715871dc4b40d89d9799fd7c0ea758f86eab6a7"], + ["aff5850c0168a67296cc790c1b04a9ed9ad1ba0469263a9432fcb53676d1bb4e0eea8ea1410100000005ac65526a537d5fcb1d01d9c26d0200000000065265ab5153acc0617ca1", "51ab650063", 0, 1712981774, "8449d5247071325e5f8edcc93cb9666c0fecabb130ce0e5bef050575488477eb"], + ["e6d6b9d8042c27aec99af8c12b6c1f7a80453e2252c02515e1f391da185df0874e133696b50300000006ac5165650065ffffffff6a4b60a5bfe7af72b198eaa3cde2e02aa5fa36bdf5f24ebce79f6ecb51f3b554000000000652656aababac2ec4c5a6cebf86866b1fcc4c5bd5f4b19785a8eea2cdfe58851febf87feacf6f355324a80100000001537100145149ac1e287cef62f6f5343579189fad849dd33f25c25bfca841cb696f10c5a34503000000046a636a63df9d7c4c018d96e20100000000015100000000", "53ab", 1, -1924777542, "f98f95d0c5ec3ac3e699d81f6c440d2e7843eab15393eb023bc5a62835d6dcea"], + ["046ac25e030a344116489cc48025659a363da60bc36b3a8784df137a93b9afeab91a04c1ed020000000951ab0000526a65ac51ffffffff6c094a03869fde55b9a8c4942a9906683f0a96e2d3e5a03c73614ea3223b2c29020000000500ab636a6affffffff3da7aa5ecef9071600866267674b54af1740c5aeb88a290c459caa257a2683cb0000000004ab6565ab7e2a1b900301b916030000000005abac63656308f4ed03000000000852ab53ac63ac51ac73d620020000000003ab00008deb1285", "6a", 2, 1299505108, "f79e6b776e2592bad45ca328c54abf14050c241d8f822d982c36ea890fd45757"], + ["bd515acd0130b0ac47c2d87f8d65953ec7d657af8d96af584fc13323d0c182a2e5f9a96573000000000652ac51acac65ffffffff0467aade000000000003655363dc577d050000000006515252ab5300137f60030000000007535163530065004cdc860500000000036a5265241bf53e", "acab", 0, 621090621, "771d4d87f1591a13d77e51858c16d78f1956712fe09a46ff1abcabbc1e7af711"], + ["ff1ae37103397245ac0fa1c115b079fa20930757f5b6623db3579cb7663313c2dc4a3ffdb300000000076353656a000053ffffffff83c59e38e5ad91216ee1a312d15b4267bae2dd2e57d1a3fd5c2f0f809eeb5d46010000000800abab6a6a53ab51ffffffff9d5e706c032c1e0ca75915f8c6686f64ec995ebcd2539508b7dd8abc3e4d7d2a01000000006b2bdcda02a8fe070500000000045253000019e31d04000000000700ab63acab526a00000000", "53656aab6a525251", 0, 881938872, "726bb88cdf3af2f7603a31f33d2612562306d08972a4412a55dbbc0e3363721c"], + ["ff5400dd02fec5beb9a396e1cbedc82bedae09ed44bae60ba9bef2ff375a6858212478844b03000000025253ffffffff01e46c203577a79d1172db715e9cc6316b9cfc59b5e5e4d9199fef201c6f9f0f000000000900ab6552656a5165acffffffff02e8ce62040000000002515312ce3e00000000000251513f119316", "", 0, 1541581667, "1e0da47eedbbb381b0e0debbb76e128d042e02e65b11125e17fd127305fc65cd"], + ["28e3daa603c03626ad91ffd0ff927a126e28d29db5012588b829a06a652ea4a8a5732407030200000004ab6552acffffffff8e643146d3d0568fc2ad854fd7864d43f6f16b84e395db82b739f6f5c84d97b40000000004515165526b01c2dc1469db0198bd884e95d8f29056c48d7e74ff9fd37a9dec53e44b8769a6c99c030200000009ab006a516a53630065eea8738901002398000000000007ac5363516a51abeaef12f5", "52ab52515253ab", 2, 1687390463, "55591346aec652980885a558cc5fc2e3f8d21cbd09f314a798e5a7ead5113ea6"], + ["b54bf5ac043b62e97817abb892892269231b9b220ba08bc8dbc570937cd1ea7cdc13d9676c010000000451ab5365a10adb7b35189e1e8c00b86250f769319668189b7993d6bdac012800f1749150415b2deb0200000003655300ffffffff60b9f4fb9a7e17069fd00416d421f804e2ef2f2c67de4ca04e0241b9f9c1cc5d0200000003ab6aacfffffffff048168461cce1d40601b42fbc5c4f904ace0d35654b7cc1937ccf53fe78505a0100000008526563525265abacffffffff01dbf4e6040000000007acac656553636500000000", "63", 2, 882302077, "f5b38b0f06e246e47ce622e5ee27d5512c509f8ac0e39651b3389815eff2ab93"], + ["ebf628b30360bab3fa4f47ce9e0dcbe9ceaf6675350e638baff0c2c197b2419f8e4fb17e16000000000452516365ac4d909a79be207c6e5fb44fbe348acc42fc7fe7ef1d0baa0e4771a3c4a6efdd7e2c118b0100000003acacacffffffffa6166e9101f03975721a3067f1636cc390d72617be72e5c3c4f73057004ee0ee010000000863636a6a516a5252c1b1e82102d8d54500000000000153324c900400000000015308384913", "0063516a51", 1, -1658428367, "eb2d8dea38e9175d4d33df41f4087c6fea038a71572e3bad1ea166353bf22184"], + ["d6a8500303f1507b1221a91adb6462fb62d741b3052e5e7684ea7cd061a5fc0b0e93549fa50100000004acab65acfffffffffdec79bf7e139c428c7cfd4b35435ae94336367c7b5e1f8e9826fcb0ebaaaea30300000000ffffffffd115fdc00713d52c35ea92805414bd57d1e59d0e6d3b79a77ee18a3228278ada020000000453005151ffffffff040231510300000000085100ac6a6a000063c6041c0400000000080000536a6563acac138a0b04000000000263abd25fbe03000000000900656a00656aac510000000000", "ac526aac6a00", 1, -2007972591, "13d12a51598b34851e7066cd93ab8c5212d60c6ed2dae09d91672c10ccd7f87c"], + ["658cb1c1049564e728291a56fa79987a4ed3146775fce078bd2e875d1a5ca83baf6166a82302000000056a656351ab2170e7d0826cbdb45fda0457ca7689745fd70541e2137bb4f52e7b432dcfe2112807bd720300000007006a0052536351ffffffff8715ca2977696abf86d433d5c920ef26974f50e9f4a20c584fecbb68e530af5101000000009e49d864155bf1d3c757186d29f3388fd89c7f55cc4d9158b4cf74ca27a35a1dd93f945502000000096a535353ac656351510d29fa870230b809040000000006ab6a6a526a633b41da050000000004ab6a6a65ed63bf62", "52acabac", 2, -1774073281, "53ab197fa7e27b8a3f99ff48305e67081eb90e95d89d7e92d80cee25a03a6689"], + ["e92492cc01aec4e62df67ea3bc645e2e3f603645b3c5b353e4ae967b562d23d6e043badecd0100000003acab65ffffffff02c7e5ea040000000002ab52e1e584010000000005536365515195d16047", "6551", 0, -424930556, "93c34627f526d73f4bea044392d1a99776b4409f7d3d835f23b03c358f5a61c2"], + ["02e242db04be2d8ced9179957e98cee395d4767966f71448dd084426844cbc6d15f2182e85030000000200650c8ffce3db9de9c3f9cdb9104c7cb26647a7531ad1ebf7591c259a9c9985503be50f8de30000000007ac6a51636a6353ffffffffa2e33e7ff06fd6469987ddf8a626853dbf30c01719efb259ae768f051f803cd30300000000fffffffffd69d8aead941683ca0b1ee235d09eade960e0b1df3cd99f850afc0af1b73e070300000001ab60bb602a011659670100000000076363526300acac00000000", "6353ab515251", 3, 1451100552, "bbc9069b8615f3a52ac8a77359098dcc6c1ba88c8372d5d5fe080b99eb781e55"], + ["b28d5f5e015a7f24d5f9e7b04a83cd07277d452e898f78b50aae45393dfb87f94a26ef57720200000008ababac630053ac52ffffffff046475ed040000000008ab5100526363ac65c9834a04000000000251abae26b30100000000040000ac65ceefb900000000000000000000", "ac6551ac6a536553", 0, -1756558188, "5848d93491044d7f21884eef7a244fe7d38886f8ae60df49ce0dfb2a342cd51a"], + ["efb8b09801f647553b91922a5874f8e4bb2ed8ddb3536ed2d2ed0698fac5e0e3a298012391030000000952ac005263ac52006affffffff04cdfa0f050000000007ac53ab51abac65b68d1b02000000000553ab65ac00d057d50000000000016a9e1fda010000000007ac63ac536552ac00000000", "6aac", 0, 1947322973, "603a9b61cd30fcea43ef0a5c18b88ca372690b971b379ee9e01909c336280511"], + ["68a59fb901c21946797e7d07a4a3ea86978ce43df0479860d7116ac514ba955460bae78fff0000000001abffffffff03979be80100000000036553639300bc040000000008006552006a656565cfa78d0000000000076552acab63ab5100000000", "ab65ab", 0, 995583673, "3b320dd47f2702452a49a1288bdc74a19a4b849b132b6cad9a1d945d87dfbb23"], + ["67761f2a014a16f3940dcb14a22ba5dc057fcffdcd2cf6150b01d516be00ef55ef7eb07a830100000004636a6a51ffffffff01af67bd050000000008526553526300510000000000", "6a00", 0, 1570943676, "079fa62e9d9d7654da8b74b065da3154f3e63c315f25751b4d896733a1d67807"], + ["e20fe96302496eb436eee98cd5a32e1c49f2a379ceb71ada8a48c5382df7c8cd88bdc47ced03000000016556aa0e180660925a841b457aed0aae47fca2a92fa1d7afeda647abf67198a3902a7c80dd00000000085152ac636a535265bd18335e01803c810100000000046500ac52f371025e", "6363ab", 1, -651254218, "2921a0e5e3ba83c57ba57c25569380c17986bf34c366ec216d4188d5ba8b0b47"], + ["4e1bd9fa011fe7aa14eee8e78f27c9fde5127f99f53d86bc67bdab23ca8901054ee8a8b6eb0300000009ac535153006a6a0063ffffffff044233670500000000000a667205000000000652ab636a51abe5bf35030000000003535351d579e505000000000700630065ab51ac3419ac30", "52abac52", 0, -1807563680, "4aae6648f856994bed252d319932d78db55da50d32b9008216d5366b44bfdf8a"], + ["ec02fbee03120d02fde12574649660c441b40d330439183430c6feb404064d4f507e704f3c0100000000ffffffffe108d99c7a4e5f75cc35c05debb615d52fac6e3240a6964a29c1704d98017fb60200000002ab63fffffffff726ec890038977adfc9dadbeaf5e486d5fcb65dc23acff0dd90b61b8e2773410000000002ac65e9dace55010f881b010000000005ac00ab650000000000", "51ac525152ac6552", 2, -1564046020, "3f988922d8cd11c7adff1a83ce9499019e5ab5f424752d8d361cf1762e04269b"], + ["23dbdcc1039c99bf11938d8e3ccec53b60c6c1d10c8eb6c31197d62c6c4e2af17f52115c3a0300000008636352000063ababffffffff17823880e1df93e63ad98c29bfac12e36efd60254346cac9d3f8ada020afc0620300000003ab63631c26f002ac66e86cd22a25e3ed3cb39d982f47c5118f03253054842daadc88a6c41a2e1500000000096a00ab636a53635163195314de015570fd0100000000096a5263acab5200005300000000", "ababac6a6553", 1, 11586329, "bd36a50e0e0a4ecbf2709e68daef41eddc1c0c9769efaee57910e99c0a1d1343"], + ["33b03bf00222c7ca35c2f8870bbdef2a543b70677e413ce50494ac9b22ea673287b6aa55c50000000005ab00006a52ee4d97b527eb0b427e4514ea4a76c81e68c34900a23838d3e57d0edb5410e62eeb8c92b6000000000553ac6aacac42e59e170326245c000000000009656553536aab516aabb1a10603000000000852ab52ab6a516500cc89c802000000000763ac6a63ac516300000000", "", 0, 557416556, "41bead1b073e1e9fee065dd612a617ca0689e8f9d3fed9d0acfa97398ebb404c"], + ["813eda1103ac8159850b4524ef65e4644e0fc30efe57a5db0c0365a30446d518d9b9aa8fdd0000000003656565c2f1e89448b374b8f12055557927d5b33339c52228f7108228149920e0b77ef0bcd69da60000000006abac00ab63ab82cdb7978d28630c5e1dc630f332c4245581f787936f0b1e84d38d33892141974c75b4750300000004ac53ab65ffffffff0137edfb02000000000000000000", "0063", 1, -1948560575, "71dfcd2eb7f2e6473aed47b16a6d5fcbd0af22813d892e9765023151e07771ec"], + ["9e45d9aa0248c16dbd7f435e8c54ae1ad086de50c7b25795a704f3d8e45e1886386c653fbf01000000025352fb4a1acefdd27747b60d1fb79b96d14fb88770c75e0da941b7803a513e6d4c908c6445c7010000000163ffffffff014069a8010000000001520a794fb3", "51ac005363", 1, -719113284, "0d31a221c69bd322ef7193dd7359ddfefec9e0a1521d4a8740326d46e44a5d6a"], + ["36e42018044652286b19a90e5dd4f8d9f361d0760d080c5c5add1970296ff0f1de630233c8010000000200ac39260c7606017d2246ee14ddb7611586178067e6a4be38e788e33f39a3a95a55a13a6775010000000352ac638bea784f7c2354ed02ea0b93f0240cdfb91796fa77649beee6f7027caa70778b091deee700000000066a65ac656363ffffffff4d9d77ab676d711267ef65363f2d192e1bd55d3cd37f2280a34c72e8b4c559d700000000056a006aab00001764e1020d30220100000000085252516aacab0053472097040000000009635353ab6a636a5100a56407a1", "006a536551ab53ab", 0, 827296034, "daec2af5622bbe220c762da77bab14dc75e7d28aa1ade9b7f100798f7f0fd97a"], + ["5e06159a02762b5f3a5edcdfc91fd88c3bff08b202e69eb5ba74743e9f4291c4059ab008200000000001ac348f5446bb069ef977f89dbe925795d59fb5d98562679bafd61f5f5f3150c3559582992d0000000008ab5165515353abac762fc67703847ec6010000000000e200cf040000000002abaca64b86010000000008520000515363acabb82b491b", "ab53525352ab6a", 0, -61819505, "75a7db0df41485a28bf6a77a37ca15fa8eccc95b5d6014a731fd8adb9ada0f12"], + ["a1948872013b543d6d902ccdeead231c585195214ccf5d39f136023855958436a43266911501000000086aac006a6a6a51514951c9b2038a538a04000000000452526563c0f345050000000007526a5252ac526af9be8e03000000000752acac51ab006306198db2", "ab6353", 0, -326384076, "ced7ef84aad4097e1eb96310e0d1c8e512cfcb392a01d9010713459b23bc0cf4"], + ["c3efabba03cb656f154d1e159aa4a1a4bf9423a50454ebcef07bc3c42a35fb8ad84014864d0000000000d1cc73d260980775650caa272e9103dc6408bdacaddada6b9c67c88ceba6abaa9caa2f7d020000000553536a5265ffffffff9f946e8176d9b11ff854b76efcca0a4c236d29b69fb645ba29d406480427438e01000000066a0065005300ffffffff040419c0010000000003ab6a63cdb5b6010000000009006300ab5352656a63f9fe5e050000000004acac5352611b980100000000086a00acac00006a512d7f0c40", "0053", 0, -59089911, "c503001c16fbff82a99a18d88fe18720af63656fccd8511bca1c3d0d69bd7fc0"], + ["efb55c2e04b21a0c25e0e29f6586be9ef09f2008389e5257ebf2f5251051cdc6a79fce2dac020000000351006affffffffaba73e5b6e6c62048ba5676d18c33ccbcb59866470bb7911ccafb2238cfd493802000000026563ffffffffe62d7cb8658a6eca8a8babeb0f1f4fa535b62f5fc0ec70eb0111174e72bbec5e0300000009abababac516365526affffffffbf568789e681032d3e3be761642f25e46c20322fa80346c1146cb47ac999cf1b0300000000b3dbd55902528828010000000001ab0aac7b0100000000015300000000", "acac52", 3, 1638140535, "e84444d91580da41c8a7dcf6d32229bb106f1be0c811b2292967ead5a96ce9d4"], + ["91d3b21903629209b877b3e1aef09cd59aca6a5a0db9b83e6b3472aceec3bc2109e64ab85a0200000003530065ffffffffca5f92de2f1b7d8478b8261eaf32e5656b9eabbc58dcb2345912e9079a33c4cd010000000700ab65ab00536ad530611da41bbd51a389788c46678a265fe85737b8d317a83a8ff7a839debd18892ae5c80300000007ab6aac65ab51008b86c501038b8a9a05000000000263525b3f7a040000000007ab535353ab00abd4e3ff04000000000665ac51ab65630b7b656f", "6551525151516a00", 2, 499657927, "ef4bd7622eb7b2bbbbdc48663c1bc90e01d5bde90ff4cb946596f781eb420a0c"], + ["5d5c41ad0317aa7e40a513f5141ad5fc6e17d3916eebee4ddb400ddab596175b41a111ead20100000005536a5265acffffffff900ecb5e355c5c9f278c2c6ea15ac1558b041738e4bffe5ae06a9346d66d5b2b00000000080000ab636a65ab6affffffff99f4e08305fa5bd8e38fb9ca18b73f7a33c61ff7b3c68e696b30a04fea87f3ca000000000163d3d1760d019fc13a00000000000000000000", "ab53acabab6aac6a52", 2, 1007461922, "4012f5ff2f1238a0eb84854074670b4703238ebc15bfcdcd47ffa8498105fcd9"], + ["ceecfa6c02b7e3345445b82226b15b7a097563fa7d15f3b0c979232b138124b62c0be007890200000009abac51536a63525253ffffffffbae481ccb4f15d94db5ec0d8854c24c1cc8642bd0c6300ede98a91ca13a4539a0200000001ac50b0813d023110f5020000000006acabac526563e2b0d0040000000009656aac0063516a536300000000", "0063526500", 0, -1862053821, "e1600e6df8a6160a79ac32aa40bb4644daa88b5f76c0d7d13bf003327223f70c"], + ["ae62d5fd0380c4083a26642159f51af24bf55dc69008e6b7769442b6a69a603edd980a33000000000005ab5100ab53ffffffff49d048324d899d4b8ed5e739d604f5806a1104fede4cb9f92cc825a7fa7b4bfe0200000005536a000053ffffffff42e5cea5673c650881d0b4005fa4550fd86de5f21509c4564a379a0b7252ac0e0000000007530000526a53525f26a68a03bfacc3010000000000e2496f000000000009ab5253acac52636563b11cc600000000000700510065526a6a00000000", "abab", 1, -1600104856, "05cf0ec9c61f1a15f651a0b3c5c221aa543553ce6c804593f43bb5c50bb91ffb"], + ["f06f64af04fdcb830464b5efdb3d5ee25869b0744005375481d7b9d7136a0eb8828ad1f0240200000003516563fffffffffd3ba192dabe9c4eb634a1e3079fca4f072ee5ceb4b57deb6ade5527053a92c5000000000165ffffffff39f43401a36ba13a5c6dd7f1190e793933ae32ee3bf3e7bfb967be51e681af760300000009650000536552636a528e34f50b21183952cad945a83d4d56294b55258183e1627d6e8fb3beb8457ec36cadb0630000000005abab530052334a7128014bbfd10100000000085352ab006a63656afc424a7c", "53650051635253ac00", 2, 313255000, "d309da5afd91b7afa257cfd62df3ca9df036b6a9f4b38f5697d1daa1f587312b"], + ["6dfd2f98046b08e7e2ef5fff153e00545faf7076699012993c7a30cb1a50ec528281a9022f030000000152ffffffff1f535e4851920b968e6c437d84d6ecf586984ebddb7d5db6ae035bd02ba222a8010000000651006a53ab51605072acb3e17939fa0737bc3ee43bc393b4acd58451fc4ffeeedc06df9fc649828822d5010000000253525a4955221715f27788d302382112cf60719be9ae159c51f394519bd5f7e70a4f9816c7020200000009526a6a51636aab656a36d3a5ff0445548e0100000000086a6a00516a52655167030b050000000004ac6a63525cfda8030000000000e158200000000000010000000000", "535263ac6a65515153", 3, 585774166, "72b7da10704c3ca7d1deb60c31b718ee12c70dc9dfb9ae3461edce50789fe2ba"], + ["187eafed01389a45e75e9dda526d3acbbd41e6414936b3356473d1f9793d161603efdb45670100000002ab00ffffffff04371c8202000000000563630063523b3bde02000000000753516563006300e9e765010000000005516aac656a373f9805000000000665525352acab08d46763", "ab", 0, 122457992, "393aa6c758e0eed15fa4af6d9e2d7c63f49057246dbb92b4268ec24fc87301ca"], + ["7d50b977035d50411d814d296da9f7965ddc56f3250961ca5ba805cadd0454e7c521e31b0300000000003d0416c2cf115a397bacf615339f0e54f6c35ffec95aa009284d38390bdde1595cc7aa7c0100000005ab52ac5365ffffffff4232c6e796544d5ac848c9dc8d25cfa74e32e847a5fc74c74d8f38ca51188562030000000653ac51006a51ffffffff016bd8bb00000000000465ab5253163526f3", "51ab526a00005353", 1, -1311316785, "60b7544319b42e4159976c35c32c2644f0adf42eff13be1dc2f726fc0b6bb492"], + ["2a45cd1001bf642a2315d4a427eddcc1e2b0209b1c6abd2db81a800c5f1af32812de42032702000000050051525200ffffffff032177db050000000005530051abac49186f000000000004ab6aab00645c0000000000000765655263acabac00000000", "6a65", 0, -1774715722, "6a9ac3f7da4c7735fbc91f728b52ecbd602233208f96ac5592656074a5db118a"], + ["479358c202427f3c8d19e2ea3def6d6d3ef2281b4a93cd76214f0c7d8f040aa042fe19f71f0300000001abffffffffa2709be556cf6ecaa5ef530df9e4d056d0ed57ce96de55a5b1f369fa40d4e74a020000000700006a51635365c426be3f02af578505000000000363ab63fd8f590500000000065153abac53632dfb14b3", "520063ab51", 1, -763226778, "cfe147982afacde044ce66008cbc5b1e9f0fd9b8ed52b59fc7c0fecf95a39b0e"], + ["76179a8e03bec40747ad65ab0f8a21bc0d125b5c3c17ad5565556d5cb03ade7c83b4f32d98030000000151ffffffff99b900504e0c02b97a65e24f3ad8435dfa54e3c368f4e654803b756d011d24150200000003ac5353617a04ac61bb6cf697cfa4726657ba35ed0031432da8c0ffb252a190278830f9bd54f0320100000006656551005153c8e8fc8803677c77020000000007ac6553535253ac70f442030000000001535be0f20200000000026300bf46cb3a", "6aab52", 1, -58495673, "35e94b3776a6729d20aa2f3ddeeb06d3aad1c14cc4cde52fd21a4efc212ea16c"], + ["75ae53c2042f7546223ce5d5f9e00a968ddc68d52e8932ef2013fa40ce4e8c6ed0b6195cde01000000056563ac630079da0452c20697382e3dba6f4fc300da5f52e95a9dca379bb792907db872ba751b8024ee0300000009655151536500005163ffffffffe091b6d43f51ff00eff0ccfbc99b72d3aff208e0f44b44dfa5e1c7322cfc0c5f01000000075200005363ab63ffffffff7e96c3b83443260ac5cfd18258574fbc4225c630d3950df812bf51dceaeb0f9103000000065365655165639a6bf70b01b3e14305000000000563530063ac00000000", "6300ab00ac", 2, 982422189, "ee4ea49d2aae0dbba05f0b9785172da54408eb1ec67d36759ff7ed25bfc28766"], + ["1cdfa01e01e1b8078e9c2b0ca5082249bd18fdb8b629ead659adedf9a0dd5a04031871ba120200000008525351536565ab6affffffff011e28430200000000076a5363636aac52b2febd4a", "abacac63656300", 0, 387396350, "299dcaac2bdaa627eba0dfd74767ee6c6f27c9200b49da8ff6270b1041669e7e"], + ["cc28c1810113dfa6f0fcd9c7d9c9a30fb6f1d774356abeb527a8651f24f4e6b25cf763c4e00300000003ab636affffffff02dfc6050000000000080053636351ab0052afd56903000000000453ab5265f6c90d99", "006551abacacac", 0, 1299280838, "a4c0773204ab418a939e23f493bd4b3e817375d133d307609e9782f2cc38dbcf"], + ["ca816e7802cd43d66b9374cd9bf99a8da09402d69c688d8dcc5283ace8f147e1672b757e020200000005516aabab5240fb06c95c922342279fcd88ba6cd915933e320d7becac03192e0941e0345b79223e89570300000004005151ac353ecb5d0264dfbd010000000005ac6aacababd5d70001000000000752ac53ac6a5151ec257f71", "63ac", 1, 774695685, "cc180c4f797c16a639962e7aec58ec4b209853d842010e4d090895b22e7a7863"], + ["b42b955303942fedd7dc77bbd9040aa0de858afa100f399d63c7f167b7986d6c2377f66a7403000000066aac00525100ffffffff0577d04b64880425a3174055f94191031ad6b4ca6f34f6da9be7c3411d8b51fc000000000300526a6391e1cf0f22e45ef1c44298523b516b3e1249df153590f592fcb5c5fc432dc66f3b57cb03000000046a6aac65ffffffff0393a6c9000000000004516a65aca674ac0400000000046a525352c82c370000000000030053538e577f89", "", 1, -1237094944, "566953eb806d40a9fb684d46c1bf8c69dea86273424d562bd407b9461c8509af"], + ["92c9fe210201e781b72554a0ed5e22507fb02434ddbaa69aff6e74ea8bad656071f1923f3f02000000056a63ac6a514470cef985ba83dcb8eee2044807bedbf0d983ae21286421506ae276142359c8c6a34d68020000000863ac63525265006aa796dd0102ca3f9d05000000000800abab52ab535353cd5c83010000000007ac00525252005322ac75ee", "5165", 0, 97879971, "6e6307cef4f3a9b386f751a6f40acebab12a0e7e17171d2989293cbec7fd45c2"], + ["ccca1d5b01e40fe2c6b3ee24c660252134601dab785b8f55bd6201ffaf2fddc7b3e2192325030000000365535100496d4703b4b66603000000000665535253ac633013240000000000015212d2a502000000000951abac636353636a5337b82426", "0052", 0, -1691630172, "577bf2b3520b40aef44899a20d37833f1cded6b167e4d648fc5abe203e43b649"], + ["bc1a7a3c01691e2d0c4266136f12e391422f93655c71831d90935fbda7e840e50770c61da20000000008635253abac516353ffffffff031f32aa020000000003636563786dbc0200000000003e950f00000000000563516a655184b8a1de", "51536a", 0, -1627072905, "730bc25699b46703d7718fd5f5c34c4b5f00f594a9968ddc247fa7d5175124ed"], + ["076d209e02d904a6c40713c7225d23e7c25d4133c3c3477828f98c7d6dbd68744023dbb66b030000000753ab00536565acffffffff10975f1b8db8861ca94c8cc7c7cff086ddcd83e10b5fffd4fc8f2bdb03f9463c0100000000ffffffff029dff76010000000006526365530051a3be6004000000000000000000", "515253ac65acacac", 1, -1207502445, "66c488603b2bc53f0d22994a1f0f66fb2958203102eba30fe1d37b27a55de7a5"], + ["690fd1f80476db1f9eebe91317f2f130a60cbc1f4feadd9d6474d438e9cb7f91e4994600af0300000004ab536a63a15ce9fa6622d0c4171d895b42bff884dc6e8a7452f827fdc68a29c3c88e6fdee364eaf50000000002ab52ffffffff022dc39d3c0956b24d7f410b1e387859e7a72955f45d6ffb1e884d77888d18fe0300000005ac6a63656afffffffff10b06bce1800f5c49153d24748fdefb0bf514c12863247d1042d56018c3e25c03000000086a63ac6365536a52ffffffff031f162f0500000000060000655265abffbcd40500000000045151ac001a9c8c05000000000652ac53656a6300000000", "ac51ab63acac", 0, -67986012, "051c0df7ac688c2c930808dabde1f50300aea115f2bb3334f4753d5169b51e46"], + ["49ac2af00216c0307a29e83aa5de19770e6b20845de329290bd69cf0e0db7aed61ae41b39002000000035163ac8b2558ef84635bfc59635150e90b61fc753d34acfd10d97531043053e229cd720133cd95000000000463516a51ffffffff02458471040000000008abab636a51ac0065545aa80000000000096a6553516a5263ac6a00000000", "51526300ab5363", 1, 1449668540, "ddfd902bba312a06197810da96a0ddccb595f96670b28ded7dba88d8cd0469b8"], + ["fa4d868b024b010bd5dce46576c2fb489aa60bb797dac3c72a4836f49812c5c564c258414f03000000007a9b3a585e05027bdd89edbadf3c85ac61f8c3a04c773fa746517ae600ff1a9d6b6c02fb0200000004515163abffffffff01b17d020500000000046a65520000000000", "536565ab65635363", 0, -1718953372, "96c2b32f0a00a5925db7ba72d0b5d39922f30ea0f7443b22bc1b734808513c47"], + ["cac6382d0462375e83b67c7a86c922b569a7473bfced67f17afd96c3cd2d896cf113febf9e0300000003006a53ffffffffaa4913b7eae6821487dd3ca43a514e94dcbbf350f8cc4cafff9c1a88720711b800000000096a6a525300acac6353ffffffff184fc4109c34ea27014cc2c1536ef7ed1821951797a7141ddacdd6e429fae6ff01000000055251655200ffffffff9e7b79b4e6836e290d7b489ead931cba65d1030ccc06f20bd4ca46a40195b33c030000000008f6bc8304a09a2704000000000563655353511dbc73050000000000cf34c500000000000091f76e0000000000085200ab00005100abd07208cb", "0063656a", 2, -1488731031, "bf078519fa87b79f40abc38f1831731422722c59f88d86775535f209cb41b9b1"], + ["1711146502c1a0b82eaa7893976fefe0fb758c3f0e560447cef6e1bde11e42de91a125f71c030000000015bd8c04703b4030496c7461482481f290c623be3e76ad23d57a955807c9e851aaaa20270300000000d04abaf20326dcb7030000000001632225350400000000075263ac00520063dddad9020000000000af23d148", "52520053510063", 0, 1852122830, "e33d5ee08c0f3c130a44d7ce29606450271b676f4a80c52ab9ffab00cecf67f8"], + ["8d5b124d0231fbfc640c706ddb1d57bb49a18ba8ca0e1101e32c7e6e65a0d4c7971d93ea360100000008acabac0000abac65ffffffff8fe0fd7696597b845c079c3e7b87d4a44110c445a330d70342a5501955e17dd70100000004ab525363ef22e8a90346629f030000000009516a00ac63acac51657bd57b05000000000200acfd4288050000000009acab5352ab00ab636300000000", "53ac526553ab65", 0, 1253152975, "8b57a7c3170c6c02dd14ae1d392ce3d828197b20e9145c89c1cfd5de050e1562"], + ["38146dc502c7430e92b6708e9e107b61cd38e5e773d9395e5c8ad8986e7e4c03ee1c1e1e760100000000c8962ce2ac1bb3b1285c0b9ba07f4d2e5ce87c738c42ac0548cd8cec1100e6928cd6b0b6010000000763ab636aab52527cccefbd04e5f6f8020000000006006aabacac65ab2c4a00000000000351635209a6f40100000000026aacce57dc040000000008ab5353ab516a516a00000000", "ab", 0, -1205978252, "3cb5b030e7da0b60ccce5b4a7f3793e6ca56f03e3799fe2d6c3cc22d6d841dcb"], + ["22d81c740469695a6a83a9a4824f77ecff8804d020df23713990afce2b72591ed7de98500502000000065352526a6a6affffffff90dc85e118379b1005d7bbc7d2b8b0bab104dad7eaa49ff5bead892f17d8c3ba010000000665656300ab51ffffffff965193879e1d5628b52005d8560a35a2ba57a7f19201a4045b7cbab85133311d0200000003ac005348af21a13f9b4e0ad90ed20bf84e4740c8a9d7129632590349afc03799414b76fd6e826200000000025353ffffffff04a0d40d04000000000060702700000000000652655151516ad31f1502000000000365ac0069a1ac0500000000095100655300ab53525100000000", "51636a52ac", 0, -1644680765, "add7f5da27262f13da6a1e2cc2feafdc809bd66a67fb8ae2a6f5e6be95373b6f"], + ["a27dcbc801e3475174a183586082e0914c314bc9d79d1570f29b54591e5e0dff07fbb45a7f0000000004ac53ab51ffffffff027347f5020000000005535351ab63d0e5c9030000000009ac65ab6a63515200ab7cd632ed", "ac63636553", 0, -686435306, "883a6ea3b2cc53fe8a803c229106366ca14d25ffbab9fef8367340f65b201da6"], + ["b123ed2204410d4e8aaaa8cdb95234ca86dad9ff77fb4ae0fd4c06ebed36794f0215ede0040100000002ac63ffffffff3b58b81b19b90d8f402701389b238c3a84ff9ba9aeea298bbf15b41a6766d27a01000000056a6553ab00151824d401786153b819831fb15926ff1944ea7b03d884935a8bde01ed069d5fd80220310200000000ffffffffa9c9d246f1eb8b7b382a9032b55567e9a93f86c77f4e32c092aa1738f7f756c30100000002ab65ffffffff011a2b48000000000000ed44d1fb", "630051ab63", 2, -1118263883, "b5dab912bcabedff5f63f6dd395fc2cf030d83eb4dd28214baba68a45b4bfff0"], + ["1339051503e196f730955c5a39acd6ed28dec89b4dadc3f7c79b203b344511270e5747fa9900000000045151636affffffff378c6090e08a3895cedf1d25453bbe955a274657172491fd2887ed5c9aceca7b0100000000ffffffffcf7cc3c36ddf9d4749edfa9cefed496d2f86e870deb814bfcd3b5637a5496461030000000451006300ffffffff04dcf3fa010000000008526a63005263acabb41d84040000000004abac5153800eff020000000005656a535365106c5e00000000000000000000", "abac5300", 2, 2013719928, "7fc74de39ce6ca46ca25d760d3cec7bb21fd14f7efe1c443b5aa294f2cb5f546"], + ["0728c606014c1fd6005ccf878196ba71a54e86cc8c53d6db500c3cc0ac369a26fac6fcbc210000000005ab53ac5365ba9668290182d7870100000000066a000053655100000000", "65", 0, 1789961588, "ab6baa6da3b2bc853868d166f8996ad31d63ef981179f9104f49968fd61c8427"], + ["a1134397034bf4067b6c81c581e2b73fb63835a08819ba24e4e92df73074bf773c94577df7000000000465525251ffffffff8b6608feaa3c1f35f49c6330a769716fa01c5c6f6e0cdc2eb10dfc99bbc21e77010000000952656aac005352655180a0bda4bc72002c2ea8262e26e03391536ec36867258cab968a6fd6ec7523b64fa1d8c001000000056a53ac6353ffffffff04dbeeed05000000000553650052abcd5d0e01000000000463abab51104b2e0500000000066aac53ac5165283ca7010000000004535252ab00000000", "ab515151516552ab", 1, -324598676, "91178482112f94d1c8e929de443e4b9c893e18682998d393ca9ca77950412586"], + ["bcdafbae04aa18eb75855aeb1f5124f30044741351b33794254a80070940cb10552fa4fa8e0300000001acd0423fe6e3f3f88ae606f2e8cfab7a5ef87caa2a8f0401765ff9a47d718afcfb40c0099b0000000008ac6565ab53ac6aac645308009d680202d600e492b31ee0ab77c7c5883ebad5065f1ce87e4dfe6453e54023a0010000000151ffffffffb9d818b14245899e1d440152827c95268a676f14c3389fc47f5a11a7b38b1bde03000000026300ffffffff03cda22102000000000751ac535263005100a4d20400000000045200536ac8bef405000000000700ab51ab6563ac00000000", "6553516a526aab", 1, -2111409753, "5e1849e7368cf4f042718586d9bd831d61479b775bab97aba9f450042bd9876a"], + ["ed3bb93802ddbd08cb030ef60a2247f715a0226de390c9c1a81d52e83f8674879065b5f87d0300000003ab6552ffffffff04d2c5e60a21fb6da8de20bf206db43b720e2a24ce26779bca25584c3f765d1e0200000008ab656a6aacab00ab6e946ded025a811d04000000000951abac6352ac00ab5143cfa3030000000005635200636a00000000", "5352ac650065535300", 1, -668727133, "e9995065e1fddef72a796eef5274de62012249660dc9d233a4f24e02a2979c87"], + ["59f4629d030fa5d115c33e8d55a79ea3cba8c209821f979ed0e285299a9c72a73c5bba00150200000002636affffffffd8aca2176df3f7a96d0dc4ee3d24e6cecde1582323eec2ebef9a11f8162f17ac0000000007ab6565acab6553ffffffffeebc10af4f99c7a21cbc1d1074bd9f0ee032482a71800f44f26ee67491208e0403000000065352ac656351ffffffff0434e955040000000004ab515152caf2b305000000000365ac007b1473030000000003ab530033da970500000000060051536a5253bb08ab51", "", 2, 396340944, "0e9c47973ef2c292b2252c623f465bbb92046fe0b893eebf4e1c9e02cb01c397"], + ["286e3eb7043902bae5173ac3b39b44c5950bc363f474386a50b98c7bdab26f98dc83449c4a020000000752ac6a00510051ffffffff4339cd6a07f5a5a2cb5815e5845da70300f5c7833788363bf7fe67595d3225520100000000fffffffff9c2dd8b06ad910365ffdee1a966f124378a2b8021065c8764f6138bb1e951380200000005ab5153ac6affffffff0370202aba7a68df85436ea7c945139513384ef391fa33d16020420b8ad40e9a000000000900ab5165526353abacffffffff020c1907000000000004abac526a1b490b040000000000df1528f7", "5353ab", 3, -1407529517, "32154c09174a9906183abf26538c39e78468344ca0848bbd0785e24a3565d932"], + ["2e245cf80179e2e95cd1b34995c2aff49fe4519cd7cee93ad7587f7f7e8105fc2dff206cd30200000009006a63516a6553ab52350435a201d5ed2d02000000000352ab6558552c89", "00ab53", 0, -233917810, "4605ae5fd3d50f9c45d37db7118a81a9ef6eb475d2333f59df5d3e216f150d49"], + ["33a98004029d262f951881b20a8d746c8c707ea802cd2c8b02a33b7e907c58699f97e42be80100000007ac53536552abacdee04cc01d205fd8a3687fdf265b064d42ab38046d76c736aad8865ca210824b7c622ecf02000000070065006a536a6affffffff01431c5d010000000000270d48ee", "", 1, 921554116, "ff9d7394002f3f196ea25472ea6c46f753bd879a7244795157bb7235c9322902"], + ["aac18f2b02b144ed481557c53f2146ae523f24fcde40f3445ab0193b6b276c315dc2894d2300000000075165650000636a233526947dbffc76aec7db1e1baa6868ad4799c76e14794dcbaaec9e713a83967f6a65170200000005abac6551ab27d518be01b652a30000000000015300000000", "52ac5353", 1, 1559377136, "59fc2959bb7bb24576cc8a237961ed95bbb900679d94da6567734c4390cb6ef5"], + ["5ab79881033555b65fe58c928883f70ce7057426fbdd5c67d7260da0fe8b1b9e6a2674cb850300000009ac516aac6aac006a6affffffffa5be9223b43c2b1a4d120b5c5b6ec0484f637952a3252181d0f8e813e76e11580200000000e4b5ceb8118cb77215bbeedc9a076a4d087bb9cd1473ea32368b71daeeeacc451ec209010000000005acac5153aced7dc34e02bc5d11030000000005ac5363006a54185803000000000552ab00636a00000000", "5100", 1, 1927062711, "e9f53d531c12cce1c50abed4ac521a372b4449b6a12f9327c80020df6bff66c0"], + ["6c2c8fac0124b0b7d4b610c3c5b91dee32b7c927ac71abdf2d008990ca1ac40de0dfd530660300000006ababac5253656bd7eada01d847ec000000000004ac52006af4232ec8", "6a6a6a0051", 0, -340809707, "fb51eb9d7e47d32ff2086205214f90c7c139e08c257a64829ae4d2b301071c6a"], + ["6e3880af031735a0059c0bb5180574a7dcc88e522c8b56746d130f8d45a52184045f96793e0100000008acabac6a526a6553fffffffffe05f14cdef7d12a9169ec0fd37524b5fcd3295f73f48ca35a36e671da4a2f560000000008006a526a6351ab63ffffffffdfbd869ac9e472640a84caf28bdd82e8c6797f42d03b99817a705a24fde2736600000000010090a090a503db956b04000000000952ac53ab6a536a63ab358390010000000009656a5200525153ac65353ee204000000000763530052526aaba6ad83fb", "535151ab6300", 2, 222014018, "57a34ddeb1bf36d28c7294dda0432e9228a9c9e5cc5c692db98b6ed2e218d825"], + ["8df1cd19027db4240718dcaf70cdee33b26ea3dece49ae6917331a028c85c5a1fb7ee3e475020000000865ab6a00510063636157988bc84d8d55a8ba93cdea001b9bf9d0fa65b5db42be6084b5b1e1556f3602f65d4d0100000005ac00ab0052206c852902b2fb54030000000008ac5252536aacac5378c4a5050000000007acabac535163532784439e", "acab6a", 0, 1105620132, "edb7c74223d1f10f9b3b9c1db8064bc487321ff7bb346f287c6bc2fad83682de"], + ["0e803682024f79337b25c98f276d412bc27e56a300aa422c42994004790cee213008ff1b8303000000080051ac65ac655165f421a331892b19a44c9f88413d057fea03c3c4a6c7de4911fe6fe79cf2e9b3b10184b1910200000005525163630096cb1c670398277204000000000253acf7d5d502000000000963536a6a636a5363ab381092020000000002ac6a911ccf32", "6565", 1, -1492094009, "f0672638a0e568a919e9d8a9cbd7c0189a3e132940beeb52f111a89dcc2daa2c"], + ["7d71669d03022f9dd90edac323cde9e56354c6804c6b8e687e9ae699f46805aafb8bcaa636000000000253abffffffff698a5fdd3d7f2b8b000c68333e4dd58fa8045b3e2f689b889beeb3156cecdb490300000009525353abab0051acabc53f0aa821cdd69b473ec6e6cf45cf9b38996e1c8f52c27878a01ec8bb02e8cb31ad24e500000000055353ab0052ffffffff0447a23401000000000565ab53ab5133aaa0030000000006515163656563057d110300000000056a6aacac52cf13b5000000000003526a5100000000", "6a6a51", 1, -1349253507, "722efdd69a7d51d3d77bed0ac5544502da67e475ea5857cd5af6bdf640a69945"], + ["9ff618e60136f8e6bb7eabaaac7d6e2535f5fba95854be6d2726f986eaa9537cb283c701ff02000000026a65ffffffff012d1c0905000000000865ab00ac6a516a652f9ad240", "51515253635351ac", 0, 1571304387, "659cd3203095d4a8672646add7d77831a1926fc5b66128801979939383695a79"], + ["9fbd43ac025e1462ecd10b1a9182a8e0c542f6d1089322a41822ab94361e214ed7e1dfdd8a020000000263519d0437581538e8e0b6aea765beff5b4f3a4a202fca6e5d19b34c141078c6688f71ba5b8e0100000003ac6552ffffffff02077774050000000009655153655263acab6a0ae4e10100000000035152524c97136b", "635152ab", 0, 1969622955, "d82d4ccd9b67810f26a378ad9592eb7a30935cbbd27e859b00981aefd0a72e08"], + ["0117c92004314b84ed228fc11e2999e657f953b6de3b233331b5f0d0cf40d5cc149b93c7b30300000005515263516a083e8af1bd540e54bf5b309d36ba80ed361d77bbf4a1805c7aa73667ad9df4f97e2da410020000000600ab6351ab524d04f2179455e794b2fcb3d214670001c885f0802e4b5e015ed13a917514a7618f5f332203000000086a536aab51000063ecf029e65a4a009a5d67796c9f1eb358b0d4bd2620c8ad7330fb98f5a802ab92d0038b1002000000036a6551a184a88804b04490000000000009ab6a5152535165526a33d1ab020000000001518e92320000000000002913df04000000000952abac6353525353ac8b19bfdf", "000051ab0000", 0, 489433059, "8eebac87e60da524bbccaf285a44043e2c9232868dda6c6271a53c153e7f3a55"], + ["e7f5482903f98f0299e0984b361efb2fddcd9979869102281e705d3001a9d283fe9f3f3a1e02000000025365ffffffffcc5c7fe82feebad32a22715fc30bc584efc9cd9cadd57e5bc4b6a265547e676e0000000001ab579d21235bc2281e08bf5e7f8f64d3afb552839b9aa5c77cf762ba2366fffd7ebb74e49400000000055263ab63633df82cf40100982e05000000000453ac535300000000", "acacab", 2, -1362931214, "046de666545330e50d53083eb78c9336416902f9b96c77cc8d8e543da6dfc7e4"], + ["09adb2e90175ca0e816326ae2dce7750c1b27941b16f6278023dbc294632ab97977852a09d030000000465ab006affffffff027739cf0100000000075151ab63ac65ab8a5bb601000000000653ac5151520011313cdc", "ac", 0, -76831756, "478ee06501b4965b40bdba6cbaad9b779b38555a970912bb791b86b7191c54bc"], + ["f973867602e30f857855cd0364b5bbb894c049f44abbfd661d7ae5dbfeaafca89fac8959c20100000005ab52536a51ffffffffbeceb68a4715f99ba50e131884d8d20f4a179313691150adf0ebf29d05f8770303000000066352ab00ac63ffffffff021fddb90000000000036a656322a177000000000008526500ac5100acac84839083", "52acab53ac", 0, 1407879325, "db0329439490efc64b7104d6d009b03fbc6fac597cf54fd786fbbb5fd73b92b4"], + ["fd22ebaa03bd588ad16795bea7d4aa7f7d48df163d75ea3afebe7017ce2f350f6a0c1cb0bb00000000086aabac5153526363ffffffff488e0bb22e26a565d77ba07178d17d8f85702630ee665ec35d152fa05af3bda10200000004515163abffffffffeb21035849e85ad84b2805e1069a91bb36c425dc9c212d9bae50a95b6bfde1200300000001ab5df262fd02b69848040000000008ab6363636a6363ace23bf2010000000007655263635253534348c1da", "006353526563516a00", 0, -1491036196, "92364ba3c7a85d4e88885b8cb9b520dd81fc29e9d2b750d0790690e9c1246673"], + ["130b462d01dd49fac019dc4442d0fb54eaa6b1c2d1ad0197590b7df26969a67abd7f3fbb4f0100000008ac65abac53ab6563ffffffff0345f825000000000004ac53acac9d5816020000000002ababeff8e90500000000086aab006552ac6a53a892dc55", "ab0065ac530052", 0, 944483412, "1f4209fd4ce7f13d175fdd522474ae9b34776fe11a5f17a27d0796c77a2a7a9d"], + ["f8e50c2604609be2a95f6d0f31553081f4e1a49a0a30777fe51eb1c596c1a9a92c053cf28c0300000009656a51ac5252630052fffffffff792ed0132ae2bd2f11d4a2aab9d0c4fbdf9a66d9ae2dc4108afccdc14d2b1700100000007ab6a6563ac636a7bfb2fa116122b539dd6a2ab089f88f3bc5923e5050c8262c112ff9ce0a3cd51c6e3e84f02000000066551ac5352650d5e687ddf4cc9a497087cabecf74d236aa4fc3081c3f67b6d323cba795e10e7a171b725000000000852635351ab635100ffffffff02df5409020000000008ac6a53acab5151004156990200000000045163655200000000", "ac53abac65005300", 0, -173065000, "b596f206d7eba22b7e2d1b7a4f4cf69c7c541b6c84dcc943f84e19a99a923310"], + ["18020dd1017f149eec65b2ec23300d8df0a7dd64fc8558b36907723c03cd1ba672bbb0f51d0300000005ab65ab6a63ffffffff037cd7ae000000000009ab516a65005352ac65f1e4360400000000056353530053f118f0040000000009536363ab006500abac00000000", "63ab51acab52ac", 0, -550412404, "e19b796c14a0373674968e342f2741d8b51092a5f8409e9bff7dcd52e56fcbcb"], + ["b04154610363fdade55ceb6942d5e5a723323863b48a0cb04fdcf56210717955763f56b08d0300000009ac526a525151635151ffffffff93a176e76151a9eabdd7af00ef2af72f9e7af5ecb0aa4d45d00618f394cdd03c030000000074d818b332ebe05dc24c44d776cf9d275c61f471cc01efce12fd5a16464157f1842c65cb00000000066a0000ac6352d3c4134f01d8a1c0030000000005520000005200000000", "5200656a656351", 2, -9757957, "6e3e5ba77f760b6b5b5557b13043f1262418f3dd2ce7f0298b012811fc8ad5bc"], + ["9794b3ce033df7b1e32db62d2f0906b589eacdacf5743963dc2255b6b9a6cba211fadd0d41020000000600ab00650065ffffffffaae00687a6a4131152bbcaafedfaed461c86754b0bde39e2bef720e6d1860a0302000000070065516aac6552ffffffff50e4ef784d6230df7486e972e8918d919f005025bc2d9aacba130f58bed7056703000000075265ab52656a52ffffffff02c6f1a9000000000006005251006363cf450c040000000008abab63510053abac00000000", "ac0063ababab515353", 1, 2063905082, "fad092fc98f17c2c20e10ba9a8eb44cc2bcc964b006f4da45cb9ceb249c69698"], + ["94533db7015e70e8df715066efa69dbb9c3a42ff733367c18c22ff070392f988f3b93920820000000006535363636300ce4dac3e03169af80300000000080065ac6a53ac65ac39c050020000000006abacab6aacac708a02050000000005ac5251520000000000", "6553", 0, -360458507, "5418cf059b5f15774836edd93571e0eed3855ba67b2b08c99dccab69dc87d3e9"], + ["c8597ada04f59836f06c224a2640b79f3a8a7b41ef3efa2602592ddda38e7597da6c639fee0300000009005251635351acabacffffffff4c518f347ee694884b9d4072c9e916b1a1f0a7fc74a1c90c63fdf8e5a185b6ae02000000007113af55afb41af7518ea6146786c7c726641c68c8829a52925e8d4afd07d8945f68e7230300000008ab00ab65ab650063ffffffffc28e46d7598312c420e11dfaae12add68b4d85adb182ae5b28f8340185394b63000000000165ffffffff04dbabb7010000000000ee2f6000000000000852ab6500ab6a51acb62a27000000000009ac53515300ac006a6345fb7505000000000752516a0051636a00000000", "", 3, 15199787, "0d66003aff5bf78cf492ecbc8fd40c92891acd58d0a271be9062e035897f317e"], + ["1a28c4f702c8efaad96d879b38ec65c5283b5c084b819ad7db1c086e85e32446c7818dc7a90300000008656351536a525165fa78cef86c982f1aac9c5eb8b707aee8366f74574c8f42ef240599c955ef4401cf578be30200000002ab518893292204c430eb0100000000016503138a0300000000040053abac60e0eb010000000005525200ab63567c2d030000000004abab52006cf81e85", "ab51525152", 1, 2118315905, "4e4c9a781f626b59b1d3ad8f2c488eb6dee8bb19b9bc138bf0dc33e7799210d4"], + ["c6c7a87003f772bcae9f3a0ac5e499000b68703e1804b9ddc3e73099663564d53ddc4e1c6e01000000076a536a6aac63636e3102122f4c30056ef8711a6bf11f641ddfa6984c25ac38c3b3e286e74e839198a80a34010000000165867195cd425821dfa2f279cb1390029834c06f018b1e6af73823c867bf3a0524d1d6923b0300000005acab53ab65ffffffff02fa4c49010000000008ab656a0052650053e001100400000000008836d972", "ac526351acab", 1, 978122815, "a869c18a0edf563d6e5eddd5d5ae8686f41d07f394f95c9feb8b7e52761531ca"], + ["0ea580ac04c9495ab6af3b8d59108bb4194fcb9af90b3511c83f7bb046d87aedbf8423218e02000000085152acac006363ab9063d7dc25704e0caa5edde1c6f2dd137ded379ff597e055b2977b9c559b07a7134fcef2000000000200aca89e50181f86e9854ae3b453f239e2847cf67300fff802707c8e3867ae421df69274449402000000056365abababffffffff47a4760c881a4d7e51c69b69977707bd2fb3bcdc300f0efc61f5840e1ac72cee0000000000ffffffff0460179a020000000004ab53ab52a5250c0500000000096565acac6365ab52ab6c281e02000000000952635100ac006563654e55070400000000046552526500000000", "ab526563acac53ab", 2, 1426964167, "b1c50d58b753e8f6c7513752158e9802cf0a729ebe432b99acc0fe5d9b4e9980"], + ["c33028b301d5093e1e8397270d75a0b009b2a6509a01861061ab022ca122a6ba935b8513320200000000ffffffff013bcf5a0500000000015200000000", "", 0, -513413204, "6b1459536f51482f5dbf42d7e561896557461e1e3b6bf67871e2b51faae2832c"], + ["43b2727901a7dd06dd2abf690a1ccedc0b0739cb551200796669d9a25f24f71d8d101379f50300000000ffffffff0418e031040000000000863d770000000000085352ac526563ac5174929e040000000004ac65ac00ec31ac0100000000066a51ababab5300000000", "65", 0, -492874289, "154ff7a9f0875edcfb9f8657a0b98dd9600fabee3c43eb88af37cf99286d516c"], + ["4763ed4401c3e6ab204bed280528e84d5288f9cac5fb8a2e7bd699c7b98d4df4ac0c40e55303000000066a6aacab5165ffffffff015b57f80400000000046a63535100000000", "ac51abab53", 0, -592611747, "849033a2321b5755e56ef4527ae6f51e30e3bca50149d5707368479723d744f8"], + ["d24f647b02f71708a880e6819a1dc929c1a50b16447e158f8ff62f9ccd644e0ca3c592593702000000050053536a00ffffffff67868cd5414b6ca792030b18d649de5450a456407242b296d936bcf3db79e07b02000000005af6319c016022f50100000000036a516300000000", "6aab526353516a6a", 0, 1350782301, "8556fe52d1d0782361dc28baaf8774b13f3ce5ed486ae0f124b665111e08e3e3"], + ["fe6ddf3a02657e42a7496ef170b4a8caf245b925b91c7840fd28e4a22c03cb459cb498b8d603000000065263656a650071ce6bf8d905106f9f1faf6488164f3decac65bf3c5afe1dcee20e6bc3cb6d052561985a030000000163295b117601343dbb0000000000026563dba521df", "", 1, -1696179931, "d9684685c99ce48f398fb467a91a1a59629a850c429046fb3071f1fa9a5fe816"], + ["c61523ef0129bb3952533cbf22ed797fa2088f307837dd0be1849f20decf709cf98c6f032f03000000026563c0f1d378044338310400000000066363516a5165a14fcb0400000000095163536a6a00ab53657271d60200000000001d953f0500000000010000000000", "53516353005153", 0, 1141615707, "7e975a72db5adaa3c48d525d9c28ac11cf116d0f8b16ce08f735ad75a80aec66"], + ["ba3dac6c0182562b0a26d475fe1e36315f0913b6869bdad0ecf21f1339a5fcbccd32056c840200000000ffffffff04300351050000000000220ed405000000000851abac636565ac53dbbd19020000000007636363ac6a52acbb005a0500000000016abd0c78a8", "63006a635151005352", 0, 1359658828, "47bc8ab070273e1f4a0789c37b45569a6e16f3f3092d1ce94dddc3c34a28f9f4"], + ["ac27e7f5025fc877d1d99f7fc18dd4cadbafa50e34e1676748cc89c202f93abf36ed46362101000000036300abffffffff958cd5381962b765e14d87fc9524d751e4752dd66471f973ed38b9d562e525620100000003006500ffffffff02b67120050000000004ac51516adc330c0300000000015200000000", "656352", 1, 15049991, "f3374253d64ac264055bdbcc32e27426416bd595b7c7915936c70f839e504010"], + ["edb30140029182b80c8c3255b888f7c7f061c4174d1db45879dca98c9aab8c8fed647a6ffc03000000086a53510052ab6300ffffffff82f65f261db62d517362c886c429c8fbbea250bcaad93356be6f86ba573e9d930100000000ffffffff04daaf150400000000016a86d1300100000000096a6353535252ac5165d4ddaf000000000002abab5f1c6201000000000000000000", "ab6a6a00ac", 0, -2058017816, "8d7794703dad18e2e40d83f3e65269834bb293e2d2b8525932d6921884b8f368"], + ["7e50207303146d1f7ad62843ae8017737a698498d4b9118c7a89bb02e8370307fa4fada41d000000000753006300005152b7afefc85674b1104ba33ef2bf37c6ed26316badbc0b4aa6cb8b00722da4f82ff3555a6c020000000900ac656363ac51ac52ffffffff93fab89973bd322c5d7ad7e2b929315453e5f7ada3072a36d8e33ca8bebee6e0020000000300acab930da52b04384b04000000000004650052ac435e380200000000076a6a515263ab6aa9494705000000000600ab6a525252af8ba90100000000096565acab526353536a279b17ad", "acac005263536aac63", 1, -34754133, "4e6357da0057fb7ff79da2cc0f20c5df27ff8b2f8af4c1709e6530459f7972b0"], + ["c05764f40244fb4ebe4c54f2c5298c7c798aa90e62c29709acca0b4c2c6ec08430b26167440100000008acab6a6565005253ffffffffc02c2418f398318e7f34a3cf669d034eef2111ea95b9f0978b01493293293a870100000000e563e2e00238ee8d040000000002acab03fb060200000000076500ac656a516aa37f5534", "52ab6a0065", 1, -2033176648, "83deef4a698b62a79d4877dd9afebc3011a5275dbe06e89567e9ef84e8a4ee19"], + ["5a59e0b9040654a3596d6dab8146462363cd6549898c26e2476b1f6ae42915f73fd9aedfda00000000036363abffffffff9ac9e9ca90be0187be2214251ff08ba118e6bf5e2fd1ba55229d24e50a510d53010000000165ffffffff41d42d799ac4104644969937522873c0834cc2fcdab7cdbecd84d213c0e96fd60000000000ffffffffd838db2c1a4f30e2eaa7876ef778470f8729fcf258ad228b388df2488709f8410300000000fdf2ace002ceb6d903000000000265654c1310040000000003ac00657e91c0ec", "536a63ac", 0, 82144555, "98ccde2dc14d14f5d8b1eeea5364bd18fc84560fec2fcea8de4d88b49c00695e"], + ["156ebc8202065d0b114984ee98c097600c75c859bfee13af75dc93f57c313a877efb09f230010000000463536a51ffffffff81114e8a697be3ead948b43b5005770dd87ffb1d5ccd4089fa6c8b33d3029e9c03000000066a5251656351ffffffff01a87f140000000000050000ac51ac00000000", "00", 0, -362221092, "a903c84d8c5e71134d1ab6dc1e21ac307c4c1a32c90c90f556f257b8a0ec1bf5"], + ["15e37793023c7cbf46e073428908fce0331e49550f2a42b92468827852693f0532a01c29f70200000007005353636351acffffffff38426d9cec036f00eb56ec1dcd193647e56a7577278417b8a86a78ac53199bc403000000056353006a53ffffffff04a25ce103000000000900ab5365656a526a63c8eff7030000000004526353537ab6db0200000000016a11a3fa02000000000651acacab526500000000", "53ac6aab6a6551", 0, 1117532791, "83c68b3c5a89260ce16ce8b4dbf02e1f573c532d9a72f5ea57ab419fa2630214"], + ["f7a09f10027250fc1b70398fb5c6bffd2be9718d3da727e841a73596fdd63810c9e4520a6a010000000963ac516a636a65acac1d2e2c57ab28d311edc4f858c1663972eebc3bbc93ed774801227fda65020a7ec1965f780200000005ac5252516a8299fddc01dcbf7200000000000463ac6551960fda03", "65acab51", 1, 2017321737, "9c5fa02abfd34d0f9dec32bf3edb1089fca70016debdb41f4f54affcb13a2a2a"], + ["6d97a9a5029220e04f4ccc342d8394c751282c328bf1c132167fc05551d4ca4da4795f6d4e02000000076a0052ab525165ffffffff9516a205e555fa2a16b73e6db6c223a9e759a7e09c9a149a8f376c0a7233fa1b0100000007acab51ab63ac6affffffff04868aed04000000000652ac65ac536a396edf01000000000044386c0000000000076aab5363655200894d48010000000001ab8ebefc23", "6351526aac51", 1, 1943666485, "f0bd4ca8e97203b9b4e86bc24bdc8a1a726db5e99b91000a14519dc83fc55c29"], + ["8e3fddfb028d9e566dfdda251cd874cd3ce72e9dde837f95343e90bd2a93fe21c5daeb5eed01000000045151525140517dc818181f1e7564b8b1013fd68a2f9a56bd89469686367a0e72c06be435cf99db750000000003635251ffffffff01c051780300000000096552ababac6a65acab099766eb", "5163ab6a52ababab51", 1, 1296295812, "5509eba029cc11d7dd2808b8c9eb47a19022b8d8b7778893459bbc19ab7ea820"], + ["a603f37b02a35e5f25aae73d0adc0b4b479e68a734cf722723fd4e0267a26644c36faefdab0200000000ffffffff43374ad26838bf733f8302585b0f9c22e5b8179888030de9bdda180160d770650200000001004c7309ce01379099040000000005526552536500000000", "abababab005153", 0, 1409936559, "4ca73da4fcd5f1b10da07998706ffe16408aa5dff7cec40b52081a6514e3827e"], + ["9eeedaa8034471a3a0e3165620d1743237986f060c4434f095c226114dcb4b4ec78274729f03000000086a5365510052ac6afb505af3736e347e3f299a58b1b968fce0d78f7457f4eab69240cbc40872fd61b5bf8b120200000002ac52df8247cf979b95a4c97ecb8edf26b3833f967020cd2fb25146a70e60f82c9ee4b14e88b103000000008459e2fa0125cbcd05000000000000000000", "52ab5352006353516a", 0, -1832576682, "fb018ae54206fdd20c83ae5873ec82b8e320a27ed0d0662db09cda8a071f9852"], + ["05921d7c048cf26f76c1219d0237c226454c2a713c18bf152acc83c8b0647a94b13477c07f0300000003ac526afffffffff2f494453afa0cabffd1ba0a626c56f90681087a5c1bd81d6adeb89184b27b7402000000036a6352ffffffff0ad10e2d3ce355481d1b215030820da411d3f571c3f15e8daf22fe15342fed04000000000095f29f7b93ff814a9836f54dc6852ec414e9c4e16a506636715f569151559100ccfec1d100000000055263656a53ffffffff04f4ffef010000000008ac6a6aabacabab6a0e6689040000000006ab536a5352abe364d005000000000965536363655251ab53807e00010000000004526aab63f18003e3", "6363ac51", 3, -375891099, "001b0b176f0451dfe2d9787b42097ceb62c70d324e925ead4c58b09eebdf7f67"], + ["b9b44d9f04b9f15e787d7704e6797d51bc46382190c36d8845ec68dfd63ee64cf7a467b21e00000000096aac00530052ab636aba1bcb110a80c5cbe073f12c739e3b20836aa217a4507648d133a8eedd3f02cb55c132b203000000076a000063526352b1c288e3a9ff1f2da603f230b32ef7c0d402bdcf652545e2322ac01d725d75f5024048ad0100000000ffffffffffd882d963be559569c94febc0ef241801d09dc69527c9490210f098ed8203c700000000056a006300ab9109298d01719d9a0300000000066a52ab006365d7894c5b", "ac6351650063636a", 3, -622355349, "ac87b1b93a6baab6b2c6624f10e8ebf6849b0378ef9660a3329073e8f5553c8d"], + ["ff60473b02574f46d3e49814c484081d1adb9b15367ba8487291fc6714fd6e3383d5b335f001000000026a6ae0b82da3dc77e5030db23d77b58c3c20fa0b70aa7d341a0f95f3f72912165d751afd57230300000008ac536563516a6363ffffffff04f86c0200000000000553acab636ab13111000000000003510065f0d3f305000000000951ab516a65516aabab730a3a010000000002515200000000", "ac6a", 1, 1895032314, "0767e09bba8cd66d55915677a1c781acd5054f530d5cf6de2d34320d6c467d80"], + ["f218026204f4f4fc3d3bd0eada07c57b88570d544a0436ae9f8b753792c0c239810bb30fbc0200000002536affffffff8a468928d6ec4cc10aa0f73047697970e99fa64ae8a3b4dca7551deb0b639149010000000851ab520052650051ffffffffa98dc5df357289c9f6873d0f5afcb5b030d629e8f23aa082cf06ec9a95f3b0cf0000000000ffffffffea2c2850c5107705fd380d6f29b03f533482fd036db88739122aac9eff04e0aa010000000365536a03bd37db034ac4c4020000000007515152655200ac33b27705000000000151efb71e0000000000007b65425b", "515151", 3, -1772252043, "de35c84a58f2458c33f564b9e58bc57c3e028d629f961ad1b3c10ee020166e5a"], + ["48e7d42103b260b27577b70530d1ac2fed2551e9dd607cbcf66dca34bb8c03862cf8f5fd5401000000075151526aacab00ffffffff1e3d3b841552f7c6a83ee379d9d66636836673ce0b0eda95af8f2d2523c91813030000000665acac006365ffffffff388b3c386cd8c9ef67c83f3eaddc79f1ff910342602c9152ffe8003bce51b28b0100000008636363006a636a52ffffffff04b8f67703000000000852005353ac6552520cef720200000000085151ab6352ab00ab5096d6030000000005516a005100662582020000000001ac6c137280", "6a65", 1, 1513618429, "e2fa3e1976aed82c0987ab30d4542da2cb1cffc2f73be13480132da8c8558d5c"], + ["91ebc4cf01bc1e068d958d72ee6e954b196f1d85b3faf75a521b88a78021c543a06e056279000000000265ab7c12df0503832121030000000000cc41a6010000000005ab5263516540a951050000000006ab63ab65acac00000000", "526a0065636a6a6aac", 0, -614046478, "7de4ba875b2e584a7b658818c112e51ee5e86226f5a80e5f6b15528c86400573"], + ["3cd4474201be7a6c25403bf00ca62e2aa8f8f4f700154e1bb4d18c66f7bb7f9b975649f0dc0100000006535151535153ffffffff01febbeb000000000006005151006aac00000000", "", 0, -1674687131, "6b77ca70cc452cc89acb83b69857cda98efbfc221688fe816ef4cb4faf152f86"], + ["92fc95f00307a6b3e2572e228011b9c9ed41e58ddbaefe3b139343dbfb3b34182e9fcdc3f50200000002acab847bf1935fde8bcfe41c7dd99683289292770e7f163ad09deff0e0665ed473cd2b56b0f40300000006516551ab6351294dab312dd87b9327ce2e95eb44b712cfae0e50fda15b07816c8282e8365b643390eaab01000000026aacffffffff016e0b6b040000000001ac00000000", "650065acac005300", 2, -1885164012, "bd7d26bb3a98fc8c90c972500618bf894cb1b4fe37bf5481ff60eef439d3b970"], + ["4db591ab018adcef5f4f3f2060e41f7829ce3a07ea41d681e8cb70a0e37685561e4767ac3b0000000005000052acabd280e63601ae6ef20000000000036a636326c908f7", "ac6a51526300630052", 0, 862877446, "355ccaf30697c9c5b966e619a554d3323d7494c3ea280a9b0dfb73f953f5c1cb"], + ["503fd5ef029e1beb7b242d10032ac2768f9a1aca0b0faffe51cec24770664ec707ef7ede4f01000000045253ac53375e350cc77741b8e96eb1ce2d3ca91858c052e5f5830a0193200ae2a45b413dda31541f0000000003516553ffffffff0175a5ba0500000000015200000000", "6aab65510053ab65", 1, 1603081205, "353ca9619ccb0210ae18b24d0e57efa7abf8e58fa6f7102738e51e8e72c9f0c4"], + ["c80abebd042cfec3f5c1958ee6970d2b4586e0abec8305e1d99eb9ee69ecc6c2cbd76374380000000007ac53006300ac510acee933b44817db79320df8094af039fd82111c7726da3b33269d3820123694d849ee5001000000056a65ab526562699bea8530dc916f5d61f0babea709dac578774e8a4dcd9c640ec3aceb6cb2443f24f302000000020063ea780e9e57d1e4245c1e5df19b4582f1bf704049c5654f426d783069bcc039f2d8fa659f030000000851ab53635200006a8d00de0b03654e8500000000000463ab635178ebbb0400000000055100636aab239f1d030000000006ab006300536500000000", "6565ac515100", 3, 1460851377, "b35bb1b72d02fab866ed6bbbea9726ab32d968d33a776686df3ac16aa445871e"], + ["0337b2d5043eb6949a76d6632b8bb393efc7fe26130d7409ef248576708e2d7f9d0ced9d3102000000075352636a5163007034384dfa200f52160690fea6ce6c82a475c0ef1caf5c9e5a39f8f9ddc1c8297a5aa0eb02000000026a51ffffffff38e536298799631550f793357795d432fb2d4231f4effa183c4e2f61a816bcf0030000000463ac5300706f1cd3454344e521fde05b59b96e875c8295294da5d81d6cc7efcfe8128f150aa54d6503000000008f4a98c704c1561600000000000072cfa6000000000000e43def01000000000100cf31cc0500000000066365526a6500cbaa8e2e", "", 3, 2029506437, "7615b4a7b3be865633a31e346bc3db0bcc410502c8358a65b8127089d81b01f8"], + ["59f6cffd034733f4616a20fe19ea6aaf6abddb30b408a3a6bd86cd343ab6fe90dc58300cc90200000000ffffffffc835430a04c3882066abe7deeb0fa1fdaef035d3233460c67d9eabdb05e95e5a02000000080065ac535353ab00ffffffff4b9a043e89ad1b4a129c8777b0e8d87a014a0ab6a3d03e131c27337bbdcb43b402000000066a5100abac6ad9e9bf62014bb118010000000001526cbe484f", "ab526352ab65", 0, 2103515652, "4f2ccf981598639bec57f885b4c3d8ea8db445ea6e61cfd45789c69374862e5e"], + ["cbc79b10020b15d605680a24ee11d8098ad94ae5203cb6b0589e432832e20c27b72a926af20300000006ab65516a53acbb854f3146e55c508ece25fa3d99dbfde641a58ed88c051a8a51f3dacdffb1afb827814b02000000026352c43e6ef30302410a020000000000ff4bd90100000000065100ab63000008aa8e0400000000095265526565ac5365abc52c8a77", "53526aac0051", 0, 202662340, "984efe0d8d12e43827b9e4b27e97b3777ece930fd1f589d616c6f9b71dab710e"], + ["7c07419202fa756d29288c57b5c2b83f3c847a807f4a9a651a3f6cd6c46034ae0aa3a7446b0200000004ab6a6365ffffffff9da83cf4219bb96c76f2d77d5df31c1411a421171d9b59ec02e5c1218f29935403000000008c13879002f8b1ac0400000000086a63536a636553653c584f02000000000000000000", "abac53ab656363", 1, -1038419525, "4a74f365a161bc6c9bddd249cbd70f5dadbe3de70ef4bd745dcb6ee1cd299fbd"], + ["351cbb57021346e076d2a2889d491e9bfa28c54388c91b46ee8695874ad9aa576f1241874d0200000008ab6563525300516affffffffe13e61b8880b8cd52be4a59e00f9723a4722ea58013ec579f5b3693b9e115b1100000000096363abac5252635351ffffffff027fee02040000000008ab6a5200ab006a65b85f130200000000086a52630053ab52ab00000000", "ab6aab65", 1, 586415826, "08bbb746a596991ab7f53a76e19acad087f19cf3e1db54054aab403c43682d09"], + ["a8252ea903f1e8ff953adb16c1d1455a5036222c6ea98207fc21818f0ece2e1fac310f9a0100000000095163ac635363ac0000be6619e9fffcde50a0413078821283ce3340b3993ad00b59950bae7a9f931a9b0a3a035f010000000463005300b8b0583fbd6049a1715e7adacf770162811989f2be20af33f5f60f26eba653dc26b024a00000000006525351636552ffffffff046d2acc030000000002636a9a2d430500000000080065005165ab53abecf63204000000000052b9ed050000000008acacac53ab65656500000000", "65ab53635253636a51", 2, 1442639059, "8ca11838775822f9a5beee57bdb352f4ee548f122de4a5ca61c21b01a1d50325"], + ["2f1a425c0471a5239068c4f38f9df135b1d24bf52d730d4461144b97ea637504495aec360801000000055300515365c71801dd1f49f376dd134a9f523e0b4ae611a4bb122d8b26de66d95203f181d09037974300000000025152ffffffff9bdcea7bc72b6e5262e242c94851e3a5bf8f314b3e5de0e389fc9e5b3eadac030000000009525265655151005153ffffffffdbb53ce99b5a2320a4e6e2d13b01e88ed885a0957d222e508e9ec8e4f83496cb0200000007635200abac63ac04c96237020cc5490100000000080000516a51ac6553074a360200000000025152225520ca", "6551ab65ac65516a", 1, -489869549, "9bc5bb772c553831fb40abe466074e59a469154679c7dee042b8ea3001c20393"], + ["ef3acfd4024defb48def411b8f8ba2dc408dc9ee97a4e8bde4d6cb8e10280f29c98a6e8e9103000000035100513d5389e3d67e075469dfd9f204a7d16175653a149bd7851619610d7ca6eece85a516b2df0300000005516aac6552ca678bdf02f477f003000000000057e45b0300000000055252525252af35c20a", "5165ac53ab", 1, -1900839569, "78eb6b24365ac1edc386aa4ffd15772f601059581c8776c34f92f8a7763c9ccf"], + ["ff4468dc0108475fc8d4959a9562879ce4ab4867a419664bf6e065f17ae25043e6016c70480100000000ffffffff02133c6f0400000000000bd0a8020000000004006a520035afa4f6", "51ac65ab", 0, -537664660, "f6da59b9deac63e83728850ac791de61f5dfcaeed384ebcbb20e44afcd8c8910"], + ["4e8594d803b1d0a26911a2bcdd46d7cbc987b7095a763885b1a97ca9cbb747d32c5ab9aa91030000000353ac53a0cc4b215e07f1d648b6eeb5cdbe9fa32b07400aa773b9696f582cebfd9930ade067b2b200000000060065abab6500fc99833216b8e27a02defd9be47fafae4e4a97f52a9d2a210d08148d2a4e5d02730bcd460100000004516351ac37ce3ae1033baa55040000000006006a636a63acc63c990400000000025265eb1919030000000005656a6a516a00000000", "", 1, -75217178, "04c5ee48514cd033b82a28e336c4d051074f477ef2675ce0ce4bafe565ee9049"], + ["a88830a7023f13ed19ab14fd757358eb6af10d6520f9a54923a6d613ac4f2c11e249cda8aa030000000851630065abababacffffffff8f5fe0bc04a33504c4b47e3991d25118947a0261a9fa520356731eeabd561dd3020000000363ababffffffff038404bd010000000008ab5153516aab6a63d33a5601000000000263004642dc020000000009655152acac636352004be6f3af", "5253536565006aab6a", 0, 1174417836, "2e42ead953c9f4f81b72c27557e6dc7d48c37ff2f5c46c1dbe9778fb0d79f5b2"], + ["44e1a2b4010762af23d2027864c784e34ef322b6e24c70308a28c8f2157d90d17b99cd94a401000000085163656565006300ffffffff0198233d020000000002000000000000", "52525153656365", 0, 1119696980, "d9096de94d70c6337da6202e6e588166f31bff5d51bb5adc9468594559d65695"], + ["44ca65b901259245abd50a745037b17eb51d9ce1f41aa7056b4888285f48c6f26cb97b7a25020000000552636363abffffffff047820350400000000040053acab14f3e603000000000652635100ab630ce66c03000000000001bdc704000000000765650065ac51ac3e886381", "51", 0, -263340864, "ed5622ac642d11f90e68c0feea6a2fe36d880ecae6b8c0d89c4ea4b3d162bd90"], + ["cfa147d2017fe84122122b4dda2f0d6318e59e60a7207a2d00737b5d89694d480a2c26324b0000000006006351526552ffffffff0456b5b804000000000800516aab525363ab166633000000000004655363ab254c0e02000000000952ab6a6a00ab525151097c1b020000000009656a52ac6300530065ad0d6e50", "6a535165ac6a536500", 0, -574683184, "f926d4036eac7f019a2b0b65356c4ee2fe50e089dd7a70f1843a9f7bc6997b35"], + ["91c5d5f6022fea6f230cc4ae446ce040d8313071c5ac1749c82982cc1988c94cb1738aa48503000000016a19e204f30cb45dd29e68ff4ae160da037e5fc93538e21a11b92d9dd51cf0b5efacba4dd70000000005656a6aac51ffffffff03db126905000000000953006a53ab6563636a36a273030000000006656a52656552b03ede00000000000352516500000000", "530052526a00", 1, 1437328441, "255c125b60ee85f4718b2972174c83588ee214958c3627f51f13b5fb56c8c317"], + ["03f20dc202c886907b607e278731ebc5d7373c348c8c66cac167560f19b341b782dfb634cb03000000076a51ac6aab63abea3e8de7adb9f599c9caba95aa3fa852e947fc88ed97ee50e0a0ec0d14d164f44c0115c10100000004ab5153516fdd679e0414edbd000000000005ac636a53512021f2040000000007006a0051536a52c73db2050000000005525265ac5369046e000000000003ab006a1ef7bd1e", "52656a", 0, 1360223035, "5a0a05e32ce4cd0558aabd5d79cd5fcbffa95c07137506e875a9afcba4bef5a2"], + ["d9611140036881b61e01627078512bc3378386e1d4761f959d480fdb9d9710bebddba2079d020000000763536aab5153ab819271b41e228f5b04daa1d4e72c8e1955230accd790640b81783cfc165116a9f535a74c000000000163ffffffffa2e7bb9a28e810624c251ff5ba6b0f07a356ac082048cf9f39ec036bba3d431a02000000076a000000ac65acffffffff01678a820000000000085363515153ac635100000000", "535353", 2, -82213851, "52b9e0778206af68998cbc4ebdaad5a9469e04d0a0a6cef251abfdbb74e2f031"], + ["98b3a0bf034233afdcf0df9d46ac65be84ef839e58ee9fa59f32daaa7d684b6bdac30081c60200000007636351acabababffffffffc71cf82ded4d1593e5825618dc1d5752ae30560ecfaa07f192731d68ea768d0f0100000006650052636563f3a2888deb5ddd161430177ce298242c1a86844619bc60ca2590d98243b5385bc52a5b8f00000000095365acacab520052ac50d4722801c3b8a60300000000035165517e563b65", "51", 1, -168940690, "b6b684e2d2ecec8a8dce4ed3fc1147f8b2e45732444222aa8f52d860c2a27a9d"], + ["97be4f7702dc20b087a1fdd533c7de762a3f2867a8f439bddf0dcec9a374dfd0276f9c55cc0300000000cdfb1dbe6582499569127bda6ca4aaff02c132dc73e15dcd91d73da77e92a32a13d1a0ba0200000002ab51ffffffff048cfbe202000000000900516351515363ac535128ce0100000000076aac5365ab6aabc84e8302000000000863536a53ab6a6552f051230500000000066aac535153510848d813", "ac51", 0, 229541474, "e5da9a416ea883be1f8b8b2d178463633f19de3fa82ae25d44ffb531e35bdbc8"], + ["085b6e04040b5bff81e29b646f0ed4a45e05890a8d32780c49d09643e69cdccb5bd81357670100000001abffffffffa5c981fe758307648e783217e3b4349e31a557602225e237f62b636ec26df1a80300000004650052ab4792e1da2930cc90822a8d2a0a91ea343317bce5356b6aa8aae6c3956076aa33a5351a9c0300000004abac5265e27ddbcd472a2f13325cc6be40049d53f3e266ac082172f17f6df817db1936d9ff48c02b000000000152ffffffff021aa7670500000000085353635163ab51ac14d584000000000001aca4d136cc", "6a525300536352536a", 0, -1398925877, "41ecca1e8152ec55074f4c39f8f2a7204dda48e9ec1e7f99d5e7e4044d159d43"], + ["eec32fff03c6a18b12cd7b60b7bdc2dd74a08977e53fdd756000af221228fe736bd9c42d870100000007005353ac515265ffffffff037929791a188e9980e8b9cc154ad1b0d05fb322932501698195ab5b219488fc02000000070063510065ab6a0bfc176aa7e84f771ea3d45a6b9c24887ceea715a0ff10ede63db8f089e97d927075b4f1000000000551abab63abffffffff02eb933c000000000000262c420000000000036563632549c2b6", "6352", 2, 1480445874, "ff8a4016dfdd918f53a45d3a1f62b12c407cd147d68ca5c92b7520e12c353ff5"], + ["98ea7eac0313d9fb03573fb2b8e718180c70ce647bebcf49b97a8403837a2556cb8c9377f30000000004ac53ac65ffffffff8caac77a5e52f0d8213ef6ce998bedbb50cfdf108954771031c0e0cd2a78423900000000010066e99a44937ebb37015be3693761078ad5c73aa73ec623ac7300b45375cc8eef36087eb80000000007515352acac5100ffffffff0114a51b02000000000000000000", "6aacab", 0, 243527074, "bad77967f98941af4dd52a8517d5ad1e32307c0d511e15461e86465e1b8b5273"], + ["3ab70f4604e8fc7f9de395ec3e4c3de0d560212e84a63f8d75333b604237aa52a10da17196000000000763526a6553ac63a25de6fd66563d71471716fe59087be0dde98e969e2b359282cf11f82f14b00f1c0ac70f02000000050052516aacdffed6bb6889a13e46956f4b8af20752f10185838fd4654e3191bf49579c961f5597c36c0100000005ac636363abc3a1785bae5b8a1b4be5d0cbfadc240b4f7acaa7dfed6a66e852835df5eb9ac3c553766801000000036a65630733b7530218569602000000000952006a6a6a51acab52777f06030000000007ac0063530052abc08267c9", "000000536aac0000", 1, 1919096509, "df1c87cf3ba70e754d19618a39fdbd2970def0c1bfc4576260cba5f025b87532"], + ["bdb6b4d704af0b7234ced671c04ba57421aba7ead0a117d925d7ebd6ca078ec6e7b93eea6600000000026565ffffffff3270f5ad8f46495d69b9d71d4ab0238cbf86cc4908927fbb70a71fa3043108e6010000000700516a65655152ffffffff6085a0fdc03ae8567d0562c584e8bfe13a1bd1094c518690ebcb2b7c6ce5f04502000000095251530052536a53aba576a37f2c516aad9911f687fe83d0ae7983686b6269b4dd54701cb5ce9ec91f0e6828390300000000ffffffff04cc76cc020000000002656a01ffb702000000000253ab534610040000000009acab006565516a00521f55f5040000000000389dfee9", "6a525165", 0, 1336204763, "71c294523c48fd7747eebefbf3ca06e25db7b36bff6d95b41c522fecb264a919"], + ["54258edd017d22b274fbf0317555aaf11318affef5a5f0ae45a43d9ca4aa652c6e85f8a040010000000953ac65ab5251656500ffffffff03321d450000000000085265526a51526a529ede8b030000000003635151ce6065020000000001534c56ec1b", "acac", 0, 2094130012, "110d90fea9470dfe6c5048f45c3af5e8cc0cb77dd58fd13d338268e1c24b1ccc"], + ["ce0d322e04f0ffc7774218b251530a7b64ebefca55c90db3d0624c0ff4b3f03f918e8cf6f60300000003656500ffffffff9cce943872da8d8af29022d0b6321af5fefc004a281d07b598b95f6dcc07b1830200000007abab515351acab8d926410e69d76b7e584aad1470a97b14b9c879c8b43f9a9238e52a2c2fefc2001c56af8010000000400ab5253cd2cd1fe192ce3a93b5478af82fa250c27064df82ba416dfb0debf4f0eb307a746b6928901000000096500abacac6a0063514214524502947efc0200000000035251652c40340100000000096a6aab52000052656a5231c54c", "51", 2, -2090320538, "0322ca570446869ec7ec6ad66d9838cff95405002d474c0d3c17708c7ee039c6"], + ["47ac54940313430712ebb32004679d3a512242c2b33d549bf5bbc8420ec1fd0850ed50eb6d0300000009536aac6a65acacab51ffffffffb843e44266ce2462f92e6bff54316661048c8c17ecb092cb493b39bfca9117850000000001519ab348c05e74ebc3f67423724a3371dd99e3bceb4f098f8860148f48ad70000313c4c223000000000653006565656512c2d8dc033f3c97010000000002636aa993aa010000000006526365ab526ab7cf560300000000076a0065ac6a526500000000", "005352535300ab6a", 2, 59531991, "8b5b3d00d9c658f062fe6c5298e54b1fe4ed3a3eab2a87af4f3119edc47b1691"], + ["233cd90b043916fc41eb870c64543f0111fb31f3c486dc72457689dea58f75c16ae59e9eb2000000000500536a6a6affffffff9ae30de76be7cd57fb81220fce78d74a13b2dbcad4d023f3cadb3c9a0e45a3ce000000000965ac6353ac5165515130834512dfb293f87cb1879d8d1b20ebad9d7d3d5c3e399a291ce86a3b4d30e4e32368a9020000000453005165ffffffff26d84ae93eb58c81158c9b3c3cbc24a84614d731094f38d0eea8686dec02824d0300000005636a65abacf02c784001a0bd5d03000000000900655351ab65ac516a416ef503", "", 1, -295106477, "b79f31c289e95d9dadec48ebf88e27c1d920661e50d090e422957f90ff94cb6e"], + ["9200e26b03ff36bc4bf908143de5f97d4d02358db642bd5a8541e6ff709c420d1482d471b70000000008abab65536a636553ffffffff61ba6d15f5453b5079fb494af4c48de713a0c3e7f6454d7450074a2a80cb6d880300000007ac6a00ab5165515dfb7574fbce822892c2acb5d978188b1d65f969e4fe874b08db4c791d176113272a5cc10100000000ffffffff0420958d000000000009ac63516a0063516353dd885505000000000465ac00007b79e901000000000066d8bf010000000005525252006a00000000", "ac5152", 0, 2089531339, "89ec7fab7cfe7d8d7d96956613c49dc48bf295269cfb4ea44f7333d88c170e62"], + ["45f335ba01ce2073a8b0273884eb5b48f56df474fc3dff310d9706a8ac7202cf5ac188272103000000025363ffffffff049d859502000000000365ab6a8e98b1030000000002ac51f3a80603000000000752535151ac00000306e30300000000020051b58b2b3a", "", 0, 1899564574, "78e01310a228f645c23a2ad0acbb8d91cedff4ecdf7ca997662c6031eb702b11"], + ["d8f652a6043b4faeada05e14b81756cd6920cfcf332e97f4086961d49232ad6ffb6bc6c097000000000453526563ffffffff1ea4d60e5e91193fbbc1a476c8785a79a4c11ec5e5d6c9950c668ceacfe07a15020000000352ab51fffffffffe029a374595c4edd382875a8dd3f20b9820abb3e93f877b622598d11d0b09e503000000095351000052ac515152ffffffff9d65fea491b979699ceb13caf2479cd42a354bd674ded3925e760758e85a756803000000046365acabffffffff0169001d00000000000651636a65656300000000", "ab0063630000ac", 3, 1050965951, "4cc85cbc2863ee7dbce15490d8ca2c5ded61998257b9eeaff968fe38e9f009ae"], + ["718662be026e1dcf672869ac658fd0c87d6835cfbb34bd854c44e577d5708a7faecda96e260300000004526a636a489493073353b678549adc7640281b9cbcb225037f84007c57e55b874366bb7b0fa03bdc00000000095165ababac65ac00008ab7f2a802eaa53d000000000007acac516aac526ae92f380100000000056aac00536500000000", "ab00", 1, 43296088, "2d642ceee910abff0af2116af75b2e117ffb7469b2f19ad8fef08f558416d8f7"], + ["94083c840288d40a6983faca876d452f7c52a07de9268ad892e70a81e150d602a773c175ad03000000007ec3637d7e1103e2e7e0c61896cbbf8d7e205b2ecc93dd0d6d7527d39cdbf6d335789f660300000000ffffffff019e1f7b03000000000800ac0051acac0053539cb363", "", 1, -183614058, "a17b66d6bb427f42653d08207a22b02353dd19ccf2c7de6a9a3a2bdb7c49c9e7"], + ["30e0d4d20493d0cd0e640b757c9c47a823120e012b3b64c9c1890f9a087ae4f2001ca22a61010000000152f8f05468303b8fcfaad1fb60534a08fe90daa79bff51675472528ebe1438b6f60e7f60c10100000009526aab6551ac510053ffffffffaaab73957ea2133e32329795221ed44548a0d3a54d1cf9c96827e7cffd1706df0200000009ab00526a005265526affffffffd19a6fe54352015bf170119742821696f64083b5f14fb5c7d1b5a721a3d7786801000000085265abababac53abffffffff020f39bd030000000004ab6aac52049f6c050000000004ab52516aba5b4c60", "6a6365516a6a655253", 0, -624256405, "8e221a6c4bf81ca0d8a0464562674dcd14a76a32a4b7baf99450dd9195d411e6"], + ["f9c69d940276ec00f65f9fe08120fc89385d7350388508fd80f4a6ba2b5d4597a9e21c884f010000000663ab63ababab15473ae6d82c744c07fc876ecd53bd0f3018b2dbedad77d757d5bdf3811b23d294e8c0170000000001abafababe00157ede2050000000006ac6a5263635300000000", "ab53", 1, 606547088, "714d8b14699835b26b2f94c58b6ea4c53da3f7adf0c62ea9966b1e1758272c47"], + ["5c0ac112032d6885b7a9071d3c5f493aa16c610a4a57228b2491258c38de8302014276e8be030000000300ab6a17468315215262ad5c7393bb5e0c5a6429fd1911f78f6f72dafbbbb78f3149a5073e24740300000003ac5100ffffffff33c7a14a062bdea1be3c9c8e973f54ade53fe4a69dcb5ab019df5f3345050be00100000008ac63655163526aab428defc0033ec36203000000000765516365536a00ae55b2000000000002ab53f4c0080400000000095265516a536563536a00000000", "6a005151006a", 2, 272749594, "91082410630337a5d89ff19145097090f25d4a20bdd657b4b953927b2f62c73b"], + ["e3683329026720010b08d4bec0faa244f159ae10aa582252dd0f3f80046a4e145207d54d31000000000852acac52656aacac3aaf2a5017438ad6adfa3f9d05f53ebed9ceb1b10d809d507bcf75e0604254a8259fc29c020000000653526552ab51f926e52c04b44918030000000000f7679c0100000000090000525152005365539e3f48050000000009516500ab635363ab008396c905000000000253650591024f", "6a6365", 0, 908746924, "458aec3b5089a585b6bad9f99fd37a2b443dc5a2eefac2b7e8c5b06705efc9db"], + ["48c4afb204204209e1df6805f0697edaa42c0450bbbd767941fe125b9bc40614d63d757e2203000000066a5363005152dc8b6a605a6d1088e631af3c94b8164e36e61445e2c60130292d81dabd30d15f54b355a802000000036a6353ffffffff1d05dcec4f3dedcfd02c042ce5d230587ee92cb22b52b1e59863f3717df2362f0300000005536552ac52ffffffffd4d71c4f0a7d53ba47bb0289ca79b1e33d4c569c1e951dd611fc9c9c1ca8bc6c030000000865536a65ab51abacffffffff042f9aa905000000000753655153656351ab93d8010000000002655337440e0300000000005d4c690000000000015278587acb", "ab006565526a51", 0, 1502064227, "bbed77ff0f808aa8abd946ba9e7ec1ddb003a969fa223dee0af779643cb841a9"], + ["00b20fd104dd59705b84d67441019fa26c4c3dec5fd3b50eca1aa549e750ef9ddb774dcabe000000000651ac656aac65ffffffff52d4246f2db568fc9eea143e4d260c698a319f0d0670f84c9c83341204fde48b0200000000ffffffffb8aeabb85d3bcbc67b132f1fd815b451ea12dcf7fc169c1bc2e2cf433eb6777a03000000086a51ac6aab6563acd510d209f413da2cf036a31b0def1e4dcd8115abf2e511afbcccb5ddf41d9702f28c52900100000006ac52ab6a0065ffffffff039c8276000000000008ab53655200656a52401561010000000003acab0082b7160100000000035100ab00000000", "535265", 1, -947367579, "3212c6d6dd8d9d3b2ac959dec11f4638ccde9be6ed5d36955769294e23343da0"], + ["455131860220abbaa72015519090a666faf137a0febce7edd49da1eada41feab1505a0028b02000000036365ab453ead4225724eb69beb590f2ec56a7693a608871e0ab0c34f5e96157f90e0a96148f3c502000000085251ab51535163acffffffff022d1249040000000009abac00acac6565630088b310040000000000e3920e59", "5152ab6a52ac5152", 0, 294375737, "c40fd7dfa72321ac79516502500478d09a35cc22cc264d652c7d18b14400b739"], + ["624d28cb02c8747915e9af2b13c79b417eb34d2fa2a73547897770ace08c6dd9de528848d3030000000651ab63abab533c69d3f9b75b6ef8ed2df50c2210fd0bf4e889c42477d58682f711cbaece1a626194bb85030000000765acab53ac5353ffffffff018cc280040000000009abacabac52636352ac6859409e", "ac51ac", 1, 1005144875, "919144aada50db8675b7f9a6849c9d263b86450570293a03c245bd1e3095e292"], + ["8f28471d02f7d41b2e70e9b4c804f2d90d23fb24d53426fa746bcdcfffea864925bdeabe3e0200000001acffffffff76d1d35d04db0e64d65810c808fe40168f8d1f2143902a1cc551034fd193be0e0000000001acffffffff048a5565000000000005005151516afafb610400000000045263ac53648bb30500000000086363516a6a5165513245de01000000000000000000", "6a0053510053", 1, -1525137460, "305fc8ff5dc04ebd9b6448b03c9a3d945a11567206c8d5214666b30ec6d0d6cc"], + ["10ec50d7046b8b40e4222a3c6449490ebe41513aad2eca7848284a08f3069f3352c2a9954f0000000009526aac656352acac53ffffffff0d979f236155aa972472d43ee6f8ce22a2d052c740f10b59211454ff22cb7fd00200000007acacacab63ab53ffffffffbbf97ebde8969b35725b2e240092a986a2cbfd58de48c4475fe077bdd493a20c010000000663ab5365ababffffffff4600722d33b8dba300d3ad037bcfc6038b1db8abfe8008a15a1de2da2264007302000000035351ac6dbdafaf020d0ccf04000000000663ab6a51ab6ae06e5e0200000000036aabab00000000", "", 0, -1658960232, "2420dd722e229eccafae8508e7b8d75c6920bfdb3b5bac7cb8e23419480637c2"], + ["fef98b7101bf99277b08a6eff17d08f3fcb862e20e13138a77d66fba55d54f26304143e5360100000006515365abab00ffffffff04265965030000000004655252ace2c775010000000001002b23b4040000000007516a5153ab53ac456a7a00000000000753ab525251acacba521291", "526aacacab00abab53", 0, -1614097109, "4370d05c07e231d6515c7e454a4e401000b99329d22ed7def323976fa1d2eeb5"], + ["34a2b8830253661b373b519546552a2c3bff7414ea0060df183b1052683d78d8f54e842442000000000152ffffffffd961a8e34cf374151058dfcddc86509b33832bc57267c63489f69ff01199697c0300000002abacba856cfb01b17c2f050000000008515365ac53ab000000000000", "5263ab656a", 1, -2104480987, "2f9993e0a84a6ca560d6d1cc2b63ffe7fd71236d9cfe7d809491cef62bbfad84"], + ["43559290038f32fda86580dd8a4bc4422db88dd22a626b8bd4f10f1c9dd325c8dc49bf479f01000000026351ffffffff401339530e1ed3ffe996578a17c3ec9d6fccb0723dd63e7b3f39e2c44b976b7b0300000006ab6a65656a51ffffffff6fb9ba041c96b886482009f56c09c22e7b0d33091f2ac5418d05708951816ce7000000000551ac525100ffffffff020921e40500000000035365533986f40500000000016a00000000", "52ac51", 0, 1769771809, "02040283ef2291d8e1f79bb71bdabe7c1546c40d7ed615c375643000a8b9600d"], + ["6878a6bd02e7e1c8082d5e3ee1b746cfebfac9e8b97e61caa9e0759d8a8ecb3743e36a30de0100000002ab532a911b0f12b73e0071f5d50b6bdaf783f4b9a6ce90ec0cad9eecca27d5abae188241ddec0200000001651c7758d803f7457b0500000000036551515f4e90000000000001007022080200000000035365acc86b6946", "6351ab", 0, -1929374995, "f24be499c58295f3a07f5f1c6e5084496ae160450bd61fdb2934e615289448f1"], + ["35b6fc06047ebad04783a5167ab5fc9878a00c4eb5e7d70ef297c33d5abd5137a2dea9912402000000036aacacffffffff21dc291763419a584bdb3ed4f6f8c60b218aaa5b99784e4ba8acfec04993e50c03000000046a00ac6affffffff69e04d77e4b662a82db71a68dd72ef0af48ca5bebdcb40f5edf0caf591bb41020200000000b5db78a16d93f5f24d7d932f93a29bb4b784febd0cbb1943f90216dc80bba15a0567684b000000000853ab52ab5100006a1be2208a02f6bdc103000000000265ab8550ea04000000000365636a00000000", "", 0, -1114114836, "1c8655969b241e717b841526f87e6bd68b2329905ba3fc9e9f72526c0b3ea20c"], + ["bebb90c302bf91fd4501d33555a5fc5f2e1be281d9b7743680979b65c3c919108cc2f517510100000003abab00ffffffff969c30053f1276550532d0aa33cfe80ca63758cd215b740448a9c08a84826f3303000000056565ab5153ffffffff04bf6f2a04000000000565ab5265ab903e760100000000026a6a7103fa020000000006526553525365b05b2c000000000006ab000000535300000000", "51510053ab63635153", 1, 1081291172, "94338cd47a4639be30a71e21a7103cee4c99ef7297e0edd56aaf57a068b004de"], + ["af48319f031b4eeb4319714a285f44244f283cbff30dcb9275b06f2348ccd0d7f015b54f8500000000066363ac65ac6affffffff2560a9817ebbc738ad01d0c9b9cf657b8f9179b1a7f073eb0b67517409d108180200000005ac6365ab52ffffffff0bdd67cd4ecae96249a2e2a96db1490ee645f042fd9d5579de945e22b799f4d003000000086552ab515153ab00cf187c8202e51abf0300000000066552006a00abadf37d000000000004ac6a535100000000", "63ab65", 1, -1855554446, "60caf46a7625f303c04706cec515a44b68ec319ee92273acb566cca4f66861c1"], + ["f35befbc03faf8c25cc4bc0b92f6239f477e663b44b83065c9cb7cf231243032cf367ce3130000000005ab65526a517c4c334149a9c9edc39e29276a4b3ffbbab337de7908ea6f88af331228bd90086a6900ba020000000151279d19950d2fe81979b72ce3a33c6d82ebb92f9a2e164b6471ac857f3bbd3c0ea213b542010000000953ab51635363520065052657c20300a9ba04000000000452636a6a0516ea020000000008535253656365ababcfdd3f01000000000865ac516aac00530000000000", "", 2, -99793521, "c834a5485e68dc13edb6c79948784712122440d7fa5bbaa5cd2fc3d4dac8185d"], + ["d3da18520216601acf885414538ce2fb4d910997eeb91582cac42eb6982c9381589587794f0300000000fffffffff1b1c9880356852e10cf41c02e928748dd8fae2e988be4e1c4cb32d0bfaea6f7000000000465ab6aabffffffff02fb0d69050000000002ababeda8580500000000085163526565ac52522b913c95", "ac", 1, -1247973017, "99b32b5679d91e0f9cdd6737afeb07459806e5acd7630c6a3b9ab5d550d0c003"], + ["8218eb740229c695c252e3630fc6257c42624f974bc856b7af8208df643a6c520ef681bfd00000000002510066f30f270a09b2b420e274c14d07430008e7886ec621ba45665057120afce58befca96010300000004525153ab84c380a9015d96100000000000076a5300acac526500000000", "ac005263", 0, -1855679695, "5071f8acf96aea41c7518bd1b5b6bbe16258b529df0c03f9e374b83c66b742c6"], + ["1123e7010240310013c74e5def60d8e14dd67aedff5a57d07a24abc84d933483431b8cf8ea0300000003530051fc6775ff1a23c627a2e605dd2560e84e27f4208300071e90f4589e762ad9c9fe8d0da95e020000000465655200ffffffff04251598030000000004ab65ab639d28d90400000000096563636aacac525153474df801000000000851525165ac51006a75e23b040000000000e5bd3a4a", "6363636565", 0, -467124448, "9cb0dd04e9fe287b112e94a1647590d27e8b164ca13c4fe70c610fd13f82c2fd"], + ["fd92fe1003083c5179f97e77bf7d71975788138147adbdb283306802e261c0aee080fa22630200000000860c643ba9a1816b9badf36077b4554d11720e284e395a1121bc45279e148b2064c65e49020000000651ab6a53636a2c713088d20f4bc4001264d972cce05b9fe004dc33376ad24d0d013e417b91a5f1b6734e000000000100ffffffff02e3064c0500000000066552006a5165b86e8705000000000665ab65ab53522052eadb", "00ab53525265", 0, 776203277, "47207b48777727532f62e09afcd4104ea6687e723c7657c30504fa2081331cc8"], + ["d1b6a703038f14d41fcc5cc45455faa135a5322be4bf0f5cbcd526578fc270a236cacb853f0200000001abffffffff135aeff902fa38f202ccf5bd34437ff89c9dc57a028b62447a0a38579383e8ef0000000000ffffffffadf398d2c818d0b90bc474f540c3618a4a643482eeab73d36101987e2ec0335900000000004bd3323504e69fc10000000000055151535251790ada02000000000563ab6aab521337a704000000000963ac63abacac52656a1e9862010000000007656500ac51ab6a8f4ee672", "ab5251656565ac63", 2, 82008394, "b8f3d255549909c07588ecba10a02e55a2d6f2206d831af9da1a7dae64cfbc8b"], + ["81dadaa7011556683db3fe95262f4fdb20391b7e75b7ffcee51b176af64d83c06f85545d620200000005ab5151ab52ffffffff044805ef0300000000065353516352639702c802000000000900516351515252ab5270db08040000000009ac516aab526553abac4aabc90500000000096365ab0052636a525100000000", "6565ab6a5152", 0, -2126294159, "ad01ec9d6dbae325ec3a8e1fd98e2d03b1188378210efef093dd8b0b0ef3f19d"], + ["3b937e05032b8895d2f4945cb7e3679be2fbd15311e2414f4184706dbfc0558cf7de7b4d000000000001638b91a12668a3c3ce349788c961c26aa893c862f1e630f18d80e7843686b6e1e6fc396310000000000852635353ab65ac51eeb09dd1c9605391258ee6f74b9ae17b5e8c2ef010dc721c5433dcdc6e93a1593e3b6d1700000000085365ac6553526351ffffffff0308b18e04000000000253acb6dd00040000000008536aac5153ac516ab0a88201000000000500ac006500804e3ff2", "", 0, 416167343, "595a3c02254564634e8085283ec4ea7c23808da97ce9c5da7aecd7b553e7fd7f"], + ["a48f27ca047997470da74c8ee086ddad82f36d9c22e790bd6f8603ee6e27ad4d3174ea875403000000095153ac636aab6aacabffffffffefc936294e468d2c9a99e09909ba599978a8c0891ad47dc00ba424761627cef202000000056a51630053ffffffff304cae7ed2d3dbb4f2fbd679da442aed06221ffda9aee460a28ceec5a9399f4e0200000000f5bddf82c9c25fc29c5729274c1ff0b43934303e5f595ce86316fc66ad263b96ca46ab8d0100000003536500d7cf226b0146b00c04000000000200ac5c2014ce", "515100636563", 0, 1991799059, "9c051a7092fe17fa62b1720bc2c4cb2ffc1527d9fb0b006d2e142bb8fe07bf3c"], + ["180cd53101c5074cf0b7f089d139e837fe49932791f73fa2342bd823c6df6a2f72fe6dba1303000000076a6a63ac53acabffffffff03853bc1020000000007ac526a6a6a6a003c4a8903000000000453515163a0fbbd030000000005ab656a5253253d64cf", "ac65", 0, -1548453970, "4d8efb3b99b9064d2f6be33b194a903ffabb9d0e7baa97a48fcec038072aac06"], + ["c21ec8b60376c47e057f2c71caa90269888d0ffd5c46a471649144a920d0b409e56f190b700000000008acac6a526a536365ffffffff5d315d9da8bf643a9ba11299450b1f87272e6030fdb0c8adc04e6c1bfc87de9a0000000000ea43a9a142e5830c96b0ce827663af36b23b0277244658f8f606e95384574b91750b8e940000000007516a63ac0063acffffffff023c61be0400000000055165ab5263313cc8020000000006006a53526551ed8c3d56", "6a", 1, 1160627414, "a638cc17fd91f4b1e77877e8d82448c84b2a4e100df1373f779de7ad32695112"], + ["128cd90f04b66a4cbc78bf48748f6eec0f08d5193ee8d0a6f2e8d3e5f138ed12c2c87d01a301000000085200ab6aac00ab00ffffffff09fc88bb1851e3dfb3d30179c38e15aeb1b39929c7c74f6acd071994ed4806490300000000e7fc5ea12ec56f56c0d758ecf4bb88aa95f3b08176b336db3b9bec2f6e27336dce28adbe030000000400530051fffffffffd6ff1adcf1fbe0d883451ee46904f1b7e8820243d395559b2d4ee8190a6e891000000000080fb1ae702f85b400000000000035200ab8d9651010000000006ab6a52536aab00000000", "ab", 1, 1667598199, "c10ccc9db8a92d7d4b133a2980782dab9d9d1d633d0dde9f9612ada57771fd89"], + ["da9695a403493d3511c10e1fe1286f954db0366b7667c91ef18ae4578056c1bf752114ac5901000000035351519788d91dd1f9c62dc005d80ea54eb13f7131ca5aace3d5d29f9b58ccc5fbc9a27e779950010000000453ac6a00ffffffffe2556ff29ebe83eb42a32c7a8d93bc598043578f491b5935805a33608538845a030000000252ab65d21b3b018f26c4030000000006acab51535352e1cbcb10", "006565ab52", 2, -1550927794, "0ca673a1ee66f9625ceb9ab278ebef772c113c188112b02824570c17fdf48194"], + ["b240517501334021240427adb0b413433641555424f6d24647211e3e6bfbb22a8045cbda2f000000000071bac8630112717802000000000000000000", "6a5165abac52656551", 0, 1790414254, "2c8be597620d95abd88f9c1cf4967c1ae3ca2309f3afec8928058c9598660e9e"], + ["96bac43903044a199b4b3efeeec5d196ee23fb05495541fa2cd6fb6405a9432d1723363660010000000151ffffffffe6ce2b66ce1488918a3e880bebb0e750123f007c7bcbac8fcd67ce75cb6fbae80300000000ffffffff9c0955aa07f506455834895c0c56be5a095398f47c62a3d431fe125b161d666a0200000005520000abac7ffdbc540216f2f004000000000165a26dce010000000001ab00000000", "5151ab656a656a6a63", 0, -707123065, "26b22e18d5d9081fde9631594a4f7c49069ed2e429f3d08caf9d834f685ccab2"], + ["b8fd394001ed255f49ad491fecc990b7f38688e9c837ccbc7714ddbbf5404f42524e68c18f0000000007ab6353535363ab081e15ee02706f7d050000000008515200535351526364c7ec040000000005636a53acac9206cbe1", "655352ac", 0, -1251578838, "8e0697d8cd8a9ccea837fd798cc6c5ed29f6fbd1892ee9bcb6c944772778af19"], + ["e42a76740264677829e30ed610864160c7f97232c16528fe5610fc08814b21c34eefcea69d010000000653006a6a0052ffffffff647046cf44f217d040e6a8ff3f295312ab4dd5a0df231c66968ad1c6d8f4428000000000025352ffffffff0199a7f900000000000000000000", "655263006a005163", 1, 1122505713, "7cda43f1ff9191c646c56a4e29b1a8c6cb3f7b331da6883ef2f0480a515d0861"], + ["0f034f32027a8e094119443aa9cfe11737c6d7dda9a52b839bc073dcc0235b847b28e0fab60200000006ac53ac536a63eee63447dfdad80476994b68706e916df1bd9d7cb4f3a4f6b14369de84564bea2e8688bd030000000565636a65acf8434663020b35fe01000000000800abab655163acabb3d6a103000000000353acab345eeda0", "526a51ac63ab51", 1, 66020215, "4435e62ff6531ac73529aac9cf878a7219e0b6e6cac79af8487c5355d1ad6d43"], + ["a2dfa4690214c1ab25331815a5128f143219de51a47abdc7ce2d367e683eeb93960a31af9f010000000363636affffffff8be0628abb1861b078fcc19c236bc4cc726fa49068b88ad170adb2a97862e7460200000004ac655363ffffffff0441f11103000000000153dbab0c000000000009ab53ac5365526aab63abbb95050000000004ab52516a29a029040000000003ac526a00000000", "6a52ac63", 1, -1302210567, "913060c7454e6c80f5ba3835454b54db2188e37dc4ce72a16b37d11a430b3d23"], + ["9dbc591f04521670af83fb3bb591c5d4da99206f5d38e020289f7db95414390dddbbeb56680100000004ac5100acffffffffb6a40b5e29d5e459f8e72d39f800089529f0889006cad3d734011991da8ef09d0100000009526a5100acab536a515fc427436df97cc51dc8497642ffc868857ee245314d28b356bd70adba671bd6071301fc0000000000ffffffff487efde2f620566a9b017b2e6e6d42525e4070f73a602f85c6dfd58304518db30000000005516353006a8d8090180244904a0200000000046a65656ab1e9c203000000000451ab63aba06a5449", "", 0, -1414953913, "bae189eb3d64aedbc28a6c28f6c0ccbd58472caaf0cf45a5aabae3e031dd1fea"], + ["1345fb2c04bb21a35ae33a3f9f295bece34650308a9d8984a989dfe4c977790b0c21ff9a7f0000000006ac52ac6a0053ffffffff7baee9e8717d81d375a43b691e91579be53875350dfe23ba0058ea950029fcb7020000000753ab53ab63ab52ffffffff684b6b3828dfb4c8a92043b49b8cb15dd3a7c98b978da1d314dce5b9570dadd202000000086353ab6a5200ac63d1a8647bf667ceb2eae7ec75569ca249fbfd5d1b582acfbd7e1fcf5886121fca699c011d0100000003ac006affffffff049b1eb00300000000001e46dc0100000000080065ab6a6a630065ca95b40300000000030051520c8499010000000006ab6aac526a6500000000", "53526aac636300", 2, 1809978100, "cfeaa36790bc398783d4ca45e6354e1ea52ee74e005df7f9ebd10a680e9607bf"], + ["7d75dc8f011e5f9f7313ba6aedef8dbe10d0a471aca88bbfc0c4a448ce424a2c5580cda1560300000003ab5152ffffffff01997f8e0200000000096552ac6a65656563530d93bbcc", "00656a6563", 0, 1414485913, "ec91eda1149f75bffb97612569a78855498c5d5386d473752a2c81454f297fa7"], + ["1459179504b69f01c066e8ade5e124c748ae5652566b34ed673eea38568c483a5a4c4836ca0100000008ac5352006563656affffffff5d4e037880ab1975ce95ea378d2874dcd49d5e01e1cdbfae3343a01f383fa35800000000095251ac52ac6aac6500ffffffff7de3ae7d97373b7f2aeb4c55137b5e947b2d5fb325e892530cb589bc4f92abd503000000086563ac53ab520052ffffffffb4db36a32d6e543ef49f4bafde46053cb85b2a6c4f0e19fa0860d9083901a1190300000003ab51531bbcfe5504a6dbda040000000008536a5365abac6500d660c80300000000096565abab6a53536a6a54e84e010000000003acac52df2ccf0500000000025351220c857e", "", 2, 1879181631, "3aad18a209fab8db44954eb55fd3cc7689b5ec9c77373a4d5f4dae8f7ae58d14"], + ["d98b777f04b1b3f4de16b07a05c31d79965579d0edda05600c118908d7cf642c9cd670093f020000000953005351ac65ab5363a268caad6733b7d1718008997f249e1375eb3ab9fe68ab0fe170d8e745ea24f54ce67f9b00000000066500516a5151ffffffff7ef8040dfcc86a0651f5907e8bfd1017c940f51cf8d57e3d3fe78d57e40b1e610200000003535263ffffffff39846cfed4babc098ff465256ba3820c30d710581316afcb67cd31c623b703360300000001acffffffff03d405120100000000056300006a5201a73d050000000004ab636a6a294c8c000000000006ac65536553ac00000000", "63525351abac", 1, 2018694761, "86970af23c89b72a4f9d6281e46b9ef5220816bed71ebf1ae20df53f38fe16ff"], + ["cabb1b06045a895e6dcfc0c1e971e94130c46feace286759f69a16d298c8b0f6fd0afef8f20300000004ac006352ffffffffa299f5edac903072bfb7d29b663c1dd1345c2a33546a508ba5cf17aab911234602000000056a65515365ffffffff89a20dc2ee0524b361231092a070ace03343b162e7162479c96b757739c8394a0300000002abab92ec524daf73fabee63f95c1b79fa8b84e92d0e8bac57295e1d0adc55dc7af5534ebea410200000001534d70e79b04674f6f00000000000600abacab53517d60cc0200000000035265ab96c51d040000000004ac6300ac62a787050000000008006a516563ab63639e2e7ff7", "6551ac6351ac", 3, 1942663262, "d0c4a780e4e0bc22e2f231e23f01c9d536b09f6e5be51c123d218e906ec518be"], + ["8b96d7a30132f6005b5bd33ea82aa325e2bcb441f46f63b5fca159ac7094499f380f6b7e2e00000000076aacabac6300acffffffff0158056700000000000465005100c319e6d0", "52006a", 0, -1100733473, "fb4bd26a91b5cf225dd3f170eb09bad0eac314bc1e74503cc2a3f376833f183e"], + ["112191b7013cfbe18a175eaf09af7a43cbac2c396f3695bbe050e1e5f4250603056d60910e02000000001c8a5bba03738a22010000000005525352656a77a149010000000002510003b52302000000000351ac52722be8e6", "65ac6565", 0, -1847972737, "8e795aeef18f510d117dfa2b9f4a2bd2e2847a343205276cedd2ba14548fd63f"], + ["ce6e1a9e04b4c746318424705ea69517e5e0343357d131ad55d071562d0b6ebfedafd6cb840100000003656553ffffffff67bd2fa78e2f52d9f8900c58b84c27ef9d7679f67a0a6f78645ce61b883fb8de000000000100d699a56b9861d99be2838e8504884af4d30b909b1911639dd0c5ad47c557a0773155d4d303000000046a5151abffffffff9fdb84b77c326921a8266854f7bbd5a71305b54385e747fe41af8a397e78b7fa010000000863acac6a51ab00ac0d2e9b9d049b8173010000000007ac53526a650063ba9b7e010000000008526a00525263acac0ab3fd030000000000ea8a0303000000000200aca61a97b9", "", 1, -1276952681, "b6ed4a3721be3c3c7305a5128c9d418efa58e419580cec0d83f133a93e3a22c5"], + ["a7721d94021652d90c79aaf5022d98219337d50f836382403ed313adb1116ba507ac28b0b0010000000551ac6300ab89e6d64a7aa81fb9595368f04d1b36d7020e7adf5807535c80d015f994cce29554fe869b01000000065353ab636500ffffffff024944c90100000000046300635369df9f01000000000000000000", "656a536551ab", 0, -1740151687, "935892c6f02948f3b08bcd463b6acb769b02c1912be4450126768b055e8f183a"], + ["2f7353dd02e395b0a4d16da0f7472db618857cd3de5b9e2789232952a9b154d249102245fd030000000151617fd88f103280b85b0a198198e438e7cab1a4c92ba58409709997cc7a65a619eb9eec3c0200000003636aabffffffff0397481c0200000000045300636a0dc97803000000000009d389030000000003ac6a53134007bb", "0000536552526a", 0, -1912746174, "30c4cd4bd6b291f7e9489cc4b4440a083f93a7664ea1f93e77a9597dab8ded9c"], + ["7d95473604fd5267d0e1bb8c9b8be06d7e83ff18ad597e7a568a0aa033fa5b4e1e2b6f1007020000000465006a6affffffffaee008503bfc5708bd557c7e78d2eab4878216a9f19daa87555f175490c40aaf000000000263abffffffffabd74f0cff6e7ceb9acc2ee25e65af1abcebb50c08306e6c78fa8171c37613dd010000000552acacababffffffff54a3069393f7930fa1b331cdff0cb945ec21c11d4605d8eedba1d3e094c6ae1f01000000026300ffffffff0182edeb050000000009526353ab5153530065a247e8cd", "51516aab00", 2, -426210430, "2707ca714af09494bb4cf0794abe33c6cba5f29891d619e76070269d1fa8e690"], + ["221d4718023d9ca9fe1af178dbfce02b2b369bf823ea3f43f00891b7fef98e215c06b94fdd000000000951005153ab000051acffffffffb1c7ad1c64b7441bf5e70cd0f6eb4ec96821d67fc4997d9e6dfdceadecd36dde01000000070051536a635153ffffffff04e883cd00000000000851ab536553ab0052bbb2f70400000000002f1b2e03000000000165259fcb00000000000010dbde99", "ab", 1, 665721280, "4abce77432a86dfe608e7c1646c18b5253a373392ff962e288e3ab96bba1ba1d"], + ["6f66c0b3013e6ae6aabae9382a4326df31c981eac169b6bc4f746edaa7fc1f8c796ef4e374000000000665ab6aabac6affffffff0191c8d6030000000002525300000000", "6a5352516a635352ab", 0, -1299629906, "48411efeb133c6b7fec4e7bdbe613f827093cb06ea0dbcc2ffcfde3a9ac4356c"], + ["89e7928c04363cb520eff4465251fd8e41550cbd0d2cdf18c456a0be3d634382abcfd4a2130200000006ac516a6a656355042a796061ed72db52ae47d1607b1ceef6ca6aea3b7eea48e7e02429f382b378c4e51901000000085351ab6352ab5252ffffffff53631cbda79b40183000d6ede011c778f70147dc6fa1aed3395d4ce9f7a8e69701000000096a6553ab52516a52abad0de418d80afe059aab5da73237e0beb60af4ac490c3394c12d66665d1bac13bdf29aa8000000000153f2b59ab6027a33eb040000000007005351ac5100ac88b941030000000003ab0052e1e8a143", "63656a", 0, 1258533326, "b575a04b0bb56e38bbf26e1a396a76b99fb09db01527651673a073a75f0a7a34"], + ["ca356e2004bea08ec2dd2df203dc275765dc3f6073f55c46513a588a7abcc4cbde2ff011c7020000000553525100003aefec4860ef5d6c1c6be93e13bd2d2a40c6fb7361694136a7620b020ecbaca9413bcd2a030000000965ac00536352535100ace4289e00e97caaea741f2b89c1143060011a1f93090dc230bee3f05e34fbd8d8b6c399010000000365526affffffff48fc444238bda7a757cb6a98cb89fb44338829d3e24e46a60a36d4e24ba05d9002000000026a53ffffffff03d70b440200000000056a6a526aac853c97010000000002515335552202000000000351635300000000", "0052", 3, -528192467, "fc93cc056c70d5e033933d730965f36ad81ef64f1762e57f0bc5506c5b507e24"], + ["82d4fa65017958d53e562fac073df233ab154bd0cf6e5a18f57f4badea8200b217975e31030200000004636aab51ac0891a204227cc9050000000006635200655365bfef8802000000000865650051635252acfc2d09050000000006ab65ac51516380195e030000000007ac52525352510063d50572", "53", 0, -713567171, "e095003ca82af89738c1863f0f5488ec56a96fb81ea7df334f9344fcb1d0cf40"], + ["75f6949503e0e47dd70426ef32002d6cdb564a45abedc1575425a18a8828bf385fa8e808e600000000036aabab82f9fd14e9647d7a1b5284e6c55169c8bd228a7ea335987cef0195841e83da45ec28aa2e0300000002516350dc6fe239d150efdb1b51aa288fe85f9b9f741c72956c11d9dcd176889963d699abd63f0000000001ab429a63f502777d20010000000007abac52ac516a53d081d9020000000003acac630c3cc3a8", "535152516551510000", 1, 973814968, "c6ec1b7cb5c16a1bfd8a3790db227d2acc836300534564252b57bd66acf95092"], + ["24f24cd90132b2162f938f1c22d3ca5e7daa83515883f31a61a5177aebf99d7db6bdfc398c010000000163ffffffff01d5562d0100000000016300000000", "5265ac5165ac5252ab", 0, 1055129103, "5eeb03e03806cd7bfd44bbba69c30f84c2c5120df9e68cd8facc605fcfbc9693"], + ["5ff2cac201423064a4d87a96b88f1669b33adddc6fa9acdc840c0d8a243671e0e6de49a5b00300000005ac6353655353b91db50180db5a03000000000663535151006a047a3aff", "52ab51ab5365005163", 0, -1336626596, "b8db8d57fe40ab3a99cf2f8ed57da7a65050fcc1d34d4280e25faf10108d3110"], + ["10011f150220ad76a50ccc7bb1a015eda0ff987e64cd447f84b0afb8dc3060bdae5b36a6900200000000ffffffff1e92dd814dfafa830187bc8e5b9258de2445ec07b02c420ee5181d0b203bb334000000000565ab536a65ffffffff0124e65401000000000800ab636553ab53ac00000000", "53abab0051", 0, 440222748, "c6675bf229737e005b5c8ffa6f81d9e2c4396840921b6151316f67c4315a4270"], + ["8b95ec900456648d820a9b8df1d8f816db647df8a8dc9f6e7151ebf6079d90ee3f6861352a02000000085200ab00ac535151ffffffff039b10b845f961225ac0bcaac4f5fe1991029a051aa3d06a3811b5762977a67403000000035252abffffffff8559d65f40d5e261f45aec8aad3d2c56c6114b22b26f7ee54a06f0881be3a7f5010000000765635252536363ffffffff38f8b003b50f6412feb2322b06b270197f81ad69c36af02ca5008b94eee5f650020000000165ffffffff01ae2b00010000000001638eb153a2", "0053ab5300ac53", 2, 1266056769, "205f3653f0142b35ce3ef39625442efebae98cde8cbf0516b97b51073bb0479f"], + ["babbb7ea01ab5d584727cb44393b17cf66521606dc81e25d85273be0d57bad43e8f6b6d43501000000036a656aba83a68803fb0f4a000000000005536353ab633fcfe4020000000009ac00acab6351006a65182a0c03000000000453ac5363bee74f44", "536a6a6a6365ac51ab", 0, -799187625, "3275e98dca37243b977525a07b5d8e369d6c3bdc08cb948029a635547d0d1a4e"], + ["e86a24bc03e4fae784cdf81b24d120348cb5e52d937cd9055402fdba7e43281e482e77a1c100000000046363006affffffffa5447e9bdcdab22bd20d88b19795d4c8fb263fbbf7ce8f4f9a85f865953a6325020000000663ac53535253ffffffff9f8b693bc84e0101fc73748e0513a8cecdc264270d8a4ee1a1b6717607ee1eaa00000000026a513417bf980158d82c020000000009005253005351acac5200000000", "6353516365536a6a", 2, -563792735, "508129278ef07b43112ac32faf00170ad38a500eed97615a860fd58baaad174b"], + ["53bd749603798ed78798ef0f1861b498fc61dcee2ee0f2b37cddb115b118e73bc6a5a47a0201000000096a63656a6aab6a000007ff674a0d74f8b4be9d2e8e654840e99d533263adbdd0cf083fa1d5dd38e44d2d163d900100000007abab5251ac6a51c8b6b63f744a9b9273ccfdd47ceb05d3be6400c1ed0f7283d32b34a7f4f0889cccf06be30000000009516a52636551ab516a9ac1fe63030c677e05000000000027bc610000000000086565636a635100526e2dc60200000000015300000000", "6552536a515351ab", 1, -1617066878, "fe516df92299e995b8e6489be824c6839543071ec5e9286060b2600935bf1f20"], + ["691bf9fc028ca3099020b79184e70039cf53b3c7b3fe695d661fd62d7b433e65feda2150610000000003ac63abffffffff2c814c15b142bc944192bddccb90a392cd05b968b599c1d8cd99a55a28a243fd0100000009ab5300526a5200abac98516a5803dfd3540500000000046552ac522838120100000000040053ab6a4409a903000000000665636a5300658759621b", "65ac5165ab", 0, -359941441, "d582c442e0ecc400c7ba33a56c93ad9c8cfd45af820350a13623594b793486f0"], + ["536bc5e60232eb60954587667d6bcdd19a49048d67a027383cc0c2a29a48b960dc38c5a0370300000005ac636300abffffffff8f1cfc102f39b1c9348a2195d496e602c77d9f57e0769dabde7eaaedf9c69e250100000006acabab6a6351ffffffff0432f56f0400000000046a5365517fd54b0400000000035265539484e4050000000003536a5376dc25020000000008ac536aab6aab536ab978e686", "ac0051006a006a006a", 0, -273074082, "f151f1ec305f698d9fdce18ea292b145a58d931f1518cf2a4c83484d9a429638"], + ["74606eba01c2f98b86c29ba5a32dc7a7807c2abe6ed8d89435b3da875d87c12ae05329e6070200000003510052ffffffff02a1e2c4020000000006516563526a63c68bae04000000000952ab6363ab00006363fe19ae4f", "63ababacac5365", 0, 112323400, "d1b1d79001b4a0324962607b739972d6f39c1493c4500ce814fd3bd72d32a5a0"], + ["2ed805e20399e52b5bcc9dc075dad5cf19049ff5d7f3de1a77aee9288e59c5f4986751483f020000000165ffffffff967531a5726e7a653a9db75bd3d5208fa3e2c5e6cd5970c4d3aba84eb644c72c0300000000ffffffffd79030d20c65e5f8d3c55b5692e5bdaa2ae78cfa1935a0282efb97515feac43f030000000400006365261ab88c02bdf66a000000000003ab6351d6ad8b000000000005525152abac00000000", "630053ab5265", 0, 2072814938, "1d25d16d84d5793be1ad5cda2de9c9cf70e04a66c3dae618f1a7ca4026198e7f"], + ["fab796ee03f737f07669160d1f1c8bf0800041157e3ac7961fea33a293f976d79ce49c02ab0200000003ac5252eb097ea1a6d1a7ae9dace338505ba559e579a1ee98a2e9ad96f30696d6337adcda5a85f403000000096500abab656a6a656396d5d41a9b11f571d91e4242ddc0cf2420eca796ad4882ef1251e84e42b930398ec69dd80100000005526551ac6a8e5d0de804f763bb0400000000015288271a010000000001acf2bf2905000000000300ab51c9641500000000000952655363636365ac5100000000", "00ac536552", 0, -1854521113, "f3bbab70b759fe6cfae1bf349ce10716dbc64f6e9b32916904be4386eb461f1f"], + ["f2b539a401e4e8402869d5e1502dbc3156dbce93583f516a4947b333260d5af1a34810c6a00200000003525363ffffffff01d305e2000000000005acab535200a265fe77", "", 0, -1435650456, "41617b27321a830c712638dbb156dae23d4ef181c7a06728ccbf3153ec53d7dd"], + ["9f10b1d8033aee81ac04d84ceee0c03416a784d1017a2af8f8a34d2f56b767aea28ff88c8f02000000025352ffffffff748cb29843bea8e9c44ed5ff258df1faf55fbb9146870b8d76454786c4549de100000000016a5ba089417305424d05112c0ca445bc7107339083e7da15e430050d578f034ec0c589223b0200000007abac53ac6565abffffffff025a4ecd010000000006636563ab65ab40d2700000000000056a6553526333fa296c", "", 0, -395044364, "20fd0eee5b5716d6cbc0ddf852614b686e7a1534693570809f6719b6fcb0a626"], + ["ab81755f02b325cbd2377acd416374806aa51482f9cc5c3b72991e64f459a25d0ddb52e66703000000036a00ab8727056d48c00cc6e6222be6608c721bc2b1e69d0ffbadd51d131f05ec54bcd83003aac5000000000003f2cdb60454630e020000000007526aac63000000e9e25c040000000003516a0088c97e0000000000076a535265655263771b5805000000000851ab00ac6565515100000000", "5151ab00ac", 0, -230931127, "ba0a2c987fcdd74b6915f6462f62c3f126a0750aa70048f7aa20f70726e6a20b"], + ["7a17e0ef0378dab4c601240639139335da3b7d684600fa682f59b7346ef39386fe9abd69350000000004ac5252ab807f26fb3249326813e18260a603b9ad66f41f05eaa8146f66bcca452162a502aac4aa8b02000000026a534ea460faa7e3d7854ec6c70d7e797025697b547ec500b2c09c873b4d5517767d3f3720660300000000ffffffff01b12e7a02000000000900ab006aab65656a63991c03e2", "6aab6a", 1, -1577994103, "62cd3413d9d819fb7355336365cf8a2a997f7436cc050a7143972044343b3281"], + ["ff2ecc09041b4cf5abb7b760e910b775268abee2792c7f21cc5301dd3fecc1b4233ee70a2c0200000009acac5300006a51526affffffffeb39c195a5426afff38379fc85369771e4933587218ef4968f3f05c51d6b7c92000000000165453a5f039b8dbef7c1ffdc70ac383b481f72f99f52b0b3a5903c825c45cfa5d2c0642cd50200000001654b5038e6c49daea8c0a9ac8611cfe904fc206dad03a41fb4e5b1d6d85b1ecad73ecd4c0102000000096a51000053ab656565bdb5548302cc719200000000000452655265214a3603000000000300ab6a00000000", "52516a006a63", 1, -2113289251, "37ed6fae36fcb3360c69cac8b359daa62230fc1419b2cf992a32d8f3e079dcff"], + ["70a8577804e553e462a859375957db68cfdf724d68caeacf08995e80d7fa93db7ebc04519d02000000045352ab53619f4f2a428109c5fcf9fee634a2ab92f4a09dc01a5015e8ecb3fc0d9279c4a77fb27e900000000006ab6a51006a6affffffff3ed1a0a0d03f25c5e8d279bb5d931b7eb7e99c8203306a6c310db113419a69ad010000000565516300abffffffff6bf668d4ff5005ef73a1b0c51f32e8235e67ab31fe019bf131e1382050b39a630000000004536a6563ffffffff02faf0bb00000000000163cf2b4b05000000000752ac635363acac15ab369f", "ac", 0, -1175809030, "1c9d6816c20865849078f9777544b5ddf37c8620fe7bd1618e4b72fb72dddca1"], + ["a3604e5304caa5a6ba3c257c20b45dcd468f2c732a8ca59016e77b6476ac741ce8b16ca8360200000004acac6553ffffffff695e7006495517e0b79bd4770f955040610e74d35f01e41c9932ab8ccfa3b55d0300000007ac5253515365acffffffff6153120efc5d73cd959d72566fc829a4eb00b3ef1a5bd3559677fb5aae116e38000000000400abab52c29e7abd06ff98372a3a06227386609adc7665a602e511cadcb06377cc6ac0b8f63d4fdb03000000055100acabacffffffff04209073050000000009ab5163ac525253ab6514462e05000000000952abacab636300656a20672c0400000000025153b276990000000000056565ab6a5300000000", "5351", 0, 1460890590, "249c4513a49076c6618aabf736dfd5ae2172be4311844a62cf313950b4ba94be"], + ["c6a72ed403313b7d027f6864e705ec6b5fa52eb99169f8ea7cd884f5cdb830a150cebade870100000009ac63ab516565ab6a51ffffffff398d5838735ff43c390ca418593dbe43f3445ba69394a6d665b5dc3b4769b5d700000000075265acab515365ffffffff7ee5616a1ee105fd18189806a477300e2a9cf836bf8035464e8192a0d785eea3030000000700ac6a51516a52ffffffff018075fd0000000000015100000000", "005251acac5252", 2, -656067295, "2cc1c7514fdc512fd45ca7ba4f7be8a9fe6d3318328bc1a61ae6e7675047e654"], + ["93c12cc30270fc4370c960665b8f774e07942a627c83e58e860e38bd6b0aa2cb7a2c1e060901000000036300abffffffff4d9b618035f9175f564837f733a2b108c0f462f28818093372eec070d9f0a5440300000001acffffffff039c2137020000000001525500990100000000055265ab636a07980e0300000000005ba0e9d1", "656a5100", 1, 18954182, "6beca0e0388f824ca33bf3589087a3c8ad0857f9fe7b7609ae3704bef0eb83e2"], + ["97bddc63015f1767619d56598ad0eb5c7e9f880b24a928fea1e040e95429c930c1dc653bdb0100000008ac53acac00005152aaa94eb90235ed10040000000000287bdd0400000000016a8077673a", "acac6a536352655252", 0, -813649781, "5990b139451847343c9bb89cdba0e6daee6850b60e5b7ea505b04efba15f5d92"], + ["cc3c9dd303637839fb727270261d8e9ddb8a21b7f6cbdcf07015ba1e5cf01dc3c3a327745d0300000000d2d7804fe20a9fca9659a0e49f258800304580499e8753046276062f69dbbde85d17cd2201000000096352536a520000acabffffffffbc75dfa9b5f81f3552e4143e08f485dfb97ae6187330e6cd6752de6c21bdfd21030000000600ab53650063ffffffff0313d0140400000000096565515253526aacac167f0a040000000008acab00535263536a9a52f8030000000006abab5151ab63f75b66f2", "6a635353636a65ac65", 1, 377286607, "dbc7935d718328d23d73f8a6dc4f53a267b8d4d9816d0091f33823bd1f0233e9"], + ["236f91b702b8ffea3b890700b6f91af713480769dda5a085ae219c8737ebae90ff25915a3203000000056300ac6300811a6a10230f12c9faa28dae5be2ebe93f37c06a79e76214feba49bb017fb25305ff84eb020000000100ffffffff041e351703000000000351ac004ff53e050000000003ab53636c1460010000000000cb55f701000000000651520051ab0000000000", "acac636a6aac5300", 0, 406448919, "793a3d3c37f6494fab79ff10c16702de002f63e34be25dd8561f424b0ea938c4"], + ["22e10d2003ab4ea9849a2801921113583b7c35c3710ff49a6003489395789a7cfb1e6051900100000006526a65535151ffffffff82f21e249ec60db33831d33b9ead0d56f6496db64337dcb7f1c3327c47729c4a020000000253abffffffff138f098f0e6a4cf51dc3e7a3b749f487d1ebde71b73b731d1d02ad1180ac7b8c02000000036563acda215011027a9484020000000007635165530000ac4bf6cb0400000000066aacabab65ab3ce3f32c", "ab0052ab", 2, 1136359457, "b5bd080bbcb8cd652f440484311d7a3cb6a973cd48f03c5c00fd6beb52dfc061"], + ["c47d5ad60485cb2f7a825587b95ea665a593769191382852f3514a486d7a7a11d220b62c54000000000663655253acab8c3cf32b0285b040e50dcf6987ddf7c385b3665048ad2f9317b9e0c5ba0405d8fde4129b00000000095251ab00ac65635300ffffffff549fe963ee410d6435bb2ed3042a7c294d0c7382a83edefba8582a2064af3265000000000152fffffffff7737a85e0e94c2d19cd1cde47328ece04b3e33cd60f24a8a345da7f2a96a6d0000000000865ab6a0051656aab28ff30d5049613ea020000000005ac51000063f06df1050000000008ac63516aabac5153afef5901000000000700656500655253688bc00000000000086aab5352526a53521ff1d5ff", "51ac52", 2, -1296011911, "0c1fd44476ff28bf603ad4f306e8b6c7f0135a441dc3194a6f227cb54598642a"], + ["0b43f122032f182366541e7ee18562eb5f39bc7a8e5e0d3c398f7e306e551cdef773941918030000000863006351ac51acabffffffffae586660c8ff43355b685dfa8676a370799865fbc4b641c5a962f0849a13d8250100000005abab63acabffffffff0b2b6b800d8e77807cf130de6286b237717957658443674df047a2ab18e413860100000008ab6aac655200ab63ffffffff04f1dbca03000000000800635253ab656a52a6eefd0300000000036365655d8ca90200000000005a0d530400000000015300000000", "65ac65acac", 0, 351448685, "86f26e23822afd1bdfc9fff92840fc1e60089f12f54439e3ab9e5167d0361dcf"], + ["4b0ecc0c03ba35700d2a30a71f28e432ff6ac7e357533b49f4e97cf28f1071119ad6b97f3e0300000008acab516363ac63acffffffffcd6a2019d99b5c2d639ddca0b1aa5ea7c1326a071255ea226960bd88f45ca57d00000000085253655363005353ffffffffba257635191c9f216de3277be548cb5a2313114cb1a4c563b03b4ef6c0f4f7040300000001abda542edf0495cdc40100000000026353c049e903000000000752516a53ab65512b0f9304000000000963ab516aac65516552fa9ece050000000009acab6500005152530000000000", "65ab51525352510052", 1, -1355414590, "3cd85f84aae6d702436f3f9b8980adcc1f8f202e957759540a27da0a32fc6c87"], + ["adaac0a803f66811346271c733036d6e0d45e15a9b602092e2e04ad93564f196e7f020b088000000000600526a636a00700ec3f9db07a3a6ce910bf318c7ec87a876e1f2a3366cc69f20cde09203b99c1cb9d15800000000050000ac636a4d0de554ebe95c6cc14faf5ff6361d1deba9474b8b0fd3b93c011cd96aec783abb3f36830200000005ab65005251ffffffff0464eb10050000000007520000ab6a65ab1beaa80300000000005a2f31050000000006526aab65ac52ba7db10000000000045251ab6a0cfb46e7", "ab0051ac52636a", 1, -184733716, "961ff413850336d3987c550404fc1d923266ca36cc9ffee7113edb3a9fea7f30"], + ["af1c4ab301ec462f76ee69ba419b1b2557b7ded639f3442a3522d4f9170b2d6859765c3df402000000016affffffff01a5ca6c000000000008ab52536aab00005300000000", "6a6351", 0, 110304602, "e88ed2eea9143f2517b15c03db00767eb01a5ce12193b99b964a35700607e5f4"], + ["0bfd34210451c92cdfa02125a62ba365448e11ff1db3fb8bc84f1c7e5615da40233a8cd368010000000252ac9a070cd88dec5cf9aed1eab10d19529720e12c52d3a21b92c6fdb589d056908e43ea910e0200000009ac516a52656a6a5165ffffffffc3edcca8d2f61f34a5296c405c5f6bc58276416c720c956ff277f1fb81541ddd00000000030063abffffffff811247905cdfc973d179c03014c01e37d44e78f087233444dfdce1d1389d97c302000000065163000063ab1724a26e02ca37c902000000000851ab53525352ac529012a90100000000085200525253535353fa32575b", "5352ac6351", 1, -1087700448, "b8f1e1f35e3e1368bd17008c756e59cced216b3c699bcd7bebdb5b6c8eec4697"], + ["2c84c0640487a4a695751d3e4be48019dbaea85a6e854f796881697383ea455347d2b2769001000000055265526500ffffffff6aac176d8aa00778d496a7231eeb7d3334f20c512d3db1683276402100d98de5030000000700536a5263526ac1ee9ceb171c0c984ebaf12c234fd1487fbf3b3d73aa0756907f26837efba78d1bed33200300000001ab4d9e8ec0bed837cb929bbed76ee848959cec59de44bd7667b7631a744f880d5c71a20cfd0100000007005363515300abffffffff023753fb0000000000036565532d3873050000000009005152ab6a63acab5200000000", "ab650053ab", 0, -877941183, "c49af297dffe2d80deddf10ceea84b99f8554bd2d55bbdc34e449728c31f0835"], + ["1f7e4b1b045d3efa6cd7a11d7873a8bab886c19bd11fcb6712f0948f2db3a7be76ff76c8f100000000095265ab6a0065ac5363ffffffffdaafcfa6029336c997680a541725190f09a6f6da21e54560eca4b5b8ae987da1000000000952ac52acac52515165ffffffff825a38d3b1e5bb4d10f33653ab3ab6882c7abdaec74460257d1528ce7be3f98e0100000007526a006a656a63c14adc8f04953a5d3d3f89237f38b857dd357713896d36215f7e8b77b11d98ea3cdc93df02000000015212484f6104bfafae0300000000025263a2b0120000000000056563ab00516c4d2605000000000653ac6500655301cc93030000000002acab14643b1f", "63acac53ab", 0, 333824258, "18da6ceb011cd36f15ad7dd6c55ef07e6f6ed48881ce3bb31416d3c290d9a0e9"], + ["467a3e7602e6d1a7a531106791845ec3908a29b833598e41f610ef83d02a7da3a1900bf2960000000005ab6a636353ffffffff031db6dac6f0bafafe723b9199420217ad2c94221b6880654f2b35114f44b1df010000000965ab52636a63ac6352ffffffff02b3b95c0100000000026300703216030000000001ab3261c0aa", "6a", 0, 2110869267, "3078b1d1a7713c6d101c64afe35adfae0977a5ab4c7e07a0b170b041258adbf2"], + ["8713bc4f01b411149d575ebae575f5dd7e456198d61d238695df459dd9b86c4e3b2734b62e0300000004abac6363ffffffff03b58049050000000002ac653c714c04000000000953656a005151526a527b5a9e03000000000652ac5100525300000000", "52", 0, -647281251, "0e0bed1bf2ff255aef6e5c587f879ae0be6222ab33bd75ee365ec6fbb8acbe38"], + ["f2ba8a8701b9c401efe3dd0695d655e20532b90ac0142768cee4a3bb0a89646758f544aa8102000000036a52527899f4e4040c6f0b030000000008636565ab530051ab52b60c000000000009515200ab630053ac53a49c5f040000000008ab53ab516300ab63fa27340300000000015100000000", "ac63abab5251", 0, -1328936437, "ab61497afd39e61fe06bc5677326919716f9b20083c9f3417dcea905090e0411"], + ["b5a7df6102107beded33ae7f1dec0531d4829dff7477260925aa2cba54119b7a07d92d5a1d02000000046a516a52803b625c334c1d2107a326538a3db92c6c6ae3f7c3516cd90a09b619ec6f58d10e77bd6703000000056563006a63ffffffff0117484b03000000000853acab52526a65abc1b548a1", "ac006a525100", 0, 2074359913, "680336db57347d8183b8898cd27a83f1ba5884155aeae5ce20b4840b75e12871"], + ["278cb16204b9dadf400266106392c4aa9df01ba03af988c8139dae4c1818ac009f13fc5f1a00000000065200ac656a52ffffffffd006bbebd8cbd7bdead24cddc9badfcc6bc0c2e63c037e5c29aa858f5d0f3e7d01000000046a0051acffffffffbc62a5f57e58da0b67956003ae81ac97cb4cbd1d694c914fc41515c008c4d8fd020000000165e329c844bcc16164be64b64a81cbf4ffd41ed2934e0daa0040ccb8365bab0b2a9e401c180300000003ab52abffffffff02588460030000000000a25a12030000000005535100005300000000", "6553ab6a5300acab51", 3, 989407546, "1c29f110576f4a3b257f67454d99dfc0dee62ef5517ca702848ce4bd2ea1a1d7"], + ["49eb2178020a04fca08612c34959fd41447319c190fb7ffed9f71c235aa77bec28703aa1820200000003ac6353abaff326071f07ec6b77fb651af06e8e8bd171068ec96b52ed584de1d71437fed186aecf0300000001acffffffff03da3dbe02000000000652ac63ac6aab8f3b680400000000096a536a65636a53516a5175470100000000016500000000", "6a536365", 0, 1283691249, "c670219a93234929f662ecb9aa148a85a2d281e83f4e53d10509461cdea47979"], + ["0f96cea9019b4b3233c0485d5b1bad770c246fe8d4a58fb24c3b7dfdb3b0fd90ea4e8e947f0300000006006a5163515303571e1e01906956030000000005ab635353abadc0fbbe", "acac", 0, -1491469027, "716a8180e417228f769dcb49e0491e3fda63badf3d5ea0ceeac7970d483dd7e2"], + ["9a7d858604577171f5fe3f3fd3e5e039c4b0a06717a5381e9977d80e9f53e025e0f16d2877020000000752636565536353ffffffff5862bd028e8276e63f044be1dddcbb8d0c3fa097678308abf2b0f45104a93dbd0100000001531200667ba8fdd3b28e98a35da73d3ddfe51e210303d8eb580f923de988ee632d77793892030000000752526363526563ffffffffe9744eb44db2658f120847c77f47786d268c302120d269e6004455aa3ea5f5e20200000009ab6300636aab656551ffffffff03c61a3c020000000009ab516a6aab6aab53ab737f1a05000000000853acabab655365ab92a4a00400000000016367edf6c8", "535352ab", 3, 659348595, "d36ee79fc80db2e63e05cdc50357d186181b40ae20e3720878284228a13ee8b3"], + ["148e68480196eb52529af8e83e14127cbfdbd4a174e60a86ac2d86eac9665f46f4447cf7aa01000000045200ac538f8f871401cf240c0300000000065252ab52656a5266cf61", "", 0, -344314825, "eacc47c5a53734d6ae3aedbc6a7c0a75a1565310851b29ef0342dc4745ceb607"], + ["e2bc29d4013660631ba14ecf75c60ec5e9bed7237524d8c10f66d0675daa66d1492cb834530200000004ac510065e42d0c9e04f2b26c01000000000951525152acac65ababa35b7504000000000953ac6aac00650053ab94688c0400000000056365526553a1bced0300000000016a00000000", "65ab0063655353", 0, -888431789, "59a34b3ed3a1cce0b104de8f7d733f2d386ffc7445efae67680cd90bc915f7e0"], + ["0c8a70d70494dca6ab05b2bc941b5b431c43a292bd8f2f02eab5e240a408ca73a676044a4103000000056a51ab006affffffff84496004e54836c035821f14439149f22e1db834f315b24588ba2f031511926c0100000000ffffffffbbc5e70ed1c3060ba1bfe99c1656a3158a7307c3ce8eb362ec32c668596d2bd30000000009636563635351abab00b039344c6fc4f9bec24322e45407af271b2d3dfec5f259ee2fc7227bc5285e22b3be85b40100000009ac00ab53abac6a5352e5ddfcff02d50231020000000005006a51536ab086d9020000000006ababac51ac6a00000000", "abab636565acac6a", 3, 241546088, "643a7b4c8d832e14d5c10762e74ec84f2c3f7ed96c03053157f1bed226614911"], + ["f98f79cf0274b745e1d6f36da7cbe205a79132a7ad462bdc434cfb1dcd62a6977c3d2a5dbc010000000553516a5365ffffffff4f89f485b53cdad7fb80cc1b7e314b9735b9383bc92c1248bb0e5c6173a55c0d010000000353655293f9b014045ad96d02000000000963ac526a53ac636365f4c27904000000000952536563635152526a2788f0030000000002516aff5add01000000000863530051655351abd04716ba", "ab6552536a53", 1, -2128899945, "56d29f5e300ddfed2cd8dcce5d79826e193981d0b70dc7487772c8a0b3b8d7b1"], + ["6c7913f902aa3f5f939dd1615114ce961beda7c1e0dd195be36a2f0d9d047c28ac62738c3a020000000453abac00ffffffff477bf2c5b5c6733881447ac1ecaff3a6f80d7016eee3513f382ad7f554015b970100000007ab6563acab5152ffffffff04e58fe1040000000009ab00526aabab526553e59790010000000002ab525a834b03000000000035fdaf0200000000086551ac65515200ab00000000", "63ac53", 1, 1285478169, "1536da582a0b6de017862445e91ba14181bd6bf953f4de2f46b040d351a747c9"], + ["4624aa9204584f06a8a325c84e3b108cafb97a387af62dc9eab9afd85ae5e2c71e593a3b690200000003636a005eb2b44eabbaeca6257c442fea00107c80e32e8715a1293cc164a42e62ce14fea146220c020000000090b9ee38106e3310037bfc519fd209bdbd21c588522a0e96df5fba4e979392bc993bfe9f01000000086363636a635353ab6f1907d218ef6f3c729d9200e23c1dbff2df58b8b1282c6717b26cf760ee4c880d23f4d100000000086a516a536a525163ffffffff01d6f162050000000000ebbab208", "525365ab0053", 1, -1515409325, "6cf9cd409b7185b1f118171f0a34217af5b612ea54195ea186505b667c19337f"], + ["16562fc503f1cf9113987040c408bfd4523f1512da699a2ca6ba122dc65677a4c9bf7763830000000003636552ffffffff1ec1fab5ff099d1c8e6b068156f4e39b5543286bab53c6d61e2582d1e07c96cf02000000045163656affffffffd0ef40003524d54c08cb4d13a5ee61c84fbb28cde9eca7a6d11ba3a9335d8c620100000007635153536a6300fbb84fc2012003a601000000000363ab6a00000000", "63636a006a6aab", 0, -1310262675, "1efbf3d37a92bc03d9eb950b792f307e95504f7c4998f668aa250707ebb752ac"], + ["531665d701f86bacbdb881c317ef60d9cd1baeffb2475e57d3b282cd9225e2a3bf9cbe0ded01000000086300ac515263acabffffffff0453a8500100000000086353acab516a6565e5e9200500000000026a52a44caa00000000000453ac000065e41b0500000000076500ac0065526ab4476f4d", "006563006aab00636a", 0, 1770013777, "0898b26dd3ca08632a5131fa48eb55b44386d0c5070c24d6e329673d5e3693b8"], + ["0f1227a20140655a3da36e413b9b5d108a866f6f147eb4940f032f5a89854eae6d7c3a91600100000009525363515153515253e37a79480161ab61020000000001ab00000000", "ab65005200", 0, -1996383599, "979782dc3f36d908d37d7e4046a38d306b4b08ddc60a5eba355fe3d6da1b29a9"], + ["063ff6eb01aff98d0d2a6db224475010edb634c2f3b46257084676adeb84165a4ff8558d7601000000066353006a5165deb3262c042d109c0000000000076363ab52ac005200b9c4050000000007516300ac510063cfffc800000000000200639e815501000000000700526a52ac6365ac7b07b8", "656552abac6500", 0, -1559847112, "674a4bcb04247f8dc98780f1792cac86b8aee41a800fc1e6f5032f6e1dccde65"], + ["3320f6730132f830c4681d0cae542188e4177cad5d526fae84565c60ceb5c0118e844f90bd030000000163ffffffff0257ec5a040000000005525251ac6538344d000000000002515200000000", "5352656a53ac516a65", 0, 788050308, "3afacaca0ef6be9d39e71d7b1b118994f99e4ea5973c9107ca687d28d8eba485"], + ["c13aa4b702eedd7cde09d0416e649a890d40e675aa9b5b6d6912686e20e9b9e10dbd40abb1000000000863ab6353515351ac11d24dc4cc22ded7cdbc13edd3f87bd4b226eda3e4408853a57bcd1becf2df2a1671fd1600000000045165516affffffff01baea300100000000076aab52ab53005300000000", "0065", 0, -1195908377, "241a23e7b1982d5f78917ed97a8678087acbbffe7f624b81df78a5fe5e41e754"], + ["d9a6f20e019dd1b5fae897fb472843903f9c3c2293a0ffb59cff2b413bae6eceab574aaf9d030000000663ab006a515102f54939032df5100100000000056a51ab65530ec28f010000000004ac5100007e874905000000000651005265ac6a00000000", "abacab63acacabab", 0, 271463254, "1326a46f4c21e7619f30a992719a905aa1632aaf481a57e1cbd7d7c22139b41e"], + ["157c81bf0490432b3fcb3f9a5b79e5f91f67f05efb89fa1c8740a3fe7e9bdc18d7cb6acd2203000000026351ffffffff912e48e72bbcf8a540b693cf8b028e532a950e6e63a28801f6eaad1afcc52ad00000000000b1a4b170a2b9e60e0cad88a0085137309f6807d25d5afb5c1e1d32aa10ba1cdf7df596dd0000000009525165656a51ab65ab3674fba32a76fe09b273618d5f14124465933f4190ba4e0fd09d838daafc6223b31642ac00000000086a53536551ac6565ffffffff01fe9fb6030000000008ab51656a5165636a00000000", "ab00ab6a6551", 3, -64357617, "1ddaab7f973551d71f16bd70c4c4edbf7225e64e784a6da0ee7f7a9fe4f12a0b"], + ["a2692fff03b2387f5bacd5640c86ba7df574a0ee9ed7f66f22c73cccaef3907eae791cbd230200000004536363abffffffff4d9fe7e5b375de88ba48925d9b2005447a69ea2e00495a96eafb2f144ad475b40000000008000053000052636537259bee3cedd3dcc07c8f423739690c590dc195274a7d398fa196af37f3e9b4a1413f810000000006ac63acac52abffffffff04c65fe60200000000075151536365ab657236fc020000000009005263ab00656a6a5195b8b6030000000007ac5165636aac6a7d7b66010000000002acab00000000", "51", 2, -826546582, "925037c7dc7625f3f12dc83904755a37016560de8e1cdd153c88270a7201cf15"], + ["2c5b003201b88654ac2d02ff6762446cb5a4af77586f05e65ee5d54680cea13291efcf930d0100000005ab536a006a37423d2504100367000000000004536a515335149800000000000152166aeb03000000000452510063226c8e03000000000000000000", "635251", 0, 1060344799, "7e058ca5dd07640e4aae7dea731cfb7d7fef1bfd0d6d7b6ce109d041f4ca2a31"], + ["f981b9e104acb93b9a7e2375080f3ea0e7a94ce54cd8fb25c57992fa8042bdf4378572859f0100000002630008604febba7e4837da77084d5d1b81965e0ea0deb6d61278b6be8627b0d9a2ecd7aeb06a0300000005ac5353536a42af3ef15ce7a2cd60482fc0d191c4236e66b4b48c9018d7dbe4db820f5925aad0e8b52a0300000008ab0063510052516301863715efc8608bf69c0343f18fb81a8b0c720898a3563eca8fe630736c0440a179129d03000000086aac6a52ac6a63ac44fec4c00408320a03000000000062c21c030000000007ac6a655263006553835f0100000000015303cd60000000000005535263536558b596e0", "00", 0, -2140385880, "49870a961263354c9baf108c6979b28261f99b374e97605baa532d9fa3848797"], + ["e7416df901269b7af14a13d9d0507709b3cd751f586ce9d5da8d16a121e1bd481f5a086e1103000000056aab005200ffffffff01aa269c040000000006acac6a6a5263ee718de6", "ab525363", 0, 1309186551, "eea7d2212bda2d408fff146f9ae5e85e6b640a93b9362622bb9d5e6e36798389"], + ["402a815902193073625ab13d876190d1bbb72aecb0ea733c3330f2a4c2fe6146f322d8843a0300000008656aab0000535363fffffffff9dccdec5d8509d9297d26dfcb1e789cf02236c77dc4b90ebccbf94d1b5821150300000001510bf1f96a03c5c145000000000002ac6ae11b1c0100000000055163516a5239c8a600000000000365636300000000", "63536aacab", 0, -1811424955, "0090803a20102a778ab967a74532faee13e03b702083b090b1497bc2267ee2fe"], + ["c4b702e502f1a54f235224f0e6de961d2e53b506ab45b9a40805d1dacd35148f0acf24ca5e00000000085200ac65ac53acabf34ba6099135658460de9d9b433b84a8562032723635baf21ca1db561dce1c13a06f4407000000000851ac006a63516aabffffffff02a853a603000000000163d17a67030000000005ab63006a5200000000", "ac5363515153", 1, 480734903, "5c46f7ac3d6460af0da28468fcc5b3c87f2b9093d0f837954b7c8174b4d7b6e7"], + ["9b83f78704f492b9b353a3faad8d93f688e885030c274856e4037818848b99e490afef27770200000000ffffffff36b60675a5888c0ef4d9e11744ecd90d9fe9e6d8abb4cff5666c898fdce98d9e00000000056aab656352596370fca7a7c139752971e169a1af3e67d7656fc4fc7fd3b98408e607c2f2c836c9f27c030000000653ac51ab6300a0761de7e158947f401b3595b7dc0fe7b75fa9c833d13f1af57b9206e4012de0c41b8124030000000953656a53ab53510052242e5f5601bf83b301000000000465516a6300000000", "63515200ac656365", 3, -150879312, "9cf05990421ea853782e4a2c67118e03434629e7d52ab3f1d55c37cf7d72cdc4"], + ["f492a9da04f80b679708c01224f68203d5ea2668b1f442ebba16b1aa4301d2fe5b4e2568f3010000000953005351525263ab65ffffffff93b34c3f37d4a66df255b514419105b56d7d60c24bf395415eda3d3d8aa5cd0101000000020065ffffffff9dba34dabdc4f1643b372b6b77fdf2b482b33ed425914bb4b1a61e4fad33cf390000000002ab52ffffffffbbf3dc82f397ef3ee902c5146c8a80d9a1344fa6e38b7abce0f157be7adaefae0000000009515351005365006a51ffffffff021359ba010000000000403fea0200000000095200ac6353abac635300000000", "00ac51acacac", 0, -2115078404, "fd44fc98639ca32c927929196fc3f3594578f4c4bd248156a25c04a65bf3a9f3"], + ["2f73e0b304f154d3a00fde2fdd40e791295e28d6cb76af9c0fd8547acf3771a02e3a92ba37030000000852ac6351ab6565639aa95467b065cec61b6e7dc4d6192b5536a7c569315fb43f470078b31ed22a55dab8265f02000000080065636a6aab6a53ffffffff9e3addbff52b2aaf9fe49c67017395198a9b71f0aa668c5cb354d06c295a691a0100000000ffffffff45c2b4019abaf05c5e484df982a4a07459204d1343a6ee5badade358141f8f990300000007ac516a6aacac6308655cd601f3bc2f0000000000015200000000", "", 0, -2082053939, "9a95e692e1f78efd3e46bb98f178a1e3a0ef60bd0301d9f064c0e5703dc879c2"], + ["5a60b9b503553f3c099f775db56af3456330f1e44e67355c4ab290d22764b9144a7b5f959003000000030052acbd63e0564decc8659aa53868be48c1bfcda0a8c9857b0db32a217bc8b46d9e7323fe9649020000000553ac6551abd0ecf806211db989bead96c09c7f3ec5f73c1411d3329d47d12f9e46678f09bac0dc383e0200000000ffffffff01494bb202000000000500516551ac00000000", "ac", 0, 1169947809, "62a36c6e8da037202fa8aeae03e533665376d5a4e0a854fc4624a75ec52e4eb1"], + ["7e98d353045569c52347ca0ff2fdba608829e744f61eb779ffdb5830aae0e6d6857ab2690e03000000075365acab656352ffffffffa890dd37818776d12da8dca53d02d243ef23b4535c67016f4c58103eed85360f030000000093dbacdc25ca65d2951e047d6102c4a7da5e37f3d5e3c8b87c29b489360725dcd117ee2003000000056a6300ac53c7e99fa1dc2b8b51733034e6555f6d6de47dbbf1026effac7db80cb2080678687380dc1e02000000075352005263516affffffff04423272040000000008ab6353ab65510051e0f53b0500000000086300516552635152f74a5f04000000000853acab0053ab52ab0e8e5f00000000000951ac5363516a6aabab00000000", "6a5163ab52", 3, 890006103, "476868cecd1763c91dade98f17defa42d31049547df45acffa1cc5ae5c3d75d6"], + ["e3649aa40405e6ffe377dbb1bbbb672a40d8424c430fa6512c6165273a2b9b6afa9949ec430200000007630052ab655153a365f62f2792fa90c784efe3f0981134d72aac0b1e1578097132c7f0406671457c332b84020000000353ab6ad780f40cf51be22bb4ff755434779c7f1def4999e4f289d2bd23d142f36b66fbe5cfbb4b01000000076a5252abac52ab1430ffdc67127c9c0fc97dcd4b578dab64f4fb9550d2b59d599773962077a563e8b6732c02000000016affffffff04cb2687000000000002ab636e320904000000000252acf70e9401000000000100dc3393050000000006ab0063536aacbc231765", "65520053", 3, -2016196547, "f64f805f0ff7f237359fa6b0e58085f3c766d1859003332223444fd29144112a"], + ["1d033569040700441686672832b531ab55db89b50dc1f9fc00fb72218b652da9dcfbc83be901000000066551ac526a632b390f9ad068e5fdee6563e88e2a8e4e09763c861072713dc069893dc6bbc9db3f00e26502000000096a5363526565525252ffffffff8a36bdd0aaf38f6707592d203e14476ca9f259021e487135c7e8324244057ed90300000000ed3fb2a3dfd4d46b5f3603fe0148653911988457bd0ed7f742b07c452f5476c228ff9f600200000007526aac00525152ffffffff04b88e48030000000000c753d602000000000853510000006553518fda2603000000000853ac52acac5263534839f1030000000006ac006aacac5300000000", "516553635300ab0052", 1, 2075958316, "c2cefaec2293134acbcf6d2a8bf2b3eb42e4ec04ee8f8bf30ff23e65680677c1"], + ["4c4be7540344050e3044f0f1d628039a334a7c1f7b4573469cfea46101d6888bb6161fe9710200000000ffffffffac85a4fdad641d8e28523f78cf5b0f4dc74e6c5d903c10b358dd13a5a1fd8a06000000000163e0ae75d05616b72467b691dc207fe2e65ea35e2eadb7e06ea442b2adb9715f212c0924f10200000000ffffffff0194ddfe02000000000265ac00000000", "00006500", 1, -479922562, "d66924d49f03a6960d3ca479f3415d638c45889ce9ab05e25b65ac260b51d634"], + ["202c18eb012bc0a987e69e205aea63f0f0c089f96dd8f0e9fcde199f2f37892b1d4e6da90302000000055352ac6565ffffffff0257e5450100000000025300ad257203000000000000000000", "520052ac6a005265", 0, 168054797, "502967a6f999f7ee25610a443caf8653dda288e6d644a77537bcc115a8a29894"], + ["32fa0b0804e6ea101e137665a041cc2350b794e59bf42d9b09088b01cde806ec1bbea077df0200000008515153650000006506a11c55904258fa418e57b88b12724b81153260d3f4c9f080439789a391ab147aabb0fa0000000007000052ac51ab510986f2a15c0d5e05d20dc876dd2dafa435276d53da7b47c393f20900e55f163b97ce0b800000000008ab526a520065636a8087df7d4d9c985fb42308fb09dce704650719140aa6050e8955fa5d2ea46b464a333f870000000009636300636a6565006affffffff01994a0d040000000002536500000000", "516563530065", 2, -163068286, "f58637277d2bc42e18358dc55f7e87e7043f5e33f4ce1fc974e715ef0d3d1c2a"], + ["ae23424d040cd884ebfb9a815d8f17176980ab8015285e03fdde899449f4ae71e04275e9a80100000007ab006553530053ffffffff018e06db6af519dadc5280c07791c0fd33251500955e43fe4ac747a4df5c54df020000000251ac330e977c0fec6149a1768e0d312fdb53ed9953a3737d7b5d06aad4d86e9970346a4feeb5030000000951ab51ac6563ab526a67cabc431ee3d8111224d5ecdbb7d717aa8fe82ce4a63842c9bd1aa848f111910e5ae1eb0100000004ac515300bfb7e0d7048acddc030000000009636a5253636a655363a3428e040000000001525b99c6050000000004655265ab717e6e020000000000d99011eb", "ac6a6a516565", 1, -716251549, "b098eb9aff1bbd375c70a0cbb9497882ab51f3abfebbf4e1f8d74c0739dc7717"], + ["030f44fc01b4a9267335a95677bd190c1c12655e64df74addc53b753641259af1a54146baa020000000152e004b56c04ba11780300000000026a53f125f001000000000251acd2cc7c03000000000763536563655363c9b9e50500000000015200000000", "ac", 0, -1351818298, "19dd32190ed2a37be22f0224a9b55b91e37290577c6c346d36d32774db0219a3"], + ["c05f448f02817740b30652c5681a3b128322f9dc97d166bd4402d39c37c0b14506d8adb5890300000003536353ffffffffa188b430357055ba291c648f951cd2f9b28a2e76353bef391b71a889ba68d5fc02000000056565526a6affffffff02745f73010000000001ab3ec34c0400000000036aac5200000000", "516551510053", 0, -267877178, "3a1c6742d4c374f061b1ebe330b1e169a113a19792a1fdde979b53e094cc4a3c"], + ["163ba45703dd8c2c5a1c1f8b806afdc710a2a8fc40c0138e2d83e329e0e02a9b6c837ff6b8000000000700655151ab6a522b48b8f134eb1a7e6f5a6fa319ce9d11b36327ba427b7d65ead3b4a6a69f85cda8bbcd22030000000563656552acffffffffdbcf4955232bd11eef0cc6954f3f6279675b2956b9bcc24f08c360894027a60201000000066500006500abffffffff04d0ce9d0200000000008380650000000000015233f360040000000003006aabedcf0801000000000000000000", "000065006500ac", 0, 216965323, "9afe3f4978df6a86e9a8ebd62ef6a9d48a2203f02629349f1864ef2b8b92fd55"], + ["07f7f5530453a12ad0c7eb8fbc3f140c7ab6818144d67d2d8752600ca5d9a9358e2dff87d4000000000663526aab526a9e599c379d455e2da36d0cde88d931a863a3e97e01e93b9edb65856f3d958dc08b92b720000000000165bbc8d66dae3b1b170a6e2457f5b161465cb8706e0e6ffc6af55deb918365f14c5f40d4890100000000a7bd77c069ee4b48638e2363fcf2a86b02bea022047bd9fcb16d2b94ad068308d19b31cb00000000066aab5300ab529672aa8f01dbd8a205000000000663536353006a02e99901", "ac006351006a63ab63", 1, 119789359, "6629a1e75c6ae8f4f9d5f734246b6a71682a5ea57246040ef0584f6b97916175"], + ["fe647f950311bf8f3a4d90afd7517df306e04a344d2b2a2fea368935faf11fa6882505890d0000000005ab5100516affffffff43c140947d9778718919c49c0535667fc6cc727f5876851cb8f7b6460710c7f60100000000ffffffffce4aa5d90d7ab93cbec2e9626a435afcf2a68dd693c15b0e1ece81a9fcbe025e0300000000ffffffff02f34806020000000002515262e54403000000000965635151ac655363636de5ce24", "6a005100ac516351", 2, 989643518, "818a7ceaf963f52b5c48a7f01681ac6653c26b63a9f491856f090d9d60f2ffe3"], + ["a1050f8604d0f9d2feefcdb5051ae0052f38e21bf39daf583fd0c3900faa3eab5d431c0bbe030000000653536a005151683d27e5c6e0da8f22125823f32d5d98477d8098ef36263b9694d61d4d85d3f2ac02b7570200000007000052005165abffffffff0cad981542bcb54a87d9400aa63e514c7c6fab7158c2b1fb37821ea755eb162a0200000000b94feb5100e5ef3bf8ed8d43356c8a8d5ac6c7e80d7ff6040f4f0aa19abbe783f4f461240200000007636500000052655686fd70042be3ad02000000000465ab636a15680b000000000004acac53511277c705000000000452635252d27a0102000000000000000000", "6a6aacab65655251", 1, -982144648, "dfcf484111801989eb6df8dc2bafb944d7365ffeb36a575a08f3270d3ef24c9f"], + ["cef7316804c3e77fe67fc6207a1ea6ae6eb06b3bf1b3a4010a45ae5c7ad677bb8a4ebd16d90200000009ac536a5152ac5263005301ab8a0da2b3e0654d31a30264f9356ba1851c820a403be2948d35cafc7f9fe67a06960300000006526a63636a53ffffffffbada0d85465199fa4232c6e4222df790470c5b7afd54704595a48eedd7a4916b030000000865ab63ac006a006ab28dba4ad55e58b5375053f78b8cdf4879f723ea4068aed3dd4138766cb4d80aab0aff3d0300000003ac6a00ffffffff010f5dd6010000000006ab006aab51ab00000000", "", 1, 889284257, "d0f32a6db43378af84b063a6706d614e2d647031cf066997c48c04de3b493a94"], + ["7b3ff28004ba3c7590ed6e36f45453ebb3f16636fe716acb2418bb2963df596a50ed954d2e03000000065251515265abffffffff706ee16e32e22179400c9841013971645dabf63a3a6d2d5feb42f83aa468983e030000000653ac51ac5152ffffffffa03a16e5e5de65dfa848b9a64ee8bf8656cc1f96b06a15d35bd5f3d32629876e020000000043c1a3965448b3b46f0f0689f1368f3b2981208a368ec5c30defb35595ef9cf95ffd10e902000000036aac65253a5bbe042e907204000000000800006565656352634203b4020000000002656336b3b7010000000001ab7a063f0100000000026500a233cb76", "006551636a53ac5251", 1, -1144216171, "68c7bd717b399b1ee33a6562a916825a2fed3019cdf4920418bb72ffd7403c8c"], + ["d5c1b16f0248c60a3ddccf7ebd1b3f260360bbdf2230577d1c236891a1993725e262e1b6cb000000000363636affffffff0a32362cfe68d25b243a015fc9aa172ea9c6b087c9e231474bb01824fd6bd8bc0300000005ab52ab516affffffff0420d9a70200000000045152656a45765d0000000000055252536a5277bad100000000000252ab3f3f3803000000000463acac5200000000", "52636a52ab65", 1, 1305123906, "978dc178ecd03d403b048213d904653979d11c51730381c96c4208e3ea24243a"], + ["1be8ee5604a9937ebecffc832155d9ba7860d0ca451eaced58ca3688945a31d93420c27c460100000006abac5300535288b65458af2f17cbbf7c5fbcdcfb334ffd84c1510d5500dc7d25a43c36679b702e850f7c0200000003005300ffffffff7c237281cb859653eb5bb0a66dbb7aeb2ac11d99ba9ed0f12c766a8ae2a2157203000000086aabac526365acabfffffffff09d3d6639849f442a6a52ad10a5d0e4cb1f4a6b22a98a8f442f60280c9e5be80200000007ab00ab6565ab52ffffffff0398fe83030000000005526aababacbdd6ec010000000005535252ab6a82c1e6040000000001652b71c40c", "6563526353656351", 2, -853634888, "0d936cceda2f56c7bb87d90a7b508f6208577014ff280910a710580357df25f3"], + ["9e0f99c504fbca858c209c6d9371ddd78985be1ab52845db0720af9ae5e2664d352f5037d4010000000552ac53636affffffff0e0ce866bc3f5b0a49748f597c18fa47a2483b8a94cef1d7295d9a5d36d31ae7030000000663515263ac635bb5d1698325164cdd3f7f3f7831635a3588f26d47cc30bf0fefd56cd87dc4e84f162ab702000000036a6365ffffffff85c2b1a61de4bcbd1d5332d5f59f338dd5e8accbc466fd860f96eef1f54c28ec030000000165ffffffff04f5cabd010000000007000052ac526563c18f1502000000000465510051dc9157050000000008655363ac525253ac506bb600000000000865656a53ab63006a00000000", "006a6a0052", 0, 1186324483, "2f9b7348600336512686e7271c53015d1cb096ab1a5e0bce49acd35bceb42bc8"], + ["11ce51f90164b4b54b9278f0337d95c50d16f6828fcb641df9c7a041a2b274aa70b1250f2b0000000008ab6a6a65006551524c9fe7f604af44be050000000005525365006521f79a0300000000015306bb4e04000000000265ac99611a05000000000765acab656500006dc866d0", "", 0, -1710478768, "cfa4b7573559b3b199478880c8013fa713ca81ca8754a3fd68a6d7ee6147dc5a"], + ["86bc233e02ba3c647e356558e7252481a7769491fb46e883dd547a4ce9898fc9a1ca1b77790000000006ab5351abab51f0c1d09c37696d5c7c257788f5dff5583f4700687bcb7d4acfb48521dc953659e325fa390300000003acac5280f29523027225af03000000000963abac0065ab65acab7e59d90400000000016549dac846", "53006aac52acac", 0, 711159875, "880330ccde00991503ea598a6dfd81135c6cda9d317820352781417f89134d85"], + ["beac155d03a853bf18cd5c490bb2a245b3b2a501a3ce5967945b0bf388fec2ba9f04c03d68030000000012fe96283aec4d3aafed8f888b0f1534bd903f9cd1af86a7e64006a2fa0d2d30711af770010000000163ffffffffd963a19d19a292104b9021c535d3e302925543fb3b5ed39fb2124ee23a9db00302000000056500ac63acffffffff01ad67f503000000000300ac5189f78db2", "53536a636500", 2, 748992863, "bde3dd0575164d7ece3b5783ce0783ffddb7df98f178fe6468683230314f285a"], + ["81dab34a039c9e225ba8ef421ec8e0e9d46b5172e892058a9ade579fe0eb239f7d9c97d45b0300000009ac65655351ab526363ffffffff10c0faaf7f597fc8b00bbc67c3fd4c6b70ca6b22718d15946bf6b032e62dae570000000005536a00ab6a02cddec3acf985bbe62c96fccf17012a87026ed63fc6756fa39e286eb4c2dd79b59d37400300000002516affffffff04f18b8d03000000000753abab5152636564411c02000000000400ab6300e965750300000000001bd2cf02000000000565ab526aab00000000", "006551ab", 0, -1488174485, "a3d65a8cd0c1eea8558d01396b929520a2221c29d9f25f29035b8abae874447f"], + ["489ebbf10478e260ba88c0168bd7509a651b36aaee983e400c7063da39c93bf28100011f280100000004abab63ab2fc856f05f59b257a4445253e0d91b6dffe32302d520ac8e7f6f2467f7f6b4b65f2f59e903000000096353abacab6351656affffffff0122d9480db6c45a2c6fd68b7bc57246edffbf6330c39ccd36aa3aa45ec108fc030000000265ab9a7e78a69aadd6b030b12602dff0739bbc346b466c7c0129b34f50ae1f61e634e11e9f3d0000000006516a53525100ffffffff011271070000000000086563ab6353536352c4dd0e2c", "", 0, -293358504, "4eba3055bc2b58765593ec6e11775cea4b6493d8f785e28d01e2d5470ea71575"], + ["6911195d04f449e8eade3bc49fd09b6fb4b7b7ec86529918b8593a9f6c34c2f2d301ec378b000000000263ab49162266af054643505b572c24ff6f8e4c920e601b23b3c42095881857d00caf56b28acd030000000565525200ac3ac4d24cb59ee8cfec0950312dcdcc14d1b360ab343e834004a5628d629642422f3c5acc02000000035100accf99b663e3c74787aba1272129a34130668a877cc6516bfb7574af9fa6d07f9b4197303400000000085351ab5152635252ffffffff042b3c95000000000000ff92330200000000046a5252ab884a2402000000000853530065520063000d78be03000000000953abab52ab53ac65aba72cb34b", "6a", 2, -637739405, "6b80d74eb0e7ee59d14f06f30ba7d72a48d3a8ff2d68d3b99e770dec23e9284f"], + ["746347cf03faa548f4c0b9d2bd96504d2e780292730f690bf0475b188493fb67ca58dcca4f0000000002005336e3521bfb94c254058e852a32fc4cf50d99f9cc7215f7c632b251922104f638aa0b9d080100000008656aac5351635251ffffffff4da22a678bb5bb3ad1a29f97f6f7e5b5de11bb80bcf2f7bb96b67b9f1ac44d09030000000365ababffffffff036f02b30000000000076353ab6aac63ac50b72a050000000002acaba8abf804000000000663006a6a6353797eb999", "acac5100", 1, -1484493812, "164c32a263f357e385bd744619b91c3f9e3ce6c256d6a827d6defcbdff38fa75"], + ["e17149010239dd33f847bf1f57896db60e955117d8cf013e7553fae6baa9acd3d0f1412ad90200000006516500516500cb7b32a8a67d58dddfb6ceb5897e75ef1c1ff812d8cd73875856487826dec4a4e2d2422a0100000004ac525365196dbb69039229270400000000070000535351636a8b7596020000000006ab51ac52655131e99d040000000003516551ee437f5c", "ac656a53", 1, 1102662601, "8858bb47a042243f369f27d9ab4a9cd6216adeac1c1ac413ed0890e46f23d3f3"], + ["144971940223597a2d1dec49c7d4ec557e4f4bd207428618bafa3c96c411752d494249e1fb0100000004526a5151ffffffff340a545b1080d4f7e2225ff1c9831f283a7d4ca4d3d0a29d12e07d86d6826f7f0200000003006553ffffffff03c36965000000000000dfa9af00000000000451636aac7f7d140300000000016300000000", "", 1, -108117779, "c84fcaf9d779df736a26cc3cabd04d0e61150d4d5472dd5358d6626e610be57f"], + ["b11b6752044e650b9c4744fb9c930819227d2ac4040d8c91a133080e090b042a142e93906e0000000003650053ffffffff6b9ce7e29550d3c1676b702e5e1537567354b002c8b7bb3d3535e63ad03b50ea01000000055100516300fffffffffcf7b252fea3ad5a108af3640a9bc2cd724a7a3ce22a760fba95496e88e2f2e801000000036a00ac7c58df5efba193d33d9549547f6ca839f93e14fa0e111f780c28c60cc938f785b363941b000000000863ab51516552ac5265e51fcd0308e9830400000000036a00abab72190300000000016a63d0710000000000050051ab6a6300000000", "53005165ac51ab65", 0, 229563932, "e562579d1a2b10d1c5e45c06513456002a6bec157d7eb42511d30b118103c052"], + ["2aee6b9a02172a8288e02fac654520c9dd9ab93cf514d73163701f4788b4caeeb9297d2e250300000004ab6363008fb36695528d7482710ea2926412f877a3b20acae31e9d3091406bfa6b62ebf9d9d2a6470100000009535165536a63520065ffffffff03f7b560050000000003acab6a9a8338050000000000206ce90000000000056552516a5100000000", "5252", 1, -1102319963, "fa4676c374ae3a417124b4c970d1ed3319dc3ac91fb36efca1aa9ed981a8aa1b"], + ["9554595203ad5d687f34474685425c1919e3d2cd05cf2dac89d5f33cd3963e5bb43f8706480100000000ffffffff9de2539c2fe3000d59afbd376cb46cefa8bd01dbc43938ff6089b63d68acdc2b02000000096553655251536a6500fffffffff9695e4016cd4dfeb5f7dadf00968e6a409ef048f81922cec231efed4ac78f5d010000000763abab6a5365006caaf0070162cc640200000000045163ab5100000000", "", 0, -1105256289, "e8e10ed162b1a43bfd23bd06b74a6c2f138b8dc1ab094ffb2fa11d5b22869bee"], + ["04f51f2a0484cba53d63de1cb0efdcb222999cdf2dd9d19b3542a896ca96e23a643dfc45f00200000007acac53510063002b091fd0bfc0cfb386edf7b9e694f1927d7a3cf4e1d2ce937c1e01610313729ef6419ae7030000000165a3372a913c59b8b3da458335dc1714805c0db98992fd0d93f16a7f28c55dc747fe66a5b503000000095351ab65ab52536351ffffffff5650b318b3e236802a4e41ed9bc0a19c32b7aa3f9b2cda1178f84499963a0cde000000000165ffffffff0383954f04000000000553ac536363a8fc90030000000000a2e315000000000005acab00ab5100000000", "0053", 2, -1424653648, "a5bc0356f56b2b41a2314ec05bee7b91ef57f1074bcd2efc4da442222269d1a3"], + ["5e4fab42024a27f0544fe11abc781f46596f75086730be9d16ce948b04cc36f86db7ad50fd01000000026a00613330f4916285b5305cc2d3de6f0293946aa6362fc087727e5203e558c676b314ef8dd401000000001af590d202ba496f040000000001009e3c9604000000000351ac51943d64d3", "51acabab5100ab52", 1, -129301207, "556c3f90aa81f9b4df5b92a23399fe6432cf8fecf7bba66fd8fdb0246440036c"], + ["a115284704b88b45a5f060af429a3a8eab10b26b7c15ed421258f5320fa22f4882817d6c2b0300000003005300ffffffff4162f4d738e973e5d26991452769b2e1be4b2b5b7e8cbeab79b9cf9df2882c040000000006636aac63ac5194abc8aa22f8ddc8a7ab102a58e39671683d1891799d19bd1308d24ea6d365e571172f1e030000000700515352515153ffffffff4da7ad75ce6d8541acbb0226e9818a1784e9c97c54b7d1ff82f791df1c6578f60000000000ffffffff01b1f265040000000009ab0051ac656a516a5300000000", "51abab6352535265", 0, -1269106800, "0ef7b6e87c782fa33fe109aab157a2d9cddc4472864f629510a1c92fa1fe7fc1"], + ["f3f771ae02939752bfe309d6c652c0d271b7cab14107e98032f269d92b2a8c8853ab057da8010000000563ab6a6365670c305c38f458e30a7c0ab45ee9abd9a8dc03bae1860f965ffced879cb2e5d0bb156821020000000153ffffffff025dc619050000000002ac51ec0d250100000000076a5200636a6363333aecd8", "650053ac515100ab", 1, 1812404608, "a7aa34bf8a5644f03c6dd8801f9b15ba2e07e07256dbf1e02dad59f0d3e17ea9"], + ["fd3e267203ae7d6d3975e738ca84f12540229bb237dd228d5f688e9d5ba53fce4302b0334d01000000026353ffffffff602a3ab75af7aa951d93093e345ef0037a2863f3f580a9b1a575fffe68e677450300000000239e476d1e8f81e8b6313880d8a49b27c1b00af467f29756e76f675f084a5676539636ab030000000765ab6351acac52d9217747044d773204000000000752ac51526353acc33e45050000000005516500005115d889040000000004ab5163510cbbbd0200000000016500000000", "65ac526aac6a53ab52", 2, -886179388, "bc46f3f83058ddf5bebd9e1f2c117a673847c4dc5e31cfb24bac91adf30877cf"], + ["f380ae23033646af5dfc186f6599098015139e961919aea28502ea2d69474413d94a555ea2000000000853635265abacac5314da394b99b07733341ddba9e86022637be3b76492992fb0f58f23c915098979250a96620300000003ab6300ffffffff4bb6d1c0a0d84eac7f770d3ad0fdc5369ae42a21bbe4c06e0b5060d5990776220300000000ffffffff0486fd70020000000007ac6500635252acf3fd72010000000005656a6a6551212de90500000000096365006a63635153000fa33100000000000600535151656300000000", "ab52", 2, -740890152, "f804fc4d81f039009ed1f2cccb5c91da797543f235ac71b214c20e763a6d86d7"], + ["5c45d09801bb4d8e7679d857b86b97697472d514f8b76d862460e7421e8617b15a2df217c6010000000863acacab6565006affffffff01156dbc03000000000952ac63516551ac6aac00000000", "6aabac", 0, 1310125891, "270445ab77258ced2e5e22a6d0d8c36ac7c30fff9beefa4b3e981867b03fa0ad"], + ["4ecc6bde030ca0f83c0ed3d4b777f94c0c88708c6c933fe1df6874f296d425cac95355c23d0000000006ac6a51536a52f286a0969d6170e20f2a8000193807f5bc556770e9d82341ef8e17b0035eace89c76edd50200000007ac65525100656affffffff5bade6e462fac1927f078d69d3a981f5b4c1e59311a38efcb9a910aa436afaa80000000007ac6a006352ab52ffffffff0331e58902000000000763ac53636352abb8b3ca000000000001637a1d26040000000009535263ac6a5352ab655ae34a39", "6a65ab", 2, 2142728517, "4a3415eb1677ae4e0c939644a4cfd5dc6299780b55cd0dc735967057b6b1526a"], + ["a59484b501eb50114be0fc79e72ab9bc9f4a5f7acdf274a56d6b68684eb68cf8b07ec5d1c2000000000765abab00ab00639e09aa940141e3530200000000046500ac6500000000", "00516565ab", 0, -1561622405, "d60bbadd2cc0674100baa08d0e0493ee4248f0304b3eb778da942041f503a896"], + ["53dc1a88046531c7b57a35f4d9adf101d068bf8d63fbbedaf4741dba8bc5e92c8725def571030000000453655251fcdf116a226b3ec240739c4c7493800e4edfe67275234e371a227721eac43d3d9ecaf1b50300000003ac0052ffffffff2c9279ffeea4718d167e9499bd067600715c14484e373ef93ae4a31d2f5671ab0000000009516553ac636a6a65001977752eeba95a8f16b88c571a459c2f2a204e23d48cc7090e4f4cc35846ca7fc0a455ce00000000055165ac0063188143f80205972902000000000765ac63ac516353c7b6a50000000000036a510000000000", "655351536a", 0, 103806788, "b276584d3514e5b4e058167c41dc02915b9d97f6795936a51f40e894ed8508bc"], + ["53f8959f01ddb36afdcd20167edcbb75a63d18654fdcf10bc0004c761ab450fe236d79cb2702000000065151650063653435003a033a5e34050000000009ac52516a630000516ab86db3030000000002006344ac090500000000046363ab00f3644537", "5263abab63ac656353", 0, -218513553, "f1f2a489682e42a6fc20025dfc89584d17f150b2d7ae3ddedd2bf43d5e24f37f"], + ["5a06cb4602dcfc85f49b8d14513f33c48f67146f2ee44959bbca092788e6823b2719f3160b0200000001ab3c013f2518035b9ea635f9a1c74ec1a3fb7496a160f46aae2e09bfc5cd5111a0f20969e003000000015158c89ab7049f20d6010000000008ac6a52abac53515349765e00000000000300ab638292630100000000045351ab0086da09010000000006656a6365525300000000", "526a63", 1, 1502936586, "bdfaff8a4e775379c5dc26e024968efa805f923de53fa8272dd53ec582afa0c5"], + ["ca9d84fa0129011e1bf27d7cb71819650b59fb292b053d625c6f02b0339249b498ff7fd4b601000000025352ffffffff032173a0040000000008525253abab5152639473bb030000000009005153526a53535151d085bd0000000000086a5365ab5165655300000000", "005152ac51", 0, 580353445, "c629d93b02037f40aa110e46d903edb34107f64806aa0c418d435926feef68b8"], + ["e3cdbfb4014d90ae6a4401e85f7ac717adc2c035858bf6ff48979dd399d155bce1f150daea0300000002ac51a67a0d39017f6c71040000000005535200535200000000", "", 0, -1899950911, "c1c7df8206e661d593f6455db1d61a364a249407f88e99ecad05346e495b38d7"], + ["b2b6b9ab0283d9d73eeae3d847f41439cd88279c166aa805e44f8243adeb3b09e584efb1df00000000026300ffffffff7dfe653bd67ca094f8dab51007c6adaced09de2af745e175b9714ca1f5c68d050000000003ac6500aa8e596903fd3f3204000000000553ac6a6a533a2e210500000000075253acabab526392d0ee020000000008520065635200ab5200000000", "65acacac65005365", 0, 28298553, "39c2aaa2496212b3ab120ab7d7f37c5e852bfe38d20f5226413a2268663eeae8"], + ["f30c5c3d01a6edb9e10fafaf7e85db14e7fec558b9dca4a80b05d7c3a2944d282c5018f4680200000003005263ffffffff04aac3530300000000026551bc2419010000000009005163acab6a5100658e7085050000000000c5e4ec050000000007656a6a635365ab2d8e8882", "abac53ab005251ac52", 0, -490287546, "877e347ec7487497769e2581142276d1a8d813b652e4483cf9cc993d16354417"], + ["4314339e01de40faabcb1b970245a7f19eedbc17c507dac86cf986c2973715035cf95736ae0200000007abababababab65bde67b900151510b04000000000853ac00655200535300000000", "52", 0, 399070095, "47585dc25469d04ff3a60939d0a03779e3e81a411bf0ca18b91bb925ebd30718"], + ["2d4cf4e9031b3e175b2ff18cd933151379d9cfac4713d8bd0e63b70bd4a92277aa7af901ab000000000565515353abffffffff557666c7f3be9cdecdad44c3df206eb63a2da4ed1f159d21193882a9f0340081020000000963ab53ab5252ac63abffffffff8a8c897bdb87e93886aad5ded9d82a13101d5476554386373646ca5e23612e450300000009006a526552abab6a635ac03fc00198bb02040000000009525100526a6563636a1d052834", "ab52ac00acac6a", 0, -1469882480, "09ed6563a454814ab7e3b4c28d56d8751162b77df1825b37ba66c6147750b2a3"], + ["f063171b03e1830fdc1d685a30a377537363ccafdc68b42bf2e3acb908dac61ee24b37595c020000000765ac5100ab6aacf447bc8e037b89d6cadd62d960cc442d5ced901d188867b5122b42a862929ce45e7b628d010000000253aba009a1ba42b00f1490b0b857052820976c675f335491cda838fb7934d5eea0257684a2a202000000001e83cf2401a7f777030000000008ab6553526a53526a00000000", "", 2, 1984790332, "c19caada8e71535e29a86fa29cfd9b74a0c7412003fc722a121005e461e01636"], + ["cf7bdc250249e22cbe23baf6b648328d31773ea0e771b3b76a48b4748d7fbd390e88a004d30000000003ac536a4ab8cce0e097136c90b2037f231b7fde2063017facd40ed4e5896da7ad00e9c71dd70ae600000000096a0063516352525365ffffffff01b71e3e00000000000300536a00000000", "", 1, 546970113, "6a815ba155270af102322c882f26d22da11c5330a751f520807936b320b9af5d"], + ["ac7a125a0269d35f5dbdab9948c48674616e7507413cd10e1acebeaf85b369cd8c88301b7c030000000963656aac6a530053abffffffffed94c39a582e1a46ce4c6bffda2ccdb16cda485f3a0d94b06206066da12aecfe010000000752abab63536363ef71dcfb02ee07fa0400000000016a6908c802000000000751656a6551abac688c2c2d", "6a6351526551", 0, 858400684, "552ff97d7924f51cda6d1b94be53483153ef725cc0a3a107adbef220c753f9a6"], + ["3a1f454a03a4591e46cf1f7605a3a130b631bf4dfd81bd2443dc4fac1e0a224e74112884fe0000000005516aac6a53a87e78b55548601ffc941f91d75eab263aa79cd498c88c37fdf275a64feff89fc1710efe03000000016a39d7ef6f2a52c00378b4f8f8301853b61c54792c0f1c4e2cd18a08cb97a7668caa008d970200000002656affffffff017642b20100000000096a63535253abac6a6528271998", "51", 2, 1459585400, "e9a7f21fc2d38be7be47095fbc8f1bf8923660aa4d71df6d797ae0ba5ca4d5b0"], + ["f59366cc0114c2a18e6bd1347ed9470f2522284e9e835dd5c5f7ef243639ebea95d9b232b6020000000153474b62eb045c00170500000000096352ab516352ab5200038a520400000000086aab5253656a63005b968904000000000963536353ac0053635387106002000000000000000000", "ab52526300ab51", 0, 1834116153, "cdf51f6e3a9dc2be5a59ea4c00f5aac1e1426a5202c325e6cf2567d07d8d8de4"], + ["6269e0fa0173e76e89657ca495913f1b86af5b8f1c1586bcd6c960aede9bc759718dfd5044000000000352ac530e2c7bd90219849b000000000007ab00ab6a53006319f281000000000007ab00515165ac5200000000", "6a", 0, -2039568300, "62094f98234a05bf1b9c7078c5275ed085656856fb5bdfd1b48090e86b53dd85"], + ["eb2bc00604815b9ced1c604960d54beea4a3a74b5c0035d4a8b6bfec5d0c9108f143c0e99a0000000000ffffffff22645b6e8da5f11d90e5130fd0a0df8cf79829b2647957471d881c2372c527d8010000000263acffffffff1179dbaf17404109f706ae27ad7ba61e860346f63f0c81cb235d2b05d14f2c1003000000025300264cb23aaffdc4d6fa8ec0bb94eff3a2e50a83418a8e9473a16aaa4ef8b855625ed77ef40100000003ac51acf8414ad404dd328901000000000652526500006ab6261c000000000002526a72a4c9020000000006ac526500656586d2e7000000000006656aac00ac5279cd8908", "51", 1, -399279379, "d37532e7b2b8e7db5c7c534197600397ebcc15a750e3af07a3e2d2e4f84b024f"], + ["dc9fe6a8038b84209bbdae5d848e8c040433237f415437592907aa798bf30d9dbbddf0ff85010000000153ffffffff23269a7ea29fcf788db483b8d4c4b35669e582608644259e950ce152b0fa6e050000000003acababffffffff65de94857897ae9ea3aa0b938ba6e5adf374d48469922d2b36dbb83d3b8c8261010000000452ac5200ffffffff02856e9b0300000000026a51980c8e02000000000365ab63d2648db4", "00ab0051ac526565", 2, 1562581941, "5cef9d8e18a2d5a70448f17b465d411a19dab78f0ddf1672ffd518b188f52433"], + ["eba8b0de04ac276293c272d0d3636e81400b1aaa60db5f11561480592f99e6f6fa13ad387002000000070053acab536563bebb23d66fd17d98271b182019864a90e60a54f5a615e40b643a54f8408fa8512cfac927030000000963ac6a6aabac65ababffffffff890a72192bc01255058314f376bab1dc72b5fea104c154a15d6faee75dfa5dba020000000100592b3559b0085387ac7575c05b29b1f35d9a2c26a0c27903cc0f43e7e6e37d5a60d8305a030000000252abffffffff0126518f05000000000000000000", "005300635252635351", 1, 664344756, "26dc2cba4bd5334e5c0b3a520b44cc1640c6b923d10e576062f1197171724097"], + ["91bd040802c92f6fe97411b159df2cd60fb9571764b001f31657f2d616964637605875c2a901000000055263006a65ffffffff3651df372645f50cf4e32fdf6e61c766e912e16335db2b40c5d52fe89eefe7cd00000000040065ab65ffffffff03ca8625030000000009ab51ac63530052ab52c6bf14020000000006ab00ab52005167d270000000000007ab53525351636a00000000", "5151ab63005252ac", 1, 1983087664, "3e5aa0200248d8d86ede3b315ca1b857018b89184a4bd023bd88ab12e499f6e1"], + ["185cda1a01ecf7a8a8c28466725b60431545fc7a3367ab68e34d486e8ea85ee3128e0d8384000000000465ac63abec88b7bb031c56eb04000000000965636a51005252006a7c78d5040000000007acac63abac51ac3024a40500000000086300526a51abac51464c0e8c", "0065535265515352", 0, 1594558917, "b5280b9610c0625a65b36a8c2402a95019a7bbb9dd3de77f7c3cb1d82c3263ba"], + ["a9531f07034091668b65fea8b1a79700d586ac9e2f42ca0455a26abe41f9e1805d009a0f5702000000096365516365ac5263ab3619bac643a9e28ee47855118cf80c3a74531cdf198835d206d0fe41804e325a4f9f105e03000000016a58e3ab0d46375d98994daf0fa7c600d2bb4669e726fca0e3a3f21ea0d9e777396740328f0100000008636a5363ab526a538d3ea7700304cb66030000000007515163ab52ab510184030500000000085353636565ac0051d9cff402000000000751ab52ab5352abf0e36254", "ab5353ac5365acab", 2, 1633101834, "04c9ef72f33668ca449c0415becf62cc0b8e0c75f9c8813852d42a58acf107c8"], + ["6b5ecc7903fe0ba37ea551df92a59e12bad0a3065846ba69179a8f4a741a2b4fcf679aac810200000004535263529a3d343293b99ab425e7ef8529549d84f480bcd92472bab972ea380a302128ae14dfcd0200000000025163ffffffff24636e4545cab9bf87009119b7fc3ec4d5ee9e206b90f35d1df8a563b6cd097a010000000852abac53005153abc64467860406e832020000000009526300006a53ac6352ac1395010000000002ac53b117f300000000000863655351acab00651edf02030000000008ab51ac6353535252628ef71d", "ab63ab6a52ac526563", 2, -1559697626, "8f07ece7d65e509f1e0780584ef8d271c1c61a13b10335d5faafc7afc8b5b8ec"], + ["92c9fb780138abc472e589d5b59489303f234acc838ca66ffcdf0164517a8679bb622a4267020000000153468e373d04de03fa020000000009ac006a5265ab5163006af649050000000007515153006a00658ceb59030000000001ac36afa0020000000009ab53006351ab51000000000000", "6a", 0, 2059357502, "e2358dfb51831ee81d7b0bc602a65287d6cd2dbfacf55106e2bf597e22a4b573"], + ["6f62138301436f33a00b84a26a0457ccbfc0f82403288b9cbae39986b34357cb2ff9b889b302000000045253655335a7ff6701bac9960400000000086552ab656352635200000000", "6aac51", 0, 1444414211, "502a2435fd02898d2ff3ab08a3c19078414b32ec9b73d64a944834efc9dae10c"], + ["9981143a040a88c2484ac3abe053849e72d04862120f424f373753161997dd40505dcb4783030000000700536365536565a2e10da3f4b1c1ad049d97b33f0ae0ea48c5d7c30cc8810e144ad93be97789706a5ead180100000003636a00ffffffffbdcbac84c4bcc87f03d0ad83fbe13b369d7e42ddb3aecf40870a37e814ad8bb5010000000963536a5100636a53abffffffff883609905a80e34202101544f69b58a0b4576fb7391e12a769f890eef90ffb72020000000651656352526affffffff04243660000000000004ab5352534a9ce001000000000863656363ab6a53652df19d030000000003ac65acedc51700000000000000000000", "ac6300acac", 2, 293672388, "7ba99b289c04718a7283f150d831175ed6303081e191a0608ea81f78926c5bdf"], + ["a2bb630b01989bc5d643f2da4fb9b55c0cdf846ba06d1dbe372893024dbbe5b9b8a1900af802000000055265ac63aca7a68d2f04916c74010000000003abac007077f0040000000001007d4127010000000005ac516aac000f31e8030000000000571079c9", "65ab0051ac", 0, -1103627693, "92d53b4390262e6b288e8a32e0cfc36cd5adfdfabfe96c7bfd4a19d65e233761"], + ["49f7d0b6037bba276e910ad3cd74966c7b3bc197ffbcfefd6108d6587006947e97789835ea0300000008526a52006a650053ffffffff8d7b6c07cd10f4c4010eac7946f61aff7fb5f3920bdf3467e939e58a1d4100ab03000000076aac63ac535351ffffffff8f48c3ba2d52ad67fbcdc90d8778f3c8a3894e3c35b9730562d7176b81af23c80100000003ab5265ffffffff0301e3ef0300000000046a525353e899ac0500000000075153ab6a65abac259bea0400000000007b739972", "53516aacac6aac", 1, 955403557, "5d366a7f4346ae18aeb7c9fc4dab5af71173184aa20ed22fcb4ea8511ad25449"], + ["58a4fed801fbd8d92db9dfcb2e26b6ff10b120204243fee954d7dcb3b4b9b53380e7bb8fb60100000003006351ffffffff02a0795b050000000006536351ac6aac2718d00200000000075151acabac515354d21ba1", "005363515351", 0, -1322430665, "bbee941bbad950424bf40e3623457db47f60ed29deaa43c99dec702317cb3326"], + ["32765a0b02e455793d9ce530e9f6a44bcbc612e893a875b5da61d822dc56d8245166c398b403000000085353abac6300006a6bdee2a78d0d0b6a5ea666eed70b9bfea99d1d612ba3878f615c4da10d4a521cba27155002000000035363abffffffff043cd42401000000000551656a53653685320100000000030000511881bc0500000000065165abab636a20169f010000000007acab656aac63acdb0706a8", "65ac53ab53", 0, 1936499176, "5c5a9c3a5de7dc7a82bc171c9d3505913b8bcc450bc8b2d11772c1a1d781210b"], + ["17fad0d303da0d764fedf9f2887a91ea625331b28704940f41e39adf3903d8e75683ef6d46020000000151ffffffffff376eea4e880bcf0f03d33999104aafed2b3daf4907950bb06496af6b51720a020000000900636a63525253525196521684f3b08497bad2c660b00b43a6a517edc58217876eb5e478aa3b5fda0f29ee1bea00000000046aacab6affffffff03dde8e2050000000007ac5365ac51516a14772e000000000005630000abacbbb360010000000006ab5251ab656a50f180f0", "0053", 0, -1043701251, "a3bdf8771c8990971bff9b4e7d59b7829b067ed0b8d3ac1ec203429811384668"], + ["236c32850300045e292c84ede2b9ab5733ba08315a2bb09ab234c4b4e8894808edbdac0d3b020000000653635363abacffffffffd3f696bb31fdd18a72f3fc2bb9ae54b416a253fc37c1a0f0180b52d35bad49440100000004650053abffffffffa85c75a2406d82a93b12e555b66641c1896a4e83ae41ef1038218311e38ace060200000006abab006a51ac104b5e6701e2842c04000000000800630051ac0000ab00000000", "ab63ac6a516a", 1, -1709887524, "8c29ea8ef60c5a927fccdba8ea385db6b6b84d98e891db45f5d4ee3148d3f5a7"], + ["b78d5fd601345f3100af494cdf447e7d4076179f940035b0ebe8962587d4d0c9c6c9fc34ee0300000003516a6affffffff03dc5c890100000000085353ac53ac6a52534ac941040000000007ac63656a51ab51d4266b0100000000036aacac70731f2d", "005351ab0053", 0, -1789071265, "d5f1c1cb35956a5711d67bfb4cedbc67e77c089b912d688ad440ff735adb390d"], + ["5a2257df03554550b774e677f348939b37f8e765a212e566ce6b60b4ea8fed4c9504b7f7d1000000000653655265ab5258b67bb931df15b041177cf9599b0604160b79e30f3d7a594e7826bae2c29700f6d8f8f40300000005515300ac6a159cf8808a41f504eb5c2e0e8a9279f3801a5b5d7bc6a70515fbf1c5edc875bb4c9ffac500000000050063510052ffffffff0422a90105000000000965006a650000516a006417d2020000000006526363ab00524d969d0100000000035153acc4f077040000000005ac5200636500000000", "6a52", 1, -1482463464, "37b794b05d0687c9b93d5917ab068f6b2f0e38406ff04e7154d104fc1fb14cdc"], + ["e0032ad601269154b3fa72d3888a3151da0aed32fb2e1a15b3ae7bee57c3ddcffff76a1321010000000100110d93ae03f5bd080100000000075263516a6551002871e60100000000046a005252eaa753040000000004ab6aab526e325c71", "630052", 0, -1857873018, "ea117348e94de86381bb8ad1c7f93b8c623f0272104341701bb54e6cb433596c"], + ["014b2a5304d46764817aca180dca50f5ab25f2e0d5749f21bb74a2f8bf6b8b7b3fa8189cb7030000000965ac5165ab6a51ac6360ecd91e8abc7e700a4c36c1a708a494c94bb20cbe695c408543146566ab22be43beae9103000000045163ab00ffffffffffa48066012829629a9ec06ccd4905a05df0e2b745b966f6a269c9c8e13451fc00000000026565ffffffffc40ccadc21e65fe8a4b1e072f4994738ccaf4881ae6fede2a2844d7da4d199ab02000000065152ab536aabffffffff01b6e054030000000004515352ab3e063432", "", 0, 1056459916, "a7aff48f3b8aeb7a4bfe2e6017c80a84168487a69b69e46681e0d0d8e63a84b6"], + ["c4ef04c103c5dde65410fced19bf6a569549ecf01ceb0db4867db11f2a3a3eef0320c9e8e001000000085100536a53516aabffffffff2a0354fa5bd96f1e28835ffe30f52e19bd7d5150c687d255021a6bec03cf4cfd03000000056a006300514900c5b01d3d4ae1b97370ff1155b9dd0510e198d266c356d6168109c54c11b4c283dca00300000002ababffffffff02e19e3003000000000451655351fa5c0003000000000163ef1fc64b", "51636a51ab630065", 1, -1754709177, "0a281172d306b6a32e166e6fb2a2cc52c505c5d60ea448e9ba7029aa0a2211e1"], + ["29083fe00398bd2bb76ceb178f22c51b49b5c029336a51357442ed1bac35b67e1ae6fdf13100000000066a6500acab51ffffffffe4ca45c9dc84fd2c9c47c7281575c2ba4bf33b0b45c7eca8a2a483f9e3ebe4b3010000000200abffffffffdf47ad2b8c263fafb1e3908158b18146357c3a6e0832f718cd464518a219d18303000000096352ac656351ac0052daddfb3b0231c36f00000000000400526a5275c7e0020000000001ab00000000", "acab536aac52", 2, 300802386, "82ebc07b16cff0077e9c1a279373185b3494e39d08fd3194aae6a4a019377509"], + ["1201ab5d04f89f07c0077abd009762e59db4bb0d86048383ba9e1dad2c9c2ad96ef660e6d00200000007ab6a65ac5200652466fa5143ab13d55886b6cdc3d0f226f47ec1c3020c1c6e32602cd3428aceab544ef43e00000000086a6a6a526a6a5263ffffffffd5be0b0be13ab75001243749c839d779716f46687e2e9978bd6c9e2fe457ee48020000000365abab1e1bac0f72005cf638f71a3df2e3bbc0fa35bf00f32d9c7dc9c39a5e8909f7d53170c8ae0200000008ab6a51516363516affffffff02f0a6210500000000036300ac867356010000000009acab65ac6353536a659356d367", "ac53535252", 0, 917543338, "418acc156c2bc76a5d7baa58db29f1b4cf6c266c9222ed167ef5b4d47f0e0f41"], + ["344fa11e01c19c4dd232c77742f0dd0aeb3695f18f76da627628741d0ee362b0ea1fb3a2180200000007635151005100529bab25af01937c1f0500000000055153ab53656e7630af", "6351005163ac51", 0, -629732125, "228ca52a0a376fe0527a61cfa8da6d7baf87486bba92d49dfd3899cac8a1034f"], + ["b2fda1950191358a2b855f5626a0ebc830ab625bea7480f09f9cd3b388102e35c0f303124c030000000565ac65ab53ffffffff03f9c5ec04000000000765ab51516551650e2b9f0500000000045365525284e8f6040000000001ac00000000", "ac51655253", 0, 1433027632, "d2fa7e13c34cecda5105156bd2424c9b84ee0a07162642b0706f83243ff811a8"], + ["a4a6bbd201aa5d882957ac94f2c74d4747ae32d69fdc765add4acc2b68abd1bdb8ee333d6e0300000008516a6552515152abffffffff02c353cb040000000007ac6351ab51536588bd320500000000066552525253ac00000000", "", 0, 1702060459, "499da7d74032388f820645191ac3c8d20f9dba8e8ded7fa3a5401ea2942392a1"], + ["584e8d6c035a6b2f9dac2791b980a485994bf38e876d9dda9b77ad156eee02fa39e19224a60300000003ab636529db326cc8686a339b79ab6b6e82794a18e0aabc19d9ad13f31dee9d7aad8eff38288588020000000452530052ffffffff09a41f07755c16cea1c7e193c765807d18cadddca6ec1c2ed7f5dcdca99e90e80000000001acffffffff01cba62305000000000451ac63acccdf1f67", "ab536a6363", 2, -27393461, "1125645b49202dca2df2d76dae51877387903a096a9d3f66b5ac80e042c95788"], + ["83a583d204d926f2ee587a83dd526cf1e25a44bb668e45370798f91a2907d184f7cddcbbc7030000000700ab6565536a539f71d3776300dffdfa0cdd1c3784c9a1f773e34041ca400193612341a9c42df64e3f550e01000000050052515251ffffffff52dab2034ab0648553a1bb8fc4e924b2c89ed97c18dfc8a63e248b454035564b01000000015139ab54708c7d4d2c2886290f08a5221cf69592a810fd1979d7b63d35c271961e710424fd0300000005ac65ac5251ffffffff01168f7c030000000000a85e5fb0", "6a536353656a00", 0, 179595345, "5350a31ac954a0b49931239d0ecafbf34d035a537fd0c545816b8fdc355e9961"], + ["ffd35d51042f290108fcb6ea49a560ba0a6560f9181da7453a55dfdbdfe672dc800b39e7320200000006630065516a65f2166db2e3827f44457e86dddfd27a8af3a19074e216348daa0204717d61825f198ec0030100000006ab51abab00abffffffffdf41807adb7dff7db9f14d95fd6dc4e65f8402c002d009a3f1ddedf6f4895fc8030000000500ab006a65a5a848345052f860620abd5fcd074195548ce3bd0839fa9ad8642ed80627bf43a0d47dbd010000000765ab006a656a53b38cdd6502a186da05000000000765ab00ab006a53527c0e0100000000085365ab51acacac52534bd1b1", "6a635253ac0000", 0, 1095082149, "3c05473a816621a3613f0e903faa1a1e44891dd40862b029e41fc520776350fa"], + ["6c9a4b98013c8f1cae1b1df9f0f2de518d0c50206a0ab871603ac682155504c0e0ce946f460100000000ffffffff04e9266305000000000753535100ac6aacded39e04000000000365ac6ab93ccd010000000002515397bf3d050000000003ab636300000000", "63520052ac656353", 0, -352633155, "936eff8cdfd771be24124da87c7b24feb48da7cbc2c25fb5ba13d1a23255d902"], + ["e01dc7f0021dc07928906b2946ca3e9ac95f14ad4026887101e2d722c26982c27dc2b59fdb0000000005ac5200516ab5a31ffadcbe74957a5a3f97d7f1475cc6423fc6dbc4f96471bd44c70cc736e7dec0d1ea020000000951636a526a52abac53ffffffff04bc2edd05000000000252ab528c7b02000000000952ac51526500525353324820040000000002005380c713000000000009630065ab00ac525252451bbb48", "53ab65ac", 0, -552384418, "69c0b30f4c630a6c878fde6ea6b74dae94f4eb3bcfbde2dc3649e1a9ada00757"], + ["009046a1023f266d0113556d604931374d7932b4d6a7952d08fbd9c9b87cbd83f4f4c178b4030000000452ac526346e73b438c4516c60edd5488023131f07acb5f9ea1540b3e84de92f4e3c432289781ea4900000000046500655357dfd6da02baef910100000000026a007d101703000000000800516500abacac5100000000", "6aab6553ac", 0, -802456605, "f8757fbb4448ca34e0cd41b997685b37238d331e70316659a9cc9087d116169d"], + ["df76ec0801a3fcf3d18862c5f686b878266dd5083f16cf655facab888b4cb3123b3ce5db7e01000000010010e7ac6a0233c83803000000000365ac51faf14a040000000004ac51655100000000", "6353acab", 0, 15705861, "e7d873aa079a19ec712b269a37d2670f60d8cb334c4f97e2e3fd10eeb8ee5f5e"], + ["828fd3e0031084051ccef9cfdd97fae4d9cc50c0dae36bd22a3ff332881f17e9756c3e288e0200000004ab535363961a2ccccaf0218ec6a16ba0c1d8b5e93cfd025c95b6e72bc629ec0a3f47da7a4c396dad01000000025353ffffffff19ad28747fb32b4caf7b5dbd9b2da5a264bedb6c86d3a4805cd294ae53a86ac40200000009ab53535351ab6551abffffffff04a41650030000000005656aab6aab8331a304000000000700516365ac516a0d2a47010000000007abac516353abacdebc19040000000006ab5300636a6300000000", "51ab52ab53ac52", 0, 1866105980, "311094b4d73e31aefc77e97859ef07ca2f07a7b7e4d7def80c69d3f5d58527e5"], + ["c4b80f850323022205b3e1582f1ed097911a81be593471a8dce93d5c3a7bded92ef6c7c1260100000002006affffffff70294d62f37c3da7c5eae5d67dce6e1b28fedd7316d03f4f48e1829f78a88ae801000000096a5200530000516351f6b7b544f7c39189d3a2106ca58ce4130605328ce7795204be592a90acd81bef517d6f170200000000ffffffff012ab8080000000000075100006365006335454c1e", "53ac6a536aacac", 0, -1124103895, "06277201504e6bf8b8c94136fad81b6e3dadacb9d4a2c21a8e10017bfa929e0e"], + ["8ab69ed50351b47b6e04ac05e12320984a63801716739ed7a940b3429c9c9fed44d3398ad40300000006536a516a52638171ef3a46a2adb8025a4884b453889bc457d63499971307a7e834b0e76eec69c943038a0300000000ffffffff566bb96f94904ed8d43d9d44a4a6301073cef2c011bf5a12a89bedbaa03e4724030000000265acb606affd01edea38050000000008515252516aacac6300000000", "65000000006365ac53", 0, -1338942849, "7912573937824058103cb921a59a7f910a854bf2682f4116a393a2045045a8c3"], + ["2484991e047f1cf3cfe38eab071f915fe86ebd45d111463b315217bf9481daf0e0d10902a402000000006e71a424eb1347ffa638363604c0d5eccbc90447ff371e000bf52fc743ec832851bb564a0100000001abffffffffef7d014fad3ae7927948edbbb3afe247c1bcbe7c4c8f5d6cf97c799696412612020000000851536a5353006a001dfee0d7a0dd46ada63b925709e141863f7338f34f7aebde85d39268ae21b77c3068c01d0000000008535151ab00636563ffffffff018478070200000000095200635365ac52ab5341b08cd3", "", 3, 265623923, "24cb420a53b4f8bb477f7cbb293caabfd2fc47cc400ce37dbbab07f92d3a9575"], + ["54839ef9026f65db30fc9cfcb71f5f84d7bb3c48731ab9d63351a1b3c7bc1e7da22bbd508e0300000000442ad138f170e446d427d1f64040016032f36d8325c3b2f7a4078766bdd8fb106e52e8d20000000003656500ffffffff02219aa101000000000851ababac52ab00659646bd02000000000552acacabac24c394a5", "ac", 0, 906807497, "69264faadcd1a581f7000570a239a0a26b82f2ad40374c5b9c1f58730514de96"], + ["5036d7080434eb4eef93efda86b9131b0b4c6a0c421e1e5feb099a28ff9dd8477728639f77030000000951516aab535152ab5391429be9cce85d9f3d358c5605cf8c3666f034af42740e94d495e28b9aaa1001ba0c87580300000008006552ab00ab006affffffffd838978e10c0c78f1cd0a0830d6815f38cdcc631408649c32a25170099669daa0000000002acab8984227e804ad268b5b367285edcdf102d382d027789250a2c0641892b480c21bf84e3fb0100000000b518041e023d8653010000000001004040fb0100000000080051ac5200636a6300000000", "52ac", 0, 366357656, "bd0e88829afa6bdc1e192bb8b2d9d14db69298a4d81d464cbd34df0302c634c6"], + ["9ad5ccf503fa4facf6a27b538bc910cce83c118d6dfd82f3fb1b8ae364a1aff4dcefabd38f03000000096365655263ac655300807c48130c5937190a996105a69a8eba585e0bd32fadfc57d24029cbed6446d30ebc1f100100000004000053650f0ccfca1356768df7f9210cbf078a53c72e0712736d9a7a238e0115faac0ca383f219d0010000000600ab536552002799982b0221b8280000000000000c41320000000000086552ac6365636a6595f233a3", "6a5152", 2, 553208588, "f99c29a79f1d73d2a69c59abbb5798e987639e36d4c44125d8dc78a94ddcfb13"], + ["669538a204047214ce058aed6a07ca5ad4866c821c41ac1642c7d63ed0054f84677077a84f030000000853abacab6a655353ffffffff70c2a071c115282924e3cb678b13800c1d29b6a028b3c989a598c491bc7c76c5030000000752ac52ac5163ac80420e8a6e43d39af0163271580df6b936237f15de998e9589ec39fe717553d415ac02a4030000000463635153184ad8a5a4e69a8969f71288c331aff3c2b7d1b677d2ebafad47234840454b624bf7ac1d03000000056a63abab63df38c24a02fbc63a040000000002ab535ec3dc050000000002536500000000", "635153", 3, -190399351, "9615541884dfb1feeb08073a6a6aa73ef694bc5076e52187fdf4138a369f94d9"], + ["a7f139e502af5894be88158853b7cbea49ba08417fbbca876ca6614b5a41432be34499987b000000000765635165abac63ffffffff8b8d70e96c7f54eb70da0229b548ced438e1ca2ba5ddd648a027f72277ee1efc0100000001abffffffff044f2c4204000000000165e93f550100000000050000526a6a94550304000000000365536aadc21c0300000000016300000000", "6aacac6363ab5265ac", 1, 2143189425, "6e3f97955490d93d6a107c18d7fe402f1cada79993bb0ff0d096357261b3a724"], + ["3b94438f0366f9f53579a9989b86a95d134256ce271da63ca7cd16f7dd5e4bffa17d35133f010000000100ffffffff1aaad0c721e06ec00d07e61a84fb6dc840b9a968002ce7e142f943f06fd143a10100000008535151ac51ab0053b68b8e9c672daf66041332163e04db3f6048534bd718e1940b3fc3811c4eef5b7a56888b01000000001d58e38c012e38e700000000000852ab53ac6365536a00000000", "ab655352", 1, -935223304, "b3b336de141d4f071313a2207b2a0c7cf54a070dd8d234a511b7f1d13e23b0c4"], + ["e5dca8a20456de0a67e185fa6ea94085ceae478d2c15c73cb931a500db3a1b6735dd1649ec0200000005ab536aabab32d11bbdcb81361202681df06a6b824b12b5cb40bb1a672cf9af8f2a836e4d95b7839327030000000951005365ab65abacabb345085932939eef0c724adef8a57f9e1bf5813852d957c039b6a12d9c2f201ea520fb030000000009ac5352005165acac6a5efc6072f1a421dc7dc714fc6368f6d763a5d76d0278b95fc0503b9268ccfadb48213a2500000000026a53ffffffff039ee1c4020000000009ac5353ab6353535163184018000000000005655265526a9a4a8a050000000001ac00000000", "65ab53ab6a00ab6553", 2, 1902561212, "7928ae8e86c0b0cad1b2c120ea313087437974382ee6d46443ca5ac3f5878b88"], + ["972128b904e7b673517e96e98d80c0c8ceceae76e2f5c126d63da77ffd7893fb53308bb2da0300000006ac6552ab52acffffffff4cac767c797d297c079a93d06dc8569f016b4bf7a7d79b605c526e1d36a40e2202000000095365ab636aac6a6a6a69928d2eddc836133a690cfb72ec2d3115bf50fb3b0d10708fa5d2ebb09b4810c426a1db01000000060052526300001e8e89585da7e77b2dd2e30625887f0660accdf29e53a614d23cf698e6fc8ab03310e87700000000076a520051acac6555231ddb0330ec2d03000000000200abfaf457040000000004ab6a6352bdc42400000000000153d6dd2f04", "", 0, 209234698, "4a92fec1eb03f5bd754ee9bfd70707dc4420cc13737374f4675f48529be518e4"], + ["1fb4085b022c6cfb848f8af7ba3ba8d21bd23ffa9f0bfd181cb68bcaaf2074e66d4974a31602000000090000006a6a6500acab6c12c07d9f3dbd2d93295c3a49e3757119767097e7fd5371f7d1ba9ba32f1a67a5a426f00000000000ffffffff018fd2fc04000000000363ac5100000000", "65ab006a6aab526a", 0, 1431502299, "8b7dd0ff12ca0d8f4dbf9abf0abba00e897c2f6fd3b92c79f5f6a534e0b33b32"], + ["5374f0c603d727f63006078bd6c3dce48bd5d0a4b6ea00a47e5832292d86af258ea0825c260000000009655353636352526a6af2221067297d42a9f8933dfe07f61a574048ff9d3a44a3535cd8eb7de79fb7c45b6f47320200000003ac006affffffff153d917c447d367e75693c5591e0abf4c94bbdd88a98ab8ad7f75bfe69a08c470200000005ac65516365ffffffff037b5b7b000000000001515dc4d904000000000004bb26010000000004536a6aac00000000", "516552516352ac", 2, 328538756, "8bb7a0129eaf4b8fc23e911c531b9b7637a21ab11a246352c6c053ff6e93fcb6"], + ["c441132102cc82101b6f31c1025066ab089f28108c95f18fa67db179610247086350c163bd010000000651525263ab00ffffffff9b8d56b1f16746f075249b215bdb3516cbbe190fef6292c75b1ad8a8988897c3000000000751ab6553abab00ffffffff02f9078b000000000009ab0053ac51ac00ab51c0422105000000000651006563525200000000", "ac51", 0, -197051790, "55acd8293ed0be6792150a3d7ced6c5ccd153ca7daf09cee035c1b0dac92bb96"], + ["ab82ad3b04545bd86b3bb937eb1af304d3ef1a6d1343ed809b4346cafb79b7297c09e1648202000000086351ac5200535353ffffffff95d32795bbaaf5977a81c2128a9ec0b3c7551b9b1c3d952876fcb423b2dfb9e80000000005515363acac47a7d050ec1a603627ce6cd606b3af314fa7964abcc579d92e19c7aba00cf6c3090d6d4601000000056a516551633e794768bfe39277ebc0db18b5afb5f0c8117dde9b4dfd5697e9027210eca76a9be20d63000000000700520063ab6aacffffffff01ec2ddc050000000008ac52ac65ac65ac5100000000", "536300abab", 1, -2070209841, "b362da5634f20be7267de78b545d81773d711b82fe9310f23cd0414a8280801d"], + ["8bff9d170419fa6d556c65fa227a185fe066efc1decf8a1c490bc5cbb9f742d68da2ab7f320100000007ab000053525365a7a43a80ab9593b9e8b6130a7849603b14b5c9397a190008d89d362250c3a2257504eb810200000007acabacac00ab51ee141be418f003e75b127fd3883dbf4e8c3f6cd05ca4afcaac52edd25dd3027ae70a62a00000000008ac52526a5200536affffffffb8058f4e1d7f220a1d1fa17e96d81dfb9a304a2de4e004250c9a576963a586ae0300000005abacac5363b9bc856c039c01d804000000000951656aac53005365acb0724e00000000000565abab63acea7c7a0000000000036a00ac00000000", "6565", 1, -1349282084, "2b822737c2affeefae13451d7c9db22ff98e06490005aba57013f6b9bbc97250"], + ["0e1633b4041c50f656e882a53fde964e7f0c853b0ada0964fc89ae124a2b7ffc5bc97ea6230100000006ac6aacacabacffffffff2e35f4dfcad2d53ea1c8ada8041d13ea6c65880860d96a14835b025f76b1fbd9000000000351515121270867ef6bf63a91adbaf790a43465c61a096acc5a776b8e5215d4e5cd1492e611f761000000000600ac6aab5265ffffffff63b5fc39bcac83ca80ac36124abafc5caee608f9f63a12479b68473bd4bae769000000000965ac52acac5263acabffffffff0163153e020000000008ab005165ab65515300000000", "6a6aac00", 0, -968477862, "20732d5073805419f275c53784e78db45e53332ee618a9fcf60a3417a6e2ca69"], + ["2b052c24022369e956a8d318e38780ef73b487ba6a8f674a56bdb80a9a63634c6110fb5154010000000251acffffffff48fe138fb7fdaa014d67044bc05940f4127e70c113c6744fbd13f8d51d45143e01000000005710db3804e01aa9030000000008acac6a516a5152abfd55aa01000000000751ab510000ac636d6026010000000000b97da9000000000000fddf3b53", "006552", 0, 595461670, "685d67d84755906d67a007a7d4fa311519467b9bdc6a351913246a41e082a29f"], + ["073bc856015245f03b2ea2da62ccedc44ecb99e4250c7042f596bcb23b294c9dc92cfceb6b02000000095163abab52abab636afe292fb303b7c3f001000000000352636af3c49502000000000400ac6a535851850100000000066aac6553ab6500000000", "ab6aab53006aab52", 0, 247114317, "123916c6485cf23bfea95654a8815fbf04ce4d21a3b7f862805c241472906658"], + ["7888b71403f6d522e414d4ca2e12786247acf3e78f1918f6d727d081a79813d129ee8befce0100000009ab516a6353ab6365abffffffff4a882791bf6400fda7a8209fb2c83c6eef51831bdf0f5dacde648859090797ec030000000153ffffffffbb08957d59fa15303b681bad19ccf670d7d913697a2f4f51584bf85fcf91f1f30200000008526565ac52ac63acffffffff0227c0e8050000000001ac361dc801000000000800515165ab00ab0000000000", "656a", 2, 1869281295, "f43378a0b7822ad672773944884e866d7a46579ee34f9afc17b20afc1f6cf197"], + ["cc4dda57047bd0ca6806243a6a4b108f7ced43d8042a1acaa28083c9160911cf47eab910c40200000007526a0000ab6a63e4154e581fcf52567836c9a455e8b41b162a78c85906ccc1c2b2b300b4c69caaaa2ba0230300000008ab5152ac5100ab65ffffffff69696b523ed4bd41ecd4d65b4af73c9cf77edf0e066138712a8e60a04614ea1c0300000004ab6a000016c9045c7df7836e05ac4b2e397e2dd72a5708f4a8bf6d2bc36adc5af3cacefcf074b8b403000000065352ac5252acffffffff01d7e380050000000000cf4e699a", "525163656351", 1, -776533694, "ff18c5bffd086e00917c2234f880034d24e7ea2d1e1933a28973d134ca9e35d2"], + ["b7877f82019c832707a60cf14fba44cfa254d787501fdd676bd58c744f6e951dbba0b3b77f0200000009ac515263ac53525300a5a36e500148f89c0500000000085265ac6a6a65acab00000000", "6563", 0, -1785108415, "cb6e4322955af12eb29613c70e1a00ddbb559c887ba844df0bcdebed736dffbd"], + ["aeb14046045a28cc59f244c2347134d3434faaf980961019a084f7547218785a2bd03916f3000000000165f852e6104304955bda5fa0b75826ee176211acc4a78209816bbb4419feff984377b2352200000000003a94a5032df1e0d60390715b4b188c330e4bb7b995f07cdef11ced9d17ee0f60bb7ffc8e0100000002516513e343a5c1dc1c80cd4561e9dddad22391a2dbf9c8d2b6048e519343ca1925a9c6f0800a020000000665516365ac513180144a0290db27000000000006ab655151ab5138b187010000000007ab5363abac516a9e5cd98a", "53ac", 0, 478591320, "e8d89a302ae626898d4775d103867a8d9e81f4fd387af07212adab99946311ef"], + ["c9270fe004c7911b791a00999d108ce42f9f1b19ec59143f7b7b04a67400888808487bd59103000000066a0052ac6565b905e76687be2dd7723b22c5e8269bc0f2000a332a289cfc40bc0d617cfe3214a61a85a30300000007ac63ac00635251560871209f21eb0268f175b8b4a06edd0b04162a974cf8b5dada43e499a1f22380d35ede0300000000792213fc58b6342cc8100079f9f5f046fb89f2d92cf0a2cb6d07304d32d9da858757037c0000000008abab51636565516affffffff02c72a8b03000000000452acac530dfb9f05000000000096f94307", "5253ab536351", 3, 543688436, "0278adbcc476d135493ae9bdcd7b3c2002df17f2d81c17d631c50c73e546c264"], + ["57a5a04c0278c8c8e243d2df4bb716f81d41ac41e2df153e7096f5682380c4f441888d9d260300000004ab63ab6afdbe4203525dff42a7b1e628fe22bccaa5edbb34d8ab02faff198e085580ea5fcdb0c61b0000000002ac6affffffff03375e6c05000000000663ab516a6a513cb6260400000000007ca328020000000006516a636a52ab94701cc7", "0053ac5152", 0, -550925626, "b7ca991ab2e20d0158168df2d3dd842a57ab4a3b67cca8f45b07c4b7d1d11126"], + ["072b75a504ad2550c2e9a02614bc9b2a2f50b5b553af7b87c0ef07c64ddc8d8934c96d216401000000036aabaca1387242a5bcd21099b016ad6045bed7dce603472757d9822cc5f602caa4ae20414d378b02000000026a63e4ac816734acdc969538d6f70b8ab43a2589f55e0177a4dc471bdd0eb61d59f0f46f6bb801000000065351526aab52d9f2977be76a492c3a7617b7a16dc29a3b0a7618f328c2f7d4fd9bafe760dc427a5066ef000000000465635165ffffffff02c5793600000000000165296820050000000002ac6300000000", "53006a6aac0052ab", 2, 66084636, "437e89bb6f70fd2ed2feef33350b6f6483b891305e574da03e580b3efd81ae13"], + ["7e27c42d0279c1a05eeb9b9faedcc9be0cab6303bde351a19e5cbb26dd0d594b9d74f40d2b020000000200518c8689a08a01e862d5c4dcb294a2331912ff11c13785be7dce3092f154a005624970f84e0200000000500cf5a601e74c1f0000000000076aab52636a6a5200000000", "6500006a5351", 0, 449533391, "535ba819d74770d4d613ee19369001576f98837e18e1777b8246238ff2381dd0"], + ["11414de403d7f6c0135a9df01cb108c1359b8d4e105be50a3dcba5e6be595c8817217490b20000000003005263ffffffff0c6becb9c3ad301c8dcd92f5cbc07c8bed7973573806d1489316fc77a829da03030000000700005253535352ffffffff2346d74ff9e12e5111aa8779a2025981850d4bf788a48de72baa2e321e4bc9ca00000000056352acab63cc585b64045e0385050000000009ab5253ab516aacac00efa9cf0300000000065200635151acbe80330400000000070063635100ab000be159050000000007525300655300ac00000000", "51656a0051ab", 0, 683137826, "d4737f3b58f3e5081b35f36f91acde89dda00a6a09d447e516b523e7a99264d5"], + ["1c6b5f29033fc139338658237a42456123727c8430019ca25bd71c6168a9e35a2bf54538d80100000008536aac52ac6a6a52ffffffff3fb36be74036ff0c940a0247c451d923c65f826793d0ac2bb3f01ecbec8033290100000007ab000051ab6363ffffffff5d9eca0cf711685105bd060bf7a67321eaef95367acffab36ce8dedddd632ee2000000000652ac6a63ac517167319e032d26de040000000003516363dc38fb010000000000b37b00000000000006ab520051ac534baba51f", "636300ababac6563", 0, -2049129935, "3282a2ec6b8c87c9303e6060c17b421687db1bd35fbfa0345b48f2490e15b6cc"], + ["978b9dad0214cfc7ce392d74d9dcc507350dc34007d72e4125861c63071ebf2cc0a6fd4856020000000651ac6a6aab52ffffffff47f20734e3370e733f87a6edab95a7a268ae44db7a8974e255614836b22938720200000008635265ac51516553ffffffff0137b2560100000000035252ac2f3363e9", "006aab6352", 1, 2014249801, "55611a5fb1483bce4c14c33ed15198130e788b72cd8929b2ceef4dd68b1806bf"], + ["442f1c8703ab39876153c241ab3d69f432ba6db4732bea5002be45c8ca10c3a2356fe0e9590300000001accb2b679cab7c58a660cb6d4b3452c21cd7251a1b77a52c300f655f5baeb6fa27ff5b79880300000003005252e5ccf55712bc8ed6179f6726f8a78f3018a7a0391594b7e286ef5ee99efdcde302a102cc0200000009006352526351536a63ffffffff04443f63030000000006536a63ab63651405fb020000000009ac535351525300ab6a9f172b000000000004ab535263ad5c50050000000008656a65ab630000ac00000000", "65636aab006552", 2, 2125838294, "b3ff10f21e71ebc8b25fe058c4074c42f08617e0dcc03f9e75d20539d3242644"], + ["2b3470dd028083910117f86614cdcfb459ee56d876572510be4df24c72e8f58c70d5f5948b03000000066aab65635265da2c3aac9d42c9baafd4b655c2f3efc181784d8cba5418e053482132ee798408ba43ccf90300000000ffffffff047dda4703000000000765516a52ac53009384a603000000000651636a63ab6a8cf57a03000000000352ab6a8cf6a405000000000952636a6a6565525100661e09cb", "ac520063ac6a6a52", 1, 1405647183, "9b360c3310d55c845ef537125662b9fe56840c72136891274e9fedfef56f9bb5"], + ["d74282b501be95d3c19a5d9da3d49c8a88a7049c573f3788f2c42fc6fa594f59715560b9b00000000009655353525265ac52ac9772121f028f8303030000000003510065af5f47040000000007ac516a6551630000000000", "acab53006363ac", 0, -1113209770, "2f482b97178f17286f693796a756f4d7bd2dfcdbecd4142528eec1c7a3e5101a"], + ["3a5644a9010f199f253f858d65782d3caec0ac64c3262b56893022b9796086275c9d4d097b02000000009d168f7603a67b30050000000007ac51536a0053acd9d88a050000000007655363535263ab3cf1f403000000000352ac6a00000000", "005363536565acac6a", 0, -1383947195, "6390ab0963cf611e0cea35a71dc958b494b084e6fd71d22217fdc5524787ade6"], + ["67b3cc43049d13007485a8133b90d94648bcf30e83ba174f5486ab42c9107c69c5530c5e1f0000000003005100ffffffff9870ebb65c14263282ea8d41e4f4f40df16b565c2cf86f1d22a9494cad03a67f01000000016a5a121bee5e359da548e808ae1ad6dfccae7c67cbb8898d811638a1f455a671e822f228ef030000000151c1fcc9f9825f27c0dde27ea709da62a80a2ff9f6b1b86a5874c50d6c37d39ae31fb6c8a0030000000163553b8786020ca74a00000000000665635153ab5275c0760000000000020052e659b05d", "636aab6a6a", 0, -342795451, "f77c3322c97b1681c17b1eba461fa27b07e04c1534e8aaf735a49cab72c7c2e2"], + ["bda1ff6804a3c228b7a12799a4c20917301dd501c67847d35da497533a606701ad31bf9d5e0300000001ac16a6c5d03cf516cd7364e4cbbf5aeccd62f8fd03cb6675883a0636a7daeb650423cb1291010000000500656553ac4a63c30b6a835606909c9efbae1b2597e9db020c5ecfc0642da6dc583fba4e84167539a8020000000865525353515200acffffffff990807720a5803c305b7da08a9f24b92abe343c42ac9e917a84e1f335aad785d00000000026a52ffffffff04981f20030000000001ab8c762200000000000253ab690b9605000000000151ce88b301000000000753526a6a51006500000000", "000052ac52530000", 1, -1809193140, "5299b0fb7fc16f40a5d6b337e71fcd1eb04d2600aefd22c06fe9c71fe0b0ba54"], + ["2ead28ff0243b3ab285e5d1067f0ec8724224402b21b9cef9be962a8b0d153d401be99bbee0000000004ac635153ffffffff6985987b7c1360c9fa8406dd6e0a61141709f0d5195f946da55ed83be4e3895301000000020053ffffffff016503d20500000000085251ac6a65656a6a00000000", "51abab", 1, 1723793403, "67483ee62516be17a2431a163e96fd88a08ff2ce8634a52e42c1bc04e30f3f8a"], + ["db4904e6026b6dd8d898f278c6428a176410d1ffbde75a4fa37cda12263108ccd4ca6137440100000007656a0000515263ffffffff1db7d5005c1c40da0ed17b74cf6b2a6ee2c33c9e0bacda76c0da2017dcac2fc70200000004abab6a53ffffffff0454cf2103000000000153463aef000000000009ab6a630065ab52636387e0ed050000000000e8d16f05000000000352ac63e4521b22", "", 1, 1027042424, "48315a95e49277ab6a2d561ee4626820b7bab919eea372b6bf4e9931ab221d04"], + ["dca31ad10461ead74751e83d9a81dcee08db778d3d79ad9a6d079cfdb93919ac1b0b61871102000000086500525365ab51ac7f7e9aed78e1ef8d213d40a1c50145403d196019985c837ffe83836222fe3e5955e177e70100000006525152525300ffffffff5e98482883cc08a6fe946f674cca479822f0576a43bf4113de9cbf414ca628060100000006ac53516a5253ffffffff07490b0b898198ec16c23b75d606e14fa16aa3107ef9818594f72d5776805ec502000000036a0052ffffffff01932a2803000000000865ab6551ac6a516a2687aa06", "635300ac", 2, -1880362326, "74d6a2fa7866fd8b74b2e34693e2d6fd690410384b7afdcd6461b1ae71d265ce"], + ["e14e1a9f0442ab44dfc5f6d945ad1ff8a376bc966aad5515421e96ddbe49e529614995cafc03000000055165515165fffffffff97582b8290e5a5cfeb2b0f018882dbe1b43f60b7f45e4dd21dbd3a8b0cfca3b0200000000daa267726fe075db282d694b9fee7d6216d17a8c1f00b2229085495c5dc5b260c8f8cd5d000000000363ac6affffffffaab083d22d0465471c896a438c6ac3abf4d383ae79420617a8e0ba8b9baa872b010000000963526563ac5363ababd948b5ce022113440200000000076a636552006a53229017040000000000e6f62ac8", "526353636a65", 3, -485265025, "1bc8ad76f9b7c366c5d052dc479d6a8a2015566d3a42e93ab12f727692c89d65"], + ["720d4693025ca3d347360e219e9bc746ef8f7bc88e8795162e5e2f0b0fc99dc17116fc937100000000046353520045cb1fd79824a100d30b6946eab9b219daea2b0cdca6c86367c0c36af98f19ac64f3575002000000008a1c881003ed16f3050000000008536a63630000abac45e0e704000000000151f6551a05000000000963536565515363abab00000000", "6553ab6a6a510000ab", 1, 1249091393, "a575fa4f59a8e90cd07de012c78fe8f981183bb170b9c50fcc292b8c164cbc3b"], + ["69df842a04c1410bfca10896467ce664cfa31c681a5dac10106b34d4b9d4d6d0dc1eac01c1000000000551536a5165269835ca4ad7268667b16d0a2df154ec81e304290d5ed69e0069b43f8c89e673328005e200000000076a5153006aacabffffffffc9314bd80b176488f3d634360fcba90c3a659e74a52e100ac91d3897072e3509010000000765abac51636363ffffffff0e0768b13f10f0fbd2fa3f68e4b4841809b3b5ba0e53987c3aaffcf09eee12bf0300000008ac535263526a53ac514f4c2402da8fab0400000000001ef15201000000000451526a52d0ec9aca", "525365ac52", 1, 313967049, "a72a760b361af41832d2c667c7488dc9702091918d11e344afc234a4aea3ec44"], + ["adf2340d03af5c589cb5d28c06635ac07dd0757b884d4777ba85a6a7c410408ad5efa8b19001000000045100ab00ffffffff808dc0231c96e6667c04786865727013922bcb7db20739b686f0c17f5ba70e8f0300000000fd2332a654b580881a5e2bfec8313f5aa878ae94312f37441bf2d226e7fc953dcf0c77ab000000000163aa73dc580412f8c2050000000005636aacac63da02d502000000000153e74b52020000000001536b293d030000000009636552ababacab526500000000", "000052ab52ababab", 0, -568651175, "2c45d021db545df7167ac03c9ee56473f2398d9b2b739cf3ff3e074501d324f8"], + ["e4fec9f10378a95199c1dd23c6228732c9de0d7997bf1c83918a5cfd36012476c0c3cba24002000000085165536500ac0000ad08ab93fb49d77d12a7ccdbb596bc5110876451b53a79fdce43104ff1c316ad63501de801000000046a6352ab76af9908463444aeecd32516a04dd5803e02680ed7f16307242a794024d93287595250f4000000000089807279041a82e603000000000200521429100200000000055253636a63f20b940400000000004049ed04000000000500ab5265ab43dfaf7d", "6563526aac", 2, -1923470368, "32f3c012eca9a823bebb9b282240aec40ca65df9f38da43b1dcfa0cac0c0df7e"], + ["4000d3600100b7a3ff5b41ec8d6ccdc8b2775ad034765bad505192f05d1f55d2bc39d0cbe10100000007ab5165ac6a5163ffffffff034949150100000000026a6a92c9f6000000000008ab6553ab6aab635200e697040000000007636a5353525365237ae7d2", "52000063", 0, -880046683, "c76146f68f43037289aaeb2bacf47408cddc0fb326b350eb4f5ef6f0f8564793"], + ["eabc0aa701fe489c0e4e6222d72b52f083166b49d63ad1410fb98caed027b6a71c02ab830c03000000075253ab63530065ffffffff01a5dc0b05000000000253533e820177", "", 0, 954499283, "1d849b92eedb9bf26bd4ced52ce9cb0595164295b0526842ab1096001fcd31b1"], + ["d48d55d304aad0139783b44789a771539d052db565379f668def5084daba0dfd348f7dcf6b00000000006826f59e5ffba0dd0ccbac89c1e2d69a346531d7f995dea2ca6d7e6d9225d81aec257c6003000000096a655200ac656552acffffffffa188ffbd5365cae844c8e0dea6213c4d1b2407274ae287b769ab0bf293e049eb0300000005ac6a6aab51ad1c407c5b116ca8f65ed496b476183f85f072c5f8a0193a4273e2015b1cc288bf03e9e2030000000252abffffffff04076f44040000000006655353abab53be6500050000000003ac65ac3c15040500000000095100ab536353516a52ed3aba04000000000900ac53ab53636aabac00000000", "5253526563acac", 2, -1506108646, "bbee17c8582514744bab5df50012c94b0db4aff5984d2e13a8d09421674404e2"], + ["9746f45b039bfe723258fdb6be77eb85917af808211eb9d43b15475ee0b01253d33fc3bfc502000000065163006a655312b12562dc9c54e11299210266428632a7d0ee31d04dfc7375dcad2da6e9c11947ced0e000000000009074095a5ac4df057554566dd04740c61490e1d3826000ad9d8f777a93373c8dddc4918a00000000025351ffffffff01287564030000000004636a00ab00000000", "52", 2, -1380411075, "84af1623366c4db68d81f452b86346832344734492b9c23fbb89015e516c60b2"], + ["8731b64903d735ba16da64af537eaf487b57d73977f390baac57c7b567cb2770dfa2ef65870100000001635aedd990c42645482340eacb0bfa4a0a9e888057389c728b5b6a8691cdeb1a6a67b45e140200000008ac53526a52516551ffffffff45c4f567c47b8d999916fd49642cbc5d10d43c304b99e32d044d35091679cb860100000003006a51ffffffff0176d6c200000000000000000000", "ab6a65ab53", 2, -1221546710, "ccfdba36d9445f4451fb7cbf0752cc89c23d4fc6fff0f3930d20e116f9db0b95"], + ["f5cfc52f016209ab1385e890c2865a74e93076595d1ca77cbe8fbf2022a2f2061a90fb0f3e010000000253acffffffff027de73f0200000000085252ac510052acac49cd6a020000000000e6c2cb56", "516552535300ab63", 0, -1195302704, "5532717402a2da01a1da912d824964024185ca7e8d4ad1748659dc393a14182b"], + ["df0a32ae01c4672fd1abd0b2623aae0a1a8256028df57e532f9a472d1a9ceb194267b6ee190200000009536a6a51516a525251b545f9e803469a2302000000000465526500810631040000000000441f5b050000000006530051006aaceb183c76", "536a635252ac6a", 0, 1601138113, "9a0435996cc58bdba09643927fe48c1fc908d491a050abbef8daec87f323c58f"], + ["d102d10c028b9c721abb259fe70bc68962f6cae384dabd77477c59cbeb1fb26266e091ba3e0100000002516affffffffe8d7305a74f43e30c772109849f4cd6fb867c7216e6d92e27605e69a0818899700000000026a65ecf82d58027db4620500000000026552c28ed3010000000001ab00000000", "0051ab515365", 1, -131815460, "1d1757a782cb5860302128bcbe9398243124a2f82d671a113f74f8e582c7a182"], + ["cef930ed01c36fcb1d62ceef931bef57098f27a77a4299904cc0cbb44504802d535fb11557010000000153ffffffff02c8657403000000000863ac655253520063d593380400000000046aab536a00000000", "656a0051ab6365ab53", 0, -351313308, "e69dba3efb5c02af2ab1087d0a990678784671f4744d01ca097d71aec14dd8e9"], + ["b1c0b71804dff30812b92eefb533ac77c4b9fdb9ab2f77120a76128d7da43ad70c20bbfb990200000002536392693e6001bc59411aebf15a3dc62a6566ec71a302141b0c730a3ecc8de5d76538b30f55010000000665535252ac514b740c6271fb9fe69fdf82bf98b459a7faa8a3b62f3af34943ad55df4881e0d93d3ce0ac0200000000c4158866eb9fb73da252102d1e64a3ce611b52e873533be43e6883137d0aaa0f63966f060000000001abffffffff04a605b604000000000851006a656a630052f49a0300000000000252515a94e1050000000009abac65ab0052abab00fd8dd002000000000651535163526a2566852d", "ac5363", 0, -1718831517, "b0dc030661783dd9939e4bf1a6dfcba809da2017e1b315a6312e5942d714cf05"], + ["6a270ee404ebc8d137cfd4bb6b92aa3702213a3139a579c1fc6f56fbc7edd9574ef17b13f30100000009ab00ab656565ababacffffffffaa65b1ab6c6d87260d9e27a472edceb7dd212483e72d90f08857abf1dbfd46d10100000000fffffffff93c4c9c84c4dbbe8a912b99a2830cfe3401aebc919041de063d660e585fc9f002000000096aabacab52ac6a53acfa6dcef3f28355a8d98eee53839455445eeee83eecd2c854e784efa53cee699dbfecaebd0100000003ab6a51ffffffff04f7d71b050000000009ac6a536aac6a6365513c37650500000000065265abab6a53fa742002000000000039ed82030000000009516aac635165ab51ab2fdabd17", "ab535252526563", 1, -1326210506, "1dec0d5eb921bf5b2df39c8576e19c38d0c17254a4a0b78ac4b5422bcc426258"], + ["3657e4260304ccdc19936e47bdf058d36167ee3d4eb145c52b224eff04c9eb5d1b4e434dfc0000000001ab58aefe57707c66328d3cceef2e6f56ab6b7465e587410c5f73555a513ace2b232793a74400000000036a006522e69d3a785b61ad41a635d59b3a06b2780a92173f85f8ed428491d0aaa436619baa9c4501000000046351abab2609629902eb7793050000000000a1b967040000000003525353a34d6192", "516a", 0, -1761874713, "0a2ff41f6d155d8d0e37cd9438f3b270df9f9214cda8e95c76d5a239ca189df2"], + ["a0eb6dc402994e493c787b45d1f946d267b09c596c5edde043e620ce3d59e95b2b5b93d43002000000096a5252526aac63ab6555694287a279e29ee491c177a801cd685b8744a2eab83824255a3bcd08fc0e3ea13fb8820000000009abab6365ab52ab0063ffffffff029e424a040000000008acab53ab516a636a23830f0400000000016adf49c1f9", "ac0065ac6500005252", 1, 669294500, "e05e3d383631a7ed1b78210c13c2eb26564e5577db7ddfcea2583c7c014091d4"], + ["6e67c0d3027701ef71082204c85ed63c700ef1400c65efb62ce3580d187fb348376a23e9710200000001655b91369d3155ba916a0bc6fe4f5d94cad461d899bb8aaac3699a755838bfc229d6828920010000000765536353526a52ffffffff04c0c792000000000005650052535372f79e000000000001527fc0ee010000000005ac5300ab65d1b3e902000000000251aba942b278", "6a5151", 0, 1741407676, "e657e2c8ec4ebc769ddd3198a83267b47d4f2a419fc737e813812acefad92ff7"], + ["8f53639901f1d643e01fc631f632b7a16e831d846a0184cdcda289b8fa7767f0c292eb221a00000000046a53abacffffffff037a2daa01000000000553ac6a6a51eac349020000000005ac526552638421b3040000000007006a005100ac63048a1492", "ac65", 0, 1033685559, "da86c260d42a692358f46893d6f91563985d86eeb9ea9e21cd38c2d8ffcfcc4d"], + ["491f99cb01bdfba1aa235e5538dac081fae9ce55f9622de483afe7e65105c2b0db75d360d200000000045251636340b60f0f041421330300000000096351ac000051636553ce2822040000000005516a00ac5180c8e40300000000025100caa8570400000000020000cfdc8da6", "6a5100516aab655365", 0, -953727341, "397c68803b7ce953666830b0221a5e2bcf897aa2ded8e36a6b76c497dcb1a2e1"], + ["b3cad3a7041c2c17d90a2cd994f6c37307753fa3635e9ef05ab8b1ff121ca11239a0902e700300000009ab635300006aac5163ffffffffcec91722c7468156dce4664f3c783afef147f0e6f80739c83b5f09d5a09a57040200000004516a6552ffffffff969d1c6daf8ef53a70b7cdf1b4102fb3240055a8eaeaed2489617cd84cfd56cf020000000352ab53ffffffff46598b6579494a77b593681c33422a99559b9993d77ca2fa97833508b0c169f80200000009655300655365516351ffffffff04d7ddf800000000000853536a65ac6351ab09f3420300000000056aab65abac33589d04000000000952656a65655151acac944d6f0400000000006a8004ba", "005165", 1, 1035865506, "fe1dc9e8554deecf8f50c417c670b839cc9d650722ebaaf36572418756075d58"], + ["e1cfd73b0125add9e9d699f5a45dca458355af175a7bd4486ebef28f1928d87864384d02df02000000036a0051ffffffff0357df030100000000036a5365777e2d04000000000763ab6a00005265f434a601000000000351655100000000", "ab53ab", 0, -1936500914, "950f4b4f72ccdf8a6a0f381265d6c8842fdb7e8b3df3e9742905f643b2432b69"], + ["cf781855040a755f5ba85eef93837236b34a5d3daeb2dbbdcf58bb811828d806ed05754ab8010000000351ac53ffffffffda1e264727cf55c67f06ebcc56dfe7fa12ac2a994fecd0180ce09ee15c480f7d00000000096351516a51acac00ab53dd49ff9f334befd6d6f87f1a832cddfd826a90b78fd8cf19a52cb8287788af94e939d6020000000700525251ac526310d54a7e8900ed633f0f6f0841145aae7ee0cbbb1e2a0cae724ee4558dbabfdc58ba6855010000000552536a53abfd1b101102c51f910500000000096300656a525252656a300bee010000000009ac52005263635151abe19235c9", "53005365", 2, 1422854188, "d5981bd4467817c1330da72ddb8760d6c2556cd809264b2d85e6d274609fc3a3"], + ["fea256ce01272d125e577c0a09570a71366898280dda279b021000db1325f27edda41a53460100000002ab53c752c21c013c2b3a01000000000000000000", "65", 0, 1145543262, "076b9f844f6ae429de228a2c337c704df1652c292b6c6494882190638dad9efd"] +] diff --git a/vendor/github.com/btcsuite/btcd/txscript/data/tx_invalid.json b/vendor/github.com/btcsuite/btcd/txscript/data/tx_invalid.json new file mode 100644 index 0000000000000000000000000000000000000000..983c873ac15d5a9625a1a597659a8041c84d7f63 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/data/tx_invalid.json @@ -0,0 +1,195 @@ +[ +["The following are deserialized transactions which are invalid."], +["They are in the form"], +["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], +["serializedTransaction, verifyFlags]"], +["Objects that are only a single string (like this one) are ignored"], + +["0e1b5688cf179cd9f7cbda1fac0090f6e684bbf8cd946660120197c3f3681809 but with extra junk appended to the end of the scriptPubKey"], +[[["6ca7ec7b1847f6bdbd737176050e6a08d66ccd55bb94ad24f4018024107a5827", 0, "0x41 0x043b640e983c9690a14c039a2037ecc3467b27a0dcd58f19d76c7bc118d09fec45adc5370a1c5bf8067ca9f5557a4cf885fdb0fe0dcc9c3a7137226106fbc779a5 CHECKSIG VERIFY 1"]], +"010000000127587a10248001f424ad94bb55cd6cd6086a0e05767173bdbdf647187beca76c000000004948304502201b822ad10d6adc1a341ae8835be3f70a25201bbff31f59cbb9c5353a5f0eca18022100ea7b2f7074e9aa9cf70aa8d0ffee13e6b45dddabf1ab961bda378bcdb778fa4701ffffffff0100f2052a010000001976a914fc50c5907d86fed474ba5ce8b12a66e0a4c139d888ac00000000", "P2SH"], + +["This is the nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG from tx_valid.json"], +["but with the signature duplicated in the scriptPubKey with a non-standard pushdata prefix"], +["See FindAndDelete, which will only remove if it uses the same pushdata prefix as is standard"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"], + +["Same as above, but with the sig in the scriptSig also pushed with the same non-standard OP_PUSHDATA"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006b4c473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"], + +["This is the nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG from tx_valid.json"], +["but with the signature duplicated in the scriptPubKey with a different hashtype suffix"], +["See FindAndDelete, which will only remove if the signature, including the hash type, matches"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a81"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"], + +["An invalid P2SH Transaction"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "P2SH"], + +["Tests for CheckTransaction()"], +["No inputs"], +["Skipped because this is not checked by btcscript, this is a problem for chain."], + +["No outputs"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", "P2SH"], + +["Negative output"], +["Removed because btcscript doesn't do tx sanity checking."], + +["MAX_MONEY + 1 output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010140075af0750700015100000000", "P2SH"], + +["MAX_MONEY output + 1 output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510001000000000000015100000000", "P2SH"], + +["Duplicate inputs"], +["Removed because btcscript doesn't check input duplication, btcchain does"], + +["Coinbase of size 1"], +["Note the input is just required to make the tester happy"], +["Removed because btcscript doesn't handle coinbase checking, btcchain does"], + +["Coinbase of size 101"], +["Note the input is just required to make the tester happy"], +["Removed because btcscript doesn't handle coinbase checking, btcchain does"], + +["Null txin"], +["Removed because btcscript doesn't do tx sanity checking."], + +["Same as the transactions in valid with one input SIGHASH_ALL and one SIGHASH_ANYONECANPAY, but we set the _ANYONECANPAY sequence number, invalidating the SIGHASH_ALL signature"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], + ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], + "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df10101000000000200000000000000000000000000000000000000000000000000000000000000000000484730440220201dc2d030e380e8f9cfb41b442d930fa5a685bb2c8db5906671f865507d0670022018d9e7a8d4c8d86a73c2a724ee38ef983ec249827e0e464841735955c707ece98101000000010100000000000000015100000000", "P2SH"], + +["CHECKMULTISIG with incorrect signature order"], +["Note the input is just required to make the tester happy"], +[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], +"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe000048304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f401483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", "P2SH"], + + +["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is an OP_CHECKMULTISIG with the dummy value missing"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], + + +["CHECKMULTISIG SCRIPT_VERIFY_NULLDUMMY tests:"], + +["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is an OP_CHECKMULTISIG with the dummy value set to something other than an empty string"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a010047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], + +["As above, but using a OP_1"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], + +["As above, but using a OP_1NEGATE"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], + +["As above, but with the dummy byte missing"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], + + +["Empty stack when we try to run CHECKSIG"], +[[["ad503f72c18df5801ee64d76090afe4c607fb2b822e9b7b63c5826c50e22fc3b", 0, "0x21 0x027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5 CHECKSIG NOT"]], +"01000000013bfc220ec526583cb6b7e922b8b27f604cfe0a09764de61e80f58dc1723f50ad0000000000ffffffff0101000000000000002321027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5ac00000000", "P2SH"], + + +["Inverted versions of tx_valid CODESEPARATOR IF block tests"], + +["CODESEPARATOR in an unexecuted IF block does not change what is hashed"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], +"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0151ffffffff010000000000000000016a00000000", "P2SH"], + +["As above, with the IF block executed"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], +"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510100ffffffff010000000000000000016a00000000", "P2SH"], + +["CHECKLOCKTIMEVERIFY tests"], + +["By-height locks, with argument just beyond tx nLockTime"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000fe64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], + +["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000001 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument missing"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000001b1010000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument negative with by-blockheight nLockTime=0"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument negative with by-blocktime nLockTime=500,000,000"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000004005194b1010000000100000000000000000002000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Input locked"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1ffffffff0100000000000000000002000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Another input being unlocked isn't sufficient; the CHECKLOCKTIMEVERIFY-using input must be unlocked"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"] , + ["0000000000000000000000000000000000000000000000000000000000000200", 1, "1"]], +"010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00020000000000000000000000000000000000000000000000000000000000000100000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument/tx height/time mismatch, both versions"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b100000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument 2^32 with nLockTime=2^32-1"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967296 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Same, but with nLockTime=2^31-1"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffff7f", "P2SH,CHECKLOCKTIMEVERIFY"], + +["6 byte non-minimally-encoded arguments are invalid even if their contents are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x06 0x000000000000 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Failure due to failing CHECKLOCKTIMEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Failure due to failing CHECKLOCKTIMEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["A transaction with a non-standard DER signature."], +[[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]], +"010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "P2SH,DERSIG"], + +["Make diffs cleaner by leaving a comment here without comma at the end"] +] diff --git a/vendor/github.com/btcsuite/btcd/txscript/data/tx_valid.json b/vendor/github.com/btcsuite/btcd/txscript/data/tx_valid.json new file mode 100644 index 0000000000000000000000000000000000000000..0dfef73ae5e2bf3c2adf38fbe88184ebb4171424 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/data/tx_valid.json @@ -0,0 +1,237 @@ +[ +["The following are deserialized transactions which are valid."], +["They are in the form"], +["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], +["serializedTransaction, verifyFlags]"], +["Objects that are only a single string (like this one) are ignored"], + +["The following is 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is of particular interest because it contains an invalidly-encoded signature which OpenSSL accepts"], +["See http://r6.ca/blog/20111119T211504Z.html"], +["It is also the first OP_CHECKMULTISIG transaction in standard form"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], + +["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is an OP_CHECKMULTISIG with an arbitrary extra byte stuffed into the signature at pos length - 2"], +["The dummy byte is fine however, so the NULLDUMMY flag should be happy"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], + +["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is an OP_CHECKMULTISIG with the dummy value set to something other than an empty string"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], + +["As above, but using a OP_1"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], + +["As above, but using a OP_1NEGATE"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], + +["The following is c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"], +["It is of interest because it contains a 0-sequence as well as a signature of SIGHASH type 0 (which is not a real type)"], +[[["406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602", 0, "DUP HASH160 0x14 0xdc44b1164188067c3a32d4780f5996fa14a4f2d9 EQUALVERIFY CHECKSIG"]], +"01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000", "P2SH"], + +["A nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"], + +["Same as above, but with the signature duplicated in the scriptPubKey with the proper pushdata prefix"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"], + +["The following is f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"], +["It caught a bug in the workaround for 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63 in an overly simple implementation"], +[[["b464e85df2a238416f8bdae11d120add610380ea07f4ef19c5f9dfd472f96c3d", 0, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"], +["b7978cc96e59a8b13e0865d3f95657561a7f725be952438637475920bac9eb21", 1, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"]], +"01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000", "P2SH"], + +["The following tests for the presence of a bug in the handling of SIGHASH_SINGLE"], +["It results in signing the constant 1, instead of something generated based on the transaction,"], +["when the input doing the signing has an index greater than the maximum output index"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0xe52b482f2faa8ecbf0db344f93c84ac908557f33 EQUALVERIFY CHECKSIG"], ["0000000000000000000000000000000000000000000000000000000000000200", 0, "1"]], +"01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000", "P2SH"], + +["An invalid P2SH Transaction"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "NONE"], + +["A valid P2SH Transaction using the standard transaction type put forth in BIP 16"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x8febbed40483661de6958d957412f82deed8e2f7 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000", "P2SH"], + +["Tests for CheckTransaction()"], +["MAX_MONEY output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000", "P2SH"], + +["MAX_MONEY output + 0 output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510000000000000000015100000000", "P2SH"], + +["Coinbase of size 2"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff025151ffffffff010000000000000000015100000000", "P2SH"], + +["Coinbase of size 100"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6451515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", "P2SH"], + +["Simple transaction with first input is signed with SIGHASH_ALL, second with SIGHASH_ANYONECANPAY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], + ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], + "010000000200010000000000000000000000000000000000000000000000000000000000000000000049483045022100d180fd2eb9140aeb4210c9204d3f358766eb53842b2a9473db687fa24b12a3cc022079781799cd4f038b85135bbe49ec2b57f306b2bb17101b17f71f000fcab2b6fb01ffffffff0002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", "P2SH"], + +["Same as above, but we change the sequence number of the first input to check that SIGHASH_ANYONECANPAY is being followed"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], + ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], + "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", "P2SH"], + +["afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae which has several SIGHASH_SINGLE signatures"], +[[["63cfa5a09dc540bf63e53713b82d9ea3692ca97cd608c384f2aa88e51a0aac70", 0, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"], + ["04e8d0fcf3846c6734477b98f0f3d4badfb78f020ee097a0be5fe347645b817d", 1, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"], + ["ee1377aff5d0579909e11782e1d2f5f7b84d26537be7f5516dd4e43373091f3f", 1, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"]], + "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000", "P2SH"], + + ["ddc454a1c0c35c188c98976b17670f69e586d9c0f3593ea879928332f0a069e7, which spends an input that pushes using a PUSHDATA1 that is negative when read as signed"], + [[["c5510a5dd97a25f43175af1fe649b707b1df8e1a41489bac33a23087027a2f48", 0, "0x4c 0xae 0x606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e207460 DROP DUP HASH160 0x14 0xbfd7436b6265aa9de506f8a994f881ff08cc2872 EQUALVERIFY CHECKSIG"]], + "0100000001482f7a028730a233ac9b48411a8edfb107b749e61faf7531f4257ad95d0a51c5000000008b483045022100bf0bbae9bde51ad2b222e87fbf67530fbafc25c903519a1e5dcc52a32ff5844e022028c4d9ad49b006dd59974372a54291d5764be541574bb0c4dc208ec51f80b7190141049dd4aad62741dc27d5f267f7b70682eee22e7e9c1923b9c0957bdae0b96374569b460eb8d5b40d972e8c7c0ad441de3d94c4a29864b212d56050acb980b72b2bffffffff0180969800000000001976a914e336d0017a9d28de99d16472f6ca6d5a3a8ebc9988ac00000000", "P2SH"], + +["Correct signature order"], +["Note the input is just required to make the tester happy"], +[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], +"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe0000483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa0148304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f4014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", "P2SH"], + +["cc60b1f899ec0a69b7c3f25ddf32c4524096a9c5b01cbd84c6d0312a0c478984, which is a fairly strange transaction which relies on OP_CHECKSIG returning 0 when checking a completely invalid sig of length 0"], +[[["cbebc4da731e8995fe97f6fadcd731b36ad40e5ecb31e38e904f6e5982fa09f7", 0, "0x2102085c6600657566acc2d6382a47bc3f324008d2aa10940dd7705a48aa2a5a5e33ac7c2103f5d0fb955f95dd6be6115ce85661db412ec6a08abcbfce7da0ba8297c6cc0ec4ac7c5379a820d68df9e32a147cffa36193c6f7c43a1c8c69cda530e1c6db354bfabdcfefaf3c875379a820f531f3041d3136701ea09067c53e7159c8f9b2746a56c3d82966c54bbc553226879a5479827701200122a59a5379827701200122a59a6353798277537982778779679a68"]], +"0100000001f709fa82596e4f908ee331cb5e0ed46ab331d7dcfaf697fe95891e73dac4ebcb000000008c20ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568100201b1b01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c227f483045022100a9df60536df5733dd0de6bc921fab0b3eee6426501b43a228afa2c90072eb5ca02201c78b74266fac7d1db5deff080d8a403743203f109fbcabf6d5a760bf87386d20100ffffffff01c075790000000000232103611f9a45c18f28f06f19076ad571c344c82ce8fcfe34464cf8085217a2d294a6ac00000000", "P2SH"], + +["Empty pubkey"], +[[["229257c295e7f555421c1bfec8538dd30a4b5c37c1c8810bbe83cafa7811652c", 0, "0x00 CHECKSIG NOT"]], +"01000000012c651178faca83be0b81c8c1375c4b0ad38d53c8fe1b1c4255f5e795c25792220000000049483045022100d6044562284ac76c985018fc4a90127847708c9edb280996c507b28babdc4b2a02203d74eca3f1a4d1eea7ff77b528fde6d5dc324ec2dbfdb964ba885f643b9704cd01ffffffff010100000000000000232102c2410f8891ae918cab4ffc4bb4a3b0881be67c7a1e7faa8b5acf9ab8932ec30cac00000000", "P2SH"], + +["Empty signature"], +[[["9ca93cfd8e3806b9d9e2ba1cf64e3cc6946ee0119670b1796a09928d14ea25f7", 0, "0x21 0x028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02 CHECKSIG NOT"]], +"0100000001f725ea148d92096a79b1709611e06e94c63c4ef61cbae2d9b906388efd3ca99c000000000100ffffffff0101000000000000002321028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02ac00000000", "P2SH"], + +[[["444e00ed7840d41f20ecd9c11d3f91982326c731a02f3c05748414a4fa9e59be", 0, "1 0x00 0x21 0x02136b04758b0b6e363e7a6fbe83aaf527a153db2b060d36cc29f7f8309ba6e458 2 CHECKMULTISIG"]], +"0100000001be599efaa4148474053c2fa031c7262398913f1dc1d9ec201fd44078ed004e44000000004900473044022022b29706cb2ed9ef0cb3c97b72677ca2dfd7b4160f7b4beb3ba806aa856c401502202d1e52582412eba2ed474f1f437a427640306fd3838725fab173ade7fe4eae4a01ffffffff010100000000000000232103ac4bba7e7ca3e873eea49e08132ad30c7f03640b6539e9b59903cf14fd016bbbac00000000", "P2SH"], + +[[["e16abbe80bf30c080f63830c8dbf669deaef08957446e95940227d8c5e6db612", 0, "1 0x21 0x03905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9f 0x00 2 CHECKMULTISIG"]], +"010000000112b66d5e8c7d224059e946749508efea9d66bf8d0c83630f080cf30be8bb6ae100000000490047304402206ffe3f14caf38ad5c1544428e99da76ffa5455675ec8d9780fac215ca17953520220779502985e194d84baa36b9bd40a0dbd981163fa191eb884ae83fc5bd1c86b1101ffffffff010100000000000000232103905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9fac00000000", "P2SH"], + +[[["ebbcf4bfce13292bd791d6a65a2a858d59adbf737e387e40370d4e64cc70efb0", 0, "2 0x21 0x033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194 0x21 0x03a88b326f8767f4f192ce252afe33c94d25ab1d24f27f159b3cb3aa691ffe1423 2 CHECKMULTISIG NOT"]], +"0100000001b0ef70cc644e0d37407e387e73bfad598d852a5aa6d691d72b2913cebff4bceb000000004a00473044022068cd4851fc7f9a892ab910df7a24e616f293bcb5c5fbdfbc304a194b26b60fba022078e6da13d8cb881a22939b952c24f88b97afd06b4c47a47d7f804c9a352a6d6d0100ffffffff0101000000000000002321033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194ac00000000", "P2SH"], + +[[["ba4cd7ae2ad4d4d13ebfc8ab1d93a63e4a6563f25089a18bf0fc68f282aa88c1", 0, "2 0x21 0x037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1 0x21 0x02edc823cd634f2c4033d94f5755207cb6b60c4b1f1f056ad7471c47de5f2e4d50 2 CHECKMULTISIG NOT"]], +"0100000001c188aa82f268fcf08ba18950f263654a3ea6931dabc8bf3ed1d4d42aaed74cba000000004b0000483045022100940378576e069aca261a6b26fb38344e4497ca6751bb10905c76bb689f4222b002204833806b014c26fd801727b792b1260003c55710f87c5adbd7a9cb57446dbc9801ffffffff0101000000000000002321037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1ac00000000", "P2SH"], + + +["OP_CODESEPARATOR tests"], + +["Test that SignatureHash() removes OP_CODESEPARATOR with FindAndDelete()"], +[[["bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224", 0, "CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]], +"01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", "P2SH"], +[[["83e194f90b6ef21fa2e3a365b63794fb5daa844bdc9b25de30899fcfe7b01047", 0, "CODESEPARATOR CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]], +"01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", "P2SH"], + +["Hashed data starts at the CODESEPARATOR"], +[[["326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CODESEPARATOR CHECKSIG"]], +"01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", "P2SH"], + +["But only if execution has reached it"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]], +"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "P2SH"], + +["CODESEPARATOR in an unexecuted IF block does not change what is hashed"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], +"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000", "P2SH"], + +["As above, with the IF block executed"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], +"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000", "P2SH"], + + +["CHECKSIG is legal in scriptSigs"], +[[["ccf7f4053a02e653c36ac75c891b7496d0dc5ce5214f6c913d9cf8f1329ebee0", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], +"0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"], + +["Same semantics for OP_CODESEPARATOR"], +[[["10c9f0effe83e97f80f067de2b11c6a00c3088a4bce42c5ae761519af9306f3c", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], +"01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"], + +["Signatures are removed from the script they are in by FindAndDelete() in the CHECKSIG code; even multiple instances of one signature can be removed."], +[[["6056ebd549003b10cbbd915cea0d82209fe40b8617104be917a26fa92cbe3d6f", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], +"01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"], + +["That also includes ahead of the opcode being executed."], +[[["5a6b0021a6042a686b6b94abc36b387bef9109847774e8b1e51eb8cc55c53921", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], +"01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"], + +["Finally CHECKMULTISIG removes all signatures prior to hashing the script containing those signatures. In conjunction with the SIGHASH_SINGLE bug this lets us test whether or not FindAndDelete() is actually present in scriptPubKey/redeemScript evaluation by including a signature of the digest 0x01 We can compute in advance for our pubkey, embed it it in the scriptPubKey, and then also using a normal SIGHASH_ALL signature. If FindAndDelete() wasn't run, the 'bugged' signature would still be in the hashed script, and the normal signature would fail."], + +["Here's an example on mainnet within a P2SH redeemScript. Remarkably it's a standard transaction in <0.9"], +[[["b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], + ["ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742", 0, "HASH160 0x14 0xd8dacdadb7462ae15cd906f1878706d0da8660e6 EQUAL"]], +"0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000", "P2SH"], + +["Same idea, but with bare CHECKMULTISIG"], +[[["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], + ["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]], +"0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "P2SH"], + + +["CHECKLOCKTIMEVERIFY tests"], + +["By-height locks, with argument == 0 and == tx nLockTime"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], + +["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Any non-maxint nSequence is fine"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["The argument can be calculated rather than created directly by a PUSHDATA"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 1ADD NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Perhaps even by an ADD producing a 5-byte result that is out of bounds for other opcodes"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 2147483647 ADD NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "P2SH,CHECKLOCKTIMEVERIFY"], + +["5 byte non-minimally-encoded arguments are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x05 0x0000000000 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Valid CHECKLOCKTIMEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000001000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Valid CHECKLOCKTIMEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000001000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["A transaction with a non-standard DER signature."], +[[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]], +"010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "P2SH"], + +["Make diffs cleaner by leaving a comment here without comma at the end"] +] diff --git a/vendor/github.com/btcsuite/btcd/txscript/doc.go b/vendor/github.com/btcsuite/btcd/txscript/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..2f2b1ec6940933beef0ed11b91acdf58e99648a6 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/doc.go @@ -0,0 +1,39 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package txscript implements the bitcoin transaction script language. + +A complete description of the script language used by bitcoin can be found at +https://en.bitcoin.it/wiki/Script. The following only serves as a quick +overview to provide information on how to use the package. + +This package provides data structures and functions to parse and execute +bitcoin transaction scripts. + +Script Overview + +Bitcoin transaction scripts are written in a stack-base, FORTH-like language. + +The bitcoin script language consists of a number of opcodes which fall into +several categories such pushing and popping data to and from the stack, +performing basic and bitwise arithmetic, conditional branching, comparing +hashes, and checking cryptographic signatures. Scripts are processed from left +to right and intentionally do not provide loops. + +The vast majority of Bitcoin scripts at the time of this writing are of several +standard forms which consist of a spender providing a public key and a signature +which proves the spender owns the associated private key. This information +is used to prove the the spender is authorized to perform the transaction. + +One benefit of using a scripting language is added flexibility in specifying +what conditions must be met in order to spend bitcoins. + +Errors + +Errors returned by this package are of the form txscript.ErrStackX where X +indicates the specific error. See Variables in the package documentation for a +full list. +*/ +package txscript diff --git a/vendor/github.com/btcsuite/btcd/txscript/engine.go b/vendor/github.com/btcsuite/btcd/txscript/engine.go new file mode 100644 index 0000000000000000000000000000000000000000..97111eb16d24b62168a9fabcd5946bcd7285ce95 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/engine.go @@ -0,0 +1,648 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "fmt" + "math/big" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/wire" +) + +// ScriptFlags is a bitmask defining additional operations or tests that will be +// done when executing a script pair. +type ScriptFlags uint32 + +const ( + // ScriptBip16 defines whether the bip16 threshold has passed and thus + // pay-to-script hash transactions will be fully validated. + ScriptBip16 ScriptFlags = 1 << iota + + // ScriptStrictMultiSig defines whether to verify the stack item + // used by CHECKMULTISIG is zero length. + ScriptStrictMultiSig + + // ScriptDiscourageUpgradableNops defines whether to verify that + // NOP1 through NOP10 are reserved for future soft-fork upgrades. This + // flag must not be used for consensus critical code nor applied to + // blocks as this flag is only for stricter standard transaction + // checks. This flag is only applied when the above opcodes are + // executed. + ScriptDiscourageUpgradableNops + + // ScriptVerifyCheckLockTimeVerify defines whether to verify that + // a transaction output is spendable based on the locktime. + // This is BIP0065. + ScriptVerifyCheckLockTimeVerify + + // ScriptVerifyCleanStack defines that the stack must contain only + // one stack element after evaluation and that the element must be + // true if interpreted as a boolean. This is rule 6 of BIP0062. + // This flag should never be used without the ScriptBip16 flag. + ScriptVerifyCleanStack + + // ScriptVerifyDERSignatures defines that signatures are required + // to compily with the DER format. + ScriptVerifyDERSignatures + + // ScriptVerifyLowS defines that signtures are required to comply with + // the DER format and whose S value is <= order / 2. This is rule 5 + // of BIP0062. + ScriptVerifyLowS + + // ScriptVerifyMinimalData defines that signatures must use the smallest + // push operator. This is both rules 3 and 4 of BIP0062. + ScriptVerifyMinimalData + + // ScriptVerifySigPushOnly defines that signature scripts must contain + // only pushed data. This is rule 2 of BIP0062. + ScriptVerifySigPushOnly + + // ScriptVerifyStrictEncoding defines that signature scripts and + // public keys must follow the strict encoding requirements. + ScriptVerifyStrictEncoding +) + +const ( + // maxStackSize is the maximum combined height of stack and alt stack + // during execution. + maxStackSize = 1000 + + // maxScriptSize is the maximum allowed length of a raw script. + maxScriptSize = 10000 +) + +// halforder is used to tame ECDSA malleability (see BIP0062). +var halfOrder = new(big.Int).Rsh(btcec.S256().N, 1) + +// Engine is the virtual machine that executes scripts. +type Engine struct { + scripts [][]parsedOpcode + scriptIdx int + scriptOff int + lastCodeSep int + dstack stack // data stack + astack stack // alt stack + tx wire.MsgTx + txIdx int + condStack []int + numOps int + flags ScriptFlags + sigCache *SigCache + bip16 bool // treat execution as pay-to-script-hash + savedFirstStack [][]byte // stack from first script for bip16 scripts +} + +// hasFlag returns whether the script engine instance has the passed flag set. +func (vm *Engine) hasFlag(flag ScriptFlags) bool { + return vm.flags&flag == flag +} + +// isBranchExecuting returns whether or not the current conditional branch is +// actively executing. For example, when the data stack has an OP_FALSE on it +// and an OP_IF is encountered, the branch is inactive until an OP_ELSE or +// OP_ENDIF is encountered. It properly handles nested conditionals. +func (vm *Engine) isBranchExecuting() bool { + if len(vm.condStack) == 0 { + return true + } + return vm.condStack[len(vm.condStack)-1] == OpCondTrue +} + +// executeOpcode peforms execution on the passed opcode. It takes into account +// whether or not it is hidden by conditionals, but some rules still must be +// tested in this case. +func (vm *Engine) executeOpcode(pop *parsedOpcode) error { + // Disabled opcodes are fail on program counter. + if pop.isDisabled() { + return ErrStackOpDisabled + } + + // Always-illegal opcodes are fail on program counter. + if pop.alwaysIllegal() { + return ErrStackReservedOpcode + } + + // Note that this includes OP_RESERVED which counts as a push operation. + if pop.opcode.value > OP_16 { + vm.numOps++ + if vm.numOps > MaxOpsPerScript { + return ErrStackTooManyOperations + } + + } else if len(pop.data) > MaxScriptElementSize { + return ErrStackElementTooBig + } + + // Nothing left to do when this is not a conditional opcode and it is + // not in an executing branch. + if !vm.isBranchExecuting() && !pop.isConditional() { + return nil + } + + // Ensure all executed data push opcodes use the minimal encoding when + // the minimal data verification flag is set. + if vm.dstack.verifyMinimalData && vm.isBranchExecuting() && + pop.opcode.value >= 0 && pop.opcode.value <= OP_PUSHDATA4 { + + if err := pop.checkMinimalDataPush(); err != nil { + return err + } + } + + return pop.opcode.opfunc(pop, vm) +} + +// disasm is a helper function to produce the output for DisasmPC and +// DisasmScript. It produces the opcode prefixed by the program counter at the +// provided position in the script. It does no error checking and leaves that +// to the caller to provide a valid offset. +func (vm *Engine) disasm(scriptIdx int, scriptOff int) string { + return fmt.Sprintf("%02x:%04x: %s", scriptIdx, scriptOff, + vm.scripts[scriptIdx][scriptOff].print(false)) +} + +// validPC returns an error if the current script position is valid for +// execution, nil otherwise. +func (vm *Engine) validPC() error { + if vm.scriptIdx >= len(vm.scripts) { + return fmt.Errorf("past input scripts %v:%v %v:xxxx", + vm.scriptIdx, vm.scriptOff, len(vm.scripts)) + } + if vm.scriptOff >= len(vm.scripts[vm.scriptIdx]) { + return fmt.Errorf("past input scripts %v:%v %v:%04d", + vm.scriptIdx, vm.scriptOff, vm.scriptIdx, + len(vm.scripts[vm.scriptIdx])) + } + return nil +} + +// curPC returns either the current script and offset, or an error if the +// position isn't valid. +func (vm *Engine) curPC() (script int, off int, err error) { + err = vm.validPC() + if err != nil { + return 0, 0, err + } + return vm.scriptIdx, vm.scriptOff, nil +} + +// DisasmPC returns the string for the disassembly of the opcode that will be +// next to execute when Step() is called. +func (vm *Engine) DisasmPC() (string, error) { + scriptIdx, scriptOff, err := vm.curPC() + if err != nil { + return "", err + } + return vm.disasm(scriptIdx, scriptOff), nil +} + +// DisasmScript returns the disassembly string for the script at the requested +// offset index. Index 0 is the signature script and 1 is the public key +// script. +func (vm *Engine) DisasmScript(idx int) (string, error) { + if idx >= len(vm.scripts) { + return "", ErrStackInvalidIndex + } + + var disstr string + for i := range vm.scripts[idx] { + disstr = disstr + vm.disasm(idx, i) + "\n" + } + return disstr, nil +} + +// CheckErrorCondition returns nil if the running script has ended and was +// successful, leaving a a true boolean on the stack. An error otherwise, +// including if the script has not finished. +func (vm *Engine) CheckErrorCondition(finalScript bool) error { + // Check execution is actually done. When pc is past the end of script + // array there are no more scripts to run. + if vm.scriptIdx < len(vm.scripts) { + return ErrStackScriptUnfinished + } + if finalScript && vm.hasFlag(ScriptVerifyCleanStack) && + vm.dstack.Depth() != 1 { + + return ErrStackCleanStack + } else if vm.dstack.Depth() < 1 { + return ErrStackEmptyStack + } + + v, err := vm.dstack.PopBool() + if err != nil { + return err + } + if v == false { + // Log interesting data. + log.Tracef("%v", newLogClosure(func() string { + dis0, _ := vm.DisasmScript(0) + dis1, _ := vm.DisasmScript(1) + return fmt.Sprintf("scripts failed: script0: %s\n"+ + "script1: %s", dis0, dis1) + })) + return ErrStackScriptFailed + } + return nil +} + +// Step will execute the next instruction and move the program counter to the +// next opcode in the script, or the next script if the current has ended. Step +// will return true in the case that the last opcode was successfully executed. +// +// The result of calling Step or any other method is undefined if an error is +// returned. +func (vm *Engine) Step() (done bool, err error) { + // Verify that it is pointing to a valid script address. + err = vm.validPC() + if err != nil { + return true, err + } + opcode := &vm.scripts[vm.scriptIdx][vm.scriptOff] + + // Execute the opcode while taking into account several things such as + // disabled opcodes, illegal opcodes, maximum allowed operations per + // script, maximum script element sizes, and conditionals. + err = vm.executeOpcode(opcode) + if err != nil { + return true, err + } + + // The number of elements in the combination of the data and alt stacks + // must not exceed the maximum number of stack elements allowed. + if vm.dstack.Depth()+vm.astack.Depth() > maxStackSize { + return false, ErrStackOverflow + } + + // Prepare for next instruction. + vm.scriptOff++ + if vm.scriptOff >= len(vm.scripts[vm.scriptIdx]) { + // Illegal to have an `if' that straddles two scripts. + if err == nil && len(vm.condStack) != 0 { + return false, ErrStackMissingEndif + } + + // Alt stack doesn't persist. + _ = vm.astack.DropN(vm.astack.Depth()) + + vm.numOps = 0 // number of ops is per script. + vm.scriptOff = 0 + if vm.scriptIdx == 0 && vm.bip16 { + vm.scriptIdx++ + vm.savedFirstStack = vm.GetStack() + } else if vm.scriptIdx == 1 && vm.bip16 { + // Put us past the end for CheckErrorCondition() + vm.scriptIdx++ + // Check script ran successfully and pull the script + // out of the first stack and execute that. + err := vm.CheckErrorCondition(false) + if err != nil { + return false, err + } + + script := vm.savedFirstStack[len(vm.savedFirstStack)-1] + pops, err := parseScript(script) + if err != nil { + return false, err + } + vm.scripts = append(vm.scripts, pops) + + // Set stack to be the stack from first script minus the + // script itself + vm.SetStack(vm.savedFirstStack[:len(vm.savedFirstStack)-1]) + } else { + vm.scriptIdx++ + } + // there are zero length scripts in the wild + if vm.scriptIdx < len(vm.scripts) && vm.scriptOff >= len(vm.scripts[vm.scriptIdx]) { + vm.scriptIdx++ + } + vm.lastCodeSep = 0 + if vm.scriptIdx >= len(vm.scripts) { + return true, nil + } + } + return false, nil +} + +// Execute will execute all scripts in the script engine and return either nil +// for successful validation or an error if one occurred. +func (vm *Engine) Execute() (err error) { + done := false + for done != true { + log.Tracef("%v", newLogClosure(func() string { + dis, err := vm.DisasmPC() + if err != nil { + return fmt.Sprintf("stepping (%v)", err) + } + return fmt.Sprintf("stepping %v", dis) + })) + + done, err = vm.Step() + if err != nil { + return err + } + log.Tracef("%v", newLogClosure(func() string { + var dstr, astr string + + // if we're tracing, dump the stacks. + if vm.dstack.Depth() != 0 { + dstr = "Stack:\n" + vm.dstack.String() + } + if vm.astack.Depth() != 0 { + astr = "AltStack:\n" + vm.astack.String() + } + + return dstr + astr + })) + } + + return vm.CheckErrorCondition(true) +} + +// subScript returns the script since the last OP_CODESEPARATOR. +func (vm *Engine) subScript() []parsedOpcode { + return vm.scripts[vm.scriptIdx][vm.lastCodeSep:] +} + +// checkHashTypeEncoding returns whether or not the passed hashtype adheres to +// the strict encoding requirements if enabled. +func (vm *Engine) checkHashTypeEncoding(hashType SigHashType) error { + if !vm.hasFlag(ScriptVerifyStrictEncoding) { + return nil + } + + sigHashType := hashType & ^SigHashAnyOneCanPay + if sigHashType < SigHashAll || sigHashType > SigHashSingle { + return fmt.Errorf("invalid hashtype: 0x%x\n", hashType) + } + return nil +} + +// checkPubKeyEncoding returns whether or not the passed public key adheres to +// the strict encoding requirements if enabled. +func (vm *Engine) checkPubKeyEncoding(pubKey []byte) error { + if !vm.hasFlag(ScriptVerifyStrictEncoding) { + return nil + } + + if len(pubKey) == 33 && (pubKey[0] == 0x02 || pubKey[0] == 0x03) { + // Compressed + return nil + } + if len(pubKey) == 65 && pubKey[0] == 0x04 { + // Uncompressed + return nil + } + return ErrStackInvalidPubKey +} + +// checkSignatureEncoding returns whether or not the passed signature adheres to +// the strict encoding requirements if enabled. +func (vm *Engine) checkSignatureEncoding(sig []byte) error { + if !vm.hasFlag(ScriptVerifyDERSignatures) && + !vm.hasFlag(ScriptVerifyLowS) && + !vm.hasFlag(ScriptVerifyStrictEncoding) { + + return nil + } + + // The format of a DER encoded signature is as follows: + // + // 0x30 <total length> 0x02 <length of R> <R> 0x02 <length of S> <S> + // - 0x30 is the ASN.1 identifier for a sequence + // - Total length is 1 byte and specifies length of all remaining data + // - 0x02 is the ASN.1 identifier that specifies an integer follows + // - Length of R is 1 byte and specifies how many bytes R occupies + // - R is the arbitrary length big-endian encoded number which + // represents the R value of the signature. DER encoding dictates + // that the value must be encoded using the minimum possible number + // of bytes. This implies the first byte can only be null if the + // highest bit of the next byte is set in order to prevent it from + // being interpreted as a negative number. + // - 0x02 is once again the ASN.1 integer identifier + // - Length of S is 1 byte and specifies how many bytes S occupies + // - S is the arbitrary length big-endian encoded number which + // represents the S value of the signature. The encoding rules are + // identical as those for R. + + // Minimum length is when both numbers are 1 byte each. + // 0x30 + <1-byte> + 0x02 + 0x01 + <byte> + 0x2 + 0x01 + <byte> + if len(sig) < 8 { + // Too short + return fmt.Errorf("malformed signature: too short: %d < 8", + len(sig)) + } + + // Maximum length is when both numbers are 33 bytes each. It is 33 + // bytes because a 256-bit integer requires 32 bytes and an additional + // leading null byte might required if the high bit is set in the value. + // 0x30 + <1-byte> + 0x02 + 0x21 + <33 bytes> + 0x2 + 0x21 + <33 bytes> + if len(sig) > 72 { + // Too long + return fmt.Errorf("malformed signature: too long: %d > 72", + len(sig)) + } + if sig[0] != 0x30 { + // Wrong type + return fmt.Errorf("malformed signature: format has wrong type: 0x%x", + sig[0]) + } + if int(sig[1]) != len(sig)-2 { + // Invalid length + return fmt.Errorf("malformed signature: bad length: %d != %d", + sig[1], len(sig)-2) + } + + rLen := int(sig[3]) + + // Make sure S is inside the signature. + if rLen+5 > len(sig) { + return fmt.Errorf("malformed signature: S out of bounds") + } + + sLen := int(sig[rLen+5]) + + // The length of the elements does not match the length of the + // signature. + if rLen+sLen+6 != len(sig) { + return fmt.Errorf("malformed signature: invalid R length") + } + + // R elements must be integers. + if sig[2] != 0x02 { + return fmt.Errorf("malformed signature: missing first integer marker") + } + + // Zero-length integers are not allowed for R. + if rLen == 0 { + return fmt.Errorf("malformed signature: R length is zero") + } + + // R must not be negative. + if sig[4]&0x80 != 0 { + return fmt.Errorf("malformed signature: R value is negative") + } + + // Null bytes at the start of R are not allowed, unless R would + // otherwise be interpreted as a negative number. + if rLen > 1 && sig[4] == 0x00 && sig[5]&0x80 == 0 { + return fmt.Errorf("malformed signature: invalid R value") + } + + // S elements must be integers. + if sig[rLen+4] != 0x02 { + return fmt.Errorf("malformed signature: missing second integer marker") + } + + // Zero-length integers are not allowed for S. + if sLen == 0 { + return fmt.Errorf("malformed signature: S length is zero") + } + + // S must not be negative. + if sig[rLen+6]&0x80 != 0 { + return fmt.Errorf("malformed signature: S value is negative") + } + + // Null bytes at the start of S are not allowed, unless S would + // otherwise be interpreted as a negative number. + if sLen > 1 && sig[rLen+6] == 0x00 && sig[rLen+7]&0x80 == 0 { + return fmt.Errorf("malformed signature: invalid S value") + } + + // Verify the S value is <= half the order of the curve. This check is + // done because when it is higher, the complement modulo the order can + // be used instead which is a shorter encoding by 1 byte. Further, + // without enforcing this, it is possible to replace a signature in a + // valid transaction with the complement while still being a valid + // signature that verifies. This would result in changing the + // transaction hash and thus is source of malleability. + if vm.hasFlag(ScriptVerifyLowS) { + sValue := new(big.Int).SetBytes(sig[rLen+6 : rLen+6+sLen]) + if sValue.Cmp(halfOrder) > 0 { + return ErrStackInvalidLowSSignature + } + } + + return nil +} + +// getStack returns the contents of stack as a byte array bottom up +func getStack(stack *stack) [][]byte { + array := make([][]byte, stack.Depth()) + for i := range array { + // PeekByteArry can't fail due to overflow, already checked + array[len(array)-i-1], _ = stack.PeekByteArray(int32(i)) + } + return array +} + +// setStack sets the stack to the contents of the array where the last item in +// the array is the top item in the stack. +func setStack(stack *stack, data [][]byte) { + // This can not error. Only errors are for invalid arguments. + _ = stack.DropN(stack.Depth()) + + for i := range data { + stack.PushByteArray(data[i]) + } +} + +// GetStack returns the contents of the primary stack as an array. where the +// last item in the array is the top of the stack. +func (vm *Engine) GetStack() [][]byte { + return getStack(&vm.dstack) +} + +// SetStack sets the contents of the primary stack to the contents of the +// provided array where the last item in the array will be the top of the stack. +func (vm *Engine) SetStack(data [][]byte) { + setStack(&vm.dstack, data) +} + +// GetAltStack returns the contents of the alternate stack as an array where the +// last item in the array is the top of the stack. +func (vm *Engine) GetAltStack() [][]byte { + return getStack(&vm.astack) +} + +// SetAltStack sets the contents of the alternate stack to the contents of the +// provided array where the last item in the array will be the top of the stack. +func (vm *Engine) SetAltStack(data [][]byte) { + setStack(&vm.astack, data) +} + +// NewEngine returns a new script engine for the provided public key script, +// transaction, and input index. The flags modify the behavior of the script +// engine according to the description provided by each flag. +func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags, sigCache *SigCache) (*Engine, error) { + // The provided transaction input index must refer to a valid input. + if txIdx < 0 || txIdx >= len(tx.TxIn) { + return nil, ErrInvalidIndex + } + scriptSig := tx.TxIn[txIdx].SignatureScript + + // The clean stack flag (ScriptVerifyCleanStack) is not allowed without + // the pay-to-script-hash (P2SH) evaluation (ScriptBip16) flag. + // + // Recall that evaluating a P2SH script without the flag set results in + // non-P2SH evaluation which leaves the P2SH inputs on the stack. Thus, + // allowing the clean stack flag without the P2SH flag would make it + // possible to have a situation where P2SH would not be a soft fork when + // it should be. + vm := Engine{flags: flags, sigCache: sigCache} + if vm.hasFlag(ScriptVerifyCleanStack) && !vm.hasFlag(ScriptBip16) { + return nil, ErrInvalidFlags + } + + // The signature script must only contain data pushes when the + // associated flag is set. + if vm.hasFlag(ScriptVerifySigPushOnly) && !IsPushOnlyScript(scriptSig) { + return nil, ErrStackNonPushOnly + } + + // The engine stores the scripts in parsed form using a slice. This + // allows multiple scripts to be executed in sequence. For example, + // with a pay-to-script-hash transaction, there will be ultimately be + // a third script to execute. + scripts := [][]byte{scriptSig, scriptPubKey} + vm.scripts = make([][]parsedOpcode, len(scripts)) + for i, scr := range scripts { + if len(scr) > maxScriptSize { + return nil, ErrStackLongScript + } + var err error + vm.scripts[i], err = parseScript(scr) + if err != nil { + return nil, err + } + } + + // Advance the program counter to the public key script if the signature + // script is empty since there is nothing to execute for it in that + // case. + if len(scripts[0]) == 0 { + vm.scriptIdx++ + } + + if vm.hasFlag(ScriptBip16) && isScriptHash(vm.scripts[1]) { + // Only accept input scripts that push data for P2SH. + if !isPushOnly(vm.scripts[0]) { + return nil, ErrStackP2SHNonPushOnly + } + vm.bip16 = true + } + if vm.hasFlag(ScriptVerifyMinimalData) { + vm.dstack.verifyMinimalData = true + vm.astack.verifyMinimalData = true + } + + vm.tx = *tx + vm.txIdx = txIdx + + return &vm, nil +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/engine_test.go b/vendor/github.com/btcsuite/btcd/txscript/engine_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bcc5bee4a03922d45097b4bc7b6f843b14fda2b5 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/engine_test.go @@ -0,0 +1,454 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript_test + +import ( + "testing" + + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" +) + +// TestBadPC sets the pc to a deliberately bad result then confirms that Step() +// and Disasm fail correctly. +func TestBadPC(t *testing.T) { + t.Parallel() + + type pcTest struct { + script, off int + } + pcTests := []pcTest{ + { + script: 2, + off: 0, + }, + { + script: 0, + off: 2, + }, + } + // tx with almost empty scripts. + tx := &wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash([32]byte{ + 0xc9, 0x97, 0xa5, 0xe5, + 0x6e, 0x10, 0x41, 0x02, + 0xfa, 0x20, 0x9c, 0x6a, + 0x85, 0x2d, 0xd9, 0x06, + 0x60, 0xa2, 0x0b, 0x2d, + 0x9c, 0x35, 0x24, 0x23, + 0xed, 0xce, 0x25, 0x85, + 0x7f, 0xcd, 0x37, 0x04, + }), + Index: 0, + }, + SignatureScript: []uint8{txscript.OP_NOP}, + Sequence: 4294967295, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 1000000000, + PkScript: nil, + }, + }, + LockTime: 0, + } + pkScript := []byte{txscript.OP_NOP} + + for _, test := range pcTests { + vm, err := txscript.NewEngine(pkScript, tx, 0, 0, nil) + if err != nil { + t.Errorf("Failed to create script: %v", err) + } + + // set to after all scripts + vm.TstSetPC(test.script, test.off) + + _, err = vm.Step() + if err == nil { + t.Errorf("Step with invalid pc (%v) succeeds!", test) + continue + } + + _, err = vm.DisasmPC() + if err == nil { + t.Errorf("DisasmPC with invalid pc (%v) succeeds!", + test) + } + } +} + +// TestCheckErrorCondition tests the execute early test in CheckErrorCondition() +// since most code paths are tested elsewhere. +func TestCheckErrorCondition(t *testing.T) { + t.Parallel() + + // tx with almost empty scripts. + tx := &wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash([32]byte{ + 0xc9, 0x97, 0xa5, 0xe5, + 0x6e, 0x10, 0x41, 0x02, + 0xfa, 0x20, 0x9c, 0x6a, + 0x85, 0x2d, 0xd9, 0x06, + 0x60, 0xa2, 0x0b, 0x2d, + 0x9c, 0x35, 0x24, 0x23, + 0xed, 0xce, 0x25, 0x85, + 0x7f, 0xcd, 0x37, 0x04, + }), + Index: 0, + }, + SignatureScript: []uint8{}, + Sequence: 4294967295, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 1000000000, + PkScript: nil, + }, + }, + LockTime: 0, + } + pkScript := []byte{ + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_TRUE, + } + + vm, err := txscript.NewEngine(pkScript, tx, 0, 0, nil) + if err != nil { + t.Errorf("failed to create script: %v", err) + } + + for i := 0; i < len(pkScript)-1; i++ { + done, err := vm.Step() + if err != nil { + t.Errorf("failed to step %dth time: %v", i, err) + return + } + if done { + t.Errorf("finshed early on %dth time", i) + return + } + + err = vm.CheckErrorCondition(false) + if err != txscript.ErrStackScriptUnfinished { + t.Errorf("got unexepected error %v on %dth iteration", + err, i) + return + } + } + done, err := vm.Step() + if err != nil { + t.Errorf("final step failed %v", err) + return + } + if !done { + t.Errorf("final step isn't done!") + return + } + + err = vm.CheckErrorCondition(false) + if err != nil { + t.Errorf("unexpected error %v on final check", err) + } +} + +// TestInvalidFlagCombinations ensures the script engine returns the expected +// error when disallowed flag combinations are specified. +func TestInvalidFlagCombinations(t *testing.T) { + t.Parallel() + + tests := []txscript.ScriptFlags{ + txscript.ScriptVerifyCleanStack, + } + + // tx with almost empty scripts. + tx := &wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash([32]byte{ + 0xc9, 0x97, 0xa5, 0xe5, + 0x6e, 0x10, 0x41, 0x02, + 0xfa, 0x20, 0x9c, 0x6a, + 0x85, 0x2d, 0xd9, 0x06, + 0x60, 0xa2, 0x0b, 0x2d, + 0x9c, 0x35, 0x24, 0x23, + 0xed, 0xce, 0x25, 0x85, + 0x7f, 0xcd, 0x37, 0x04, + }), + Index: 0, + }, + SignatureScript: []uint8{txscript.OP_NOP}, + Sequence: 4294967295, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 1000000000, + PkScript: nil, + }, + }, + LockTime: 0, + } + pkScript := []byte{txscript.OP_NOP} + + for i, test := range tests { + _, err := txscript.NewEngine(pkScript, tx, 0, test, nil) + if err != txscript.ErrInvalidFlags { + t.Fatalf("TestInvalidFlagCombinations #%d unexpected "+ + "error: %v", i, err) + } + } +} + +// TestCheckPubKeyEncoding ensures the internal checkPubKeyEncoding function +// works as expected. +func TestCheckPubKeyEncoding(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + key []byte + isValid bool + }{ + { + name: "uncompressed ok", + key: decodeHex("0411db93e1dcdb8a016b49840f8c53bc1eb68" + + "a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf" + + "9744464f82e160bfa9b8b64f9d4c03f999b8643f656b" + + "412a3"), + isValid: true, + }, + { + name: "compressed ok", + key: decodeHex("02ce0b14fb842b1ba549fdd675c98075f12e9" + + "c510f8ef52bd021a9a1f4809d3b4d"), + isValid: true, + }, + { + name: "compressed ok", + key: decodeHex("032689c7c2dab13309fb143e0e8fe39634252" + + "1887e976690b6b47f5b2a4b7d448e"), + isValid: true, + }, + { + name: "hybrid", + key: decodeHex("0679be667ef9dcbbac55a06295ce870b07029" + + "bfcdb2dce28d959f2815b16f81798483ada7726a3c46" + + "55da4fbfc0e1108a8fd17b448a68554199c47d08ffb1" + + "0d4b8"), + isValid: false, + }, + { + name: "empty", + key: nil, + isValid: false, + }, + } + + flags := txscript.ScriptVerifyStrictEncoding + for _, test := range tests { + err := txscript.TstCheckPubKeyEncoding(test.key, flags) + if err != nil && test.isValid { + t.Errorf("checkSignatureEncoding test '%s' failed "+ + "when it should have succeeded: %v", test.name, + err) + } else if err == nil && !test.isValid { + t.Errorf("checkSignatureEncooding test '%s' succeeded "+ + "when it should have failed", test.name) + } + } + +} + +// TestCheckSignatureEncoding ensures the internal checkSignatureEncoding +// function works as expected. +func TestCheckSignatureEncoding(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + sig []byte + isValid bool + }{ + { + name: "valid signature", + sig: decodeHex("304402204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd41022018152" + + "2ec8eca07de4860a4acdd12909d831cc56cbbac46220" + + "82221a8768d1d09"), + isValid: true, + }, + { + name: "empty.", + sig: nil, + isValid: false, + }, + { + name: "bad magic", + sig: decodeHex("314402204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd41022018152" + + "2ec8eca07de4860a4acdd12909d831cc56cbbac46220" + + "82221a8768d1d09"), + isValid: false, + }, + { + name: "bad 1st int marker magic", + sig: decodeHex("304403204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd41022018152" + + "2ec8eca07de4860a4acdd12909d831cc56cbbac46220" + + "82221a8768d1d09"), + isValid: false, + }, + { + name: "bad 2nd int marker", + sig: decodeHex("304402204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd41032018152" + + "2ec8eca07de4860a4acdd12909d831cc56cbbac46220" + + "82221a8768d1d09"), + isValid: false, + }, + { + name: "short len", + sig: decodeHex("304302204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd41022018152" + + "2ec8eca07de4860a4acdd12909d831cc56cbbac46220" + + "82221a8768d1d09"), + isValid: false, + }, + { + name: "long len", + sig: decodeHex("304502204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd41022018152" + + "2ec8eca07de4860a4acdd12909d831cc56cbbac46220" + + "82221a8768d1d09"), + isValid: false, + }, + { + name: "long X", + sig: decodeHex("304402424e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd41022018152" + + "2ec8eca07de4860a4acdd12909d831cc56cbbac46220" + + "82221a8768d1d09"), + isValid: false, + }, + { + name: "long Y", + sig: decodeHex("304402204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd41022118152" + + "2ec8eca07de4860a4acdd12909d831cc56cbbac46220" + + "82221a8768d1d09"), + isValid: false, + }, + { + name: "short Y", + sig: decodeHex("304402204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd41021918152" + + "2ec8eca07de4860a4acdd12909d831cc56cbbac46220" + + "82221a8768d1d09"), + isValid: false, + }, + { + name: "trailing crap", + sig: decodeHex("304402204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd41022018152" + + "2ec8eca07de4860a4acdd12909d831cc56cbbac46220" + + "82221a8768d1d0901"), + isValid: false, + }, + { + name: "X == N ", + sig: decodeHex("30440220fffffffffffffffffffffffffffff" + + "ffebaaedce6af48a03bbfd25e8cd0364141022018152" + + "2ec8eca07de4860a4acdd12909d831cc56cbbac46220" + + "82221a8768d1d09"), + isValid: false, + }, + { + name: "X == N ", + sig: decodeHex("30440220fffffffffffffffffffffffffffff" + + "ffebaaedce6af48a03bbfd25e8cd0364142022018152" + + "2ec8eca07de4860a4acdd12909d831cc56cbbac46220" + + "82221a8768d1d09"), + isValid: false, + }, + { + name: "Y == N", + sig: decodeHex("304402204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd410220fffff" + + "ffffffffffffffffffffffffffebaaedce6af48a03bb" + + "fd25e8cd0364141"), + isValid: false, + }, + { + name: "Y > N", + sig: decodeHex("304402204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd410220fffff" + + "ffffffffffffffffffffffffffebaaedce6af48a03bb" + + "fd25e8cd0364142"), + isValid: false, + }, + { + name: "0 len X", + sig: decodeHex("302402000220181522ec8eca07de4860a4acd" + + "d12909d831cc56cbbac4622082221a8768d1d09"), + isValid: false, + }, + { + name: "0 len Y", + sig: decodeHex("302402204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd410200"), + isValid: false, + }, + { + name: "extra R padding", + sig: decodeHex("30450221004e45e16932b8af514961a1d3a1a" + + "25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181" + + "522ec8eca07de4860a4acdd12909d831cc56cbbac462" + + "2082221a8768d1d09"), + isValid: false, + }, + { + name: "extra S padding", + sig: decodeHex("304502204e45e16932b8af514961a1d3a1a25" + + "fdf3f4f7732e9d624c6c61548ab5fb8cd41022100181" + + "522ec8eca07de4860a4acdd12909d831cc56cbbac462" + + "2082221a8768d1d09"), + isValid: false, + }, + } + + flags := txscript.ScriptVerifyStrictEncoding + for _, test := range tests { + err := txscript.TstCheckSignatureEncoding(test.sig, flags) + if err != nil && test.isValid { + t.Errorf("checkSignatureEncoding test '%s' failed "+ + "when it should have succeeded: %v", test.name, + err) + } else if err == nil && !test.isValid { + t.Errorf("checkSignatureEncooding test '%s' succeeded "+ + "when it should have failed", test.name) + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/error.go b/vendor/github.com/btcsuite/btcd/txscript/error.go new file mode 100644 index 0000000000000000000000000000000000000000..ee908c724c25e374fff560225442b95467015270 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/error.go @@ -0,0 +1,154 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "errors" + "fmt" +) + +var ( + // ErrStackShortScript is returned if the script has an opcode that is + // too long for the length of the script. + ErrStackShortScript = errors.New("execute past end of script") + + // ErrStackLongScript is returned if the script has an opcode that is + // too long for the length of the script. + ErrStackLongScript = errors.New("script is longer than maximum allowed") + + // ErrStackUnderflow is returned if an opcode requires more items on the + // stack than is present.f + ErrStackUnderflow = errors.New("stack underflow") + + // ErrStackInvalidArgs is returned if the argument for an opcode is out + // of acceptable range. + ErrStackInvalidArgs = errors.New("invalid argument") + + // ErrStackOpDisabled is returned when a disabled opcode is encountered + // in the script. + ErrStackOpDisabled = errors.New("disabled opcode") + + // ErrStackVerifyFailed is returned when one of the OP_VERIFY or + // OP_*VERIFY instructions is executed and the conditions fails. + ErrStackVerifyFailed = errors.New("verify failed") + + // ErrStackNumberTooBig is returned when the argument for an opcode that + // should be an offset is obviously far too large. + ErrStackNumberTooBig = errors.New("number too big") + + // ErrStackInvalidOpcode is returned when an opcode marked as invalid or + // a completely undefined opcode is encountered. + ErrStackInvalidOpcode = errors.New("invalid opcode") + + // ErrStackReservedOpcode is returned when an opcode marked as reserved + // is encountered. + ErrStackReservedOpcode = errors.New("reserved opcode") + + // ErrStackEarlyReturn is returned when OP_RETURN is executed in the + // script. + ErrStackEarlyReturn = errors.New("script returned early") + + // ErrStackNoIf is returned if an OP_ELSE or OP_ENDIF is encountered + // without first having an OP_IF or OP_NOTIF in the script. + ErrStackNoIf = errors.New("OP_ELSE or OP_ENDIF with no matching OP_IF") + + // ErrStackMissingEndif is returned if the end of a script is reached + // without and OP_ENDIF to correspond to a conditional expression. + ErrStackMissingEndif = fmt.Errorf("execute fail, in conditional execution") + + // ErrStackTooManyPubKeys is returned if an OP_CHECKMULTISIG is + // encountered with more than MaxPubKeysPerMultiSig pubkeys present. + ErrStackTooManyPubKeys = errors.New("invalid pubkey count in OP_CHECKMULTISIG") + + // ErrStackTooManyOperations is returned if a script has more than + // MaxOpsPerScript opcodes that do not push data. + ErrStackTooManyOperations = errors.New("too many operations in script") + + // ErrStackElementTooBig is returned if the size of an element to be + // pushed to the stack is over MaxScriptElementSize. + ErrStackElementTooBig = errors.New("element in script too large") + + // ErrStackUnknownAddress is returned when ScriptToAddrHash does not + // recognize the pattern of the script and thus can not find the address + // for payment. + ErrStackUnknownAddress = errors.New("non-recognised address") + + // ErrStackScriptFailed is returned when at the end of a script the + // boolean on top of the stack is false signifying that the script has + // failed. + ErrStackScriptFailed = errors.New("execute fail, fail on stack") + + // ErrStackScriptUnfinished is returned when CheckErrorCondition is + // called on a script that has not finished executing. + ErrStackScriptUnfinished = errors.New("error check when script unfinished") + + // ErrStackEmptyStack is returned when the stack is empty at the end of + // execution. Normal operation requires that a boolean is on top of the + // stack when the scripts have finished executing. + ErrStackEmptyStack = errors.New("stack empty at end of execution") + + // ErrStackP2SHNonPushOnly is returned when a Pay-to-Script-Hash + // transaction is encountered and the ScriptSig does operations other + // than push data (in violation of bip16). + ErrStackP2SHNonPushOnly = errors.New("pay to script hash with non " + + "pushonly input") + + // ErrStackInvalidParseType is an internal error returned from + // ScriptToAddrHash ony if the internal data tables are wrong. + ErrStackInvalidParseType = errors.New("internal error: invalid parsetype found") + + // ErrStackInvalidAddrOffset is an internal error returned from + // ScriptToAddrHash ony if the internal data tables are wrong. + ErrStackInvalidAddrOffset = errors.New("internal error: invalid offset found") + + // ErrStackInvalidIndex is returned when an out-of-bounds index was + // passed to a function. + ErrStackInvalidIndex = errors.New("invalid script index") + + // ErrStackNonPushOnly is returned when ScriptInfo is called with a + // pkScript that peforms operations other that pushing data to the stack. + ErrStackNonPushOnly = errors.New("SigScript is non pushonly") + + // ErrStackOverflow is returned when stack and altstack combined depth + // is over the limit. + ErrStackOverflow = errors.New("stack overflow") + + // ErrStackInvalidLowSSignature is returned when the ScriptVerifyLowS + // flag is set and the script contains any signatures whose S values + // are higher than the half order. + ErrStackInvalidLowSSignature = errors.New("invalid low s signature") + + // ErrStackInvalidPubKey is returned when the ScriptVerifyScriptEncoding + // flag is set and the script contains invalid pubkeys. + ErrStackInvalidPubKey = errors.New("invalid strict pubkey") + + // ErrStackCleanStack is returned when the ScriptVerifyCleanStack flag + // is set and after evalution the stack does not contain only one element, + // which also must be true if interpreted as a boolean. + ErrStackCleanStack = errors.New("stack is not clean") + + // ErrStackMinimalData is returned when the ScriptVerifyMinimalData flag + // is set and the script contains push operations that do not use + // the minimal opcode required. + ErrStackMinimalData = errors.New("non-minimally encoded script number") +) + +var ( + // ErrInvalidFlags is returned when the passed flags to NewScript + // contain an invalid combination. + ErrInvalidFlags = errors.New("invalid flags combination") + + // ErrInvalidIndex is returned when the passed input index for the + // provided transaction is out of range. + ErrInvalidIndex = errors.New("invalid input index") + + // ErrUnsupportedAddress is returned when a concrete type that + // implements a btcutil.Address is not a supported type. + ErrUnsupportedAddress = errors.New("unsupported address type") + + // ErrBadNumRequired is returned from MultiSigScript when nrequired is + // larger than the number of provided public keys. + ErrBadNumRequired = errors.New("more signatures required than keys present") +) diff --git a/vendor/github.com/btcsuite/btcd/txscript/example_test.go b/vendor/github.com/btcsuite/btcd/txscript/example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5ef2eea6bb82c6b22aa486fce90a20bc5cdbcc3a --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/example_test.go @@ -0,0 +1,181 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript_test + +import ( + "encoding/hex" + "fmt" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// 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, &chaincfg.MainNetParams) + if err != nil { + fmt.Println(err) + return + } + + // Create a public key script that pays to the address. + script, err := txscript.PayToAddrScript(address) + if err != nil { + fmt.Println(err) + return + } + fmt.Printf("Script Hex: %x\n", script) + + disasm, err := txscript.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 +} + +// This example demonstrates extracting information from a standard public key +// script. +func ExampleExtractPkScriptAddrs() { + // Start with a standard pay-to-pubkey-hash script. + scriptHex := "76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac" + script, err := hex.DecodeString(scriptHex) + if err != nil { + fmt.Println(err) + return + } + + // Extract and print details from the script. + scriptClass, addresses, reqSigs, err := txscript.ExtractPkScriptAddrs( + script, &chaincfg.MainNetParams) + if err != nil { + fmt.Println(err) + return + } + fmt.Println("Script Class:", scriptClass) + fmt.Println("Addresses:", addresses) + fmt.Println("Required Signatures:", reqSigs) + + // Output: + // Script Class: pubkeyhash + // Addresses: [12gpXQVcCL2qhTNQgyLVdCFG2Qs2px98nV] + // Required Signatures: 1 +} + +// This example demonstrates manually creating and signing a redeem transaction. +func ExampleSignTxOutput() { + // Ordinarily the private key would come from whatever storage mechanism + // is being used, but for this example just hard code it. + privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" + + "d4f8720ee63e502ee2869afab7de234b80c") + if err != nil { + fmt.Println(err) + return + } + privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes) + pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) + addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, + &chaincfg.MainNetParams) + if err != nil { + fmt.Println(err) + return + } + + // For this example, create a fake transaction that represents what + // would ordinarily be the real transaction that is being spent. It + // contains a single output that pays to address in the amount of 1 BTC. + originTx := wire.NewMsgTx() + prevOut := wire.NewOutPoint(&wire.ShaHash{}, ^uint32(0)) + txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}) + originTx.AddTxIn(txIn) + pkScript, err := txscript.PayToAddrScript(addr) + if err != nil { + fmt.Println(err) + return + } + txOut := wire.NewTxOut(100000000, pkScript) + originTx.AddTxOut(txOut) + originTxHash := originTx.TxSha() + + // Create the transaction to redeem the fake transaction. + redeemTx := wire.NewMsgTx() + + // Add the input(s) the redeeming transaction will spend. There is no + // signature script at this point since it hasn't been created or signed + // yet, hence nil is provided for it. + prevOut = wire.NewOutPoint(&originTxHash, 0) + txIn = wire.NewTxIn(prevOut, nil) + redeemTx.AddTxIn(txIn) + + // Ordinarily this would contain that actual destination of the funds, + // but for this example don't bother. + txOut = wire.NewTxOut(0, nil) + redeemTx.AddTxOut(txOut) + + // Sign the redeeming transaction. + lookupKey := func(a btcutil.Address) (*btcec.PrivateKey, bool, error) { + // Ordinarily this function would involve looking up the private + // key for the provided address, but since the only thing being + // signed in this example uses the address associated with the + // private key from above, simply return it with the compressed + // flag set since the address is using the associated compressed + // public key. + // + // NOTE: If you want to prove the code is actually signing the + // transaction properly, uncomment the following line which + // intentionally returns an invalid key to sign with, which in + // turn will result in a failure during the script execution + // when verifying the signature. + // + // privKey.D.SetInt64(12345) + // + return privKey, true, nil + } + // Notice that the script database parameter is nil here since it isn't + // used. It must be specified when pay-to-script-hash transactions are + // being signed. + sigScript, err := txscript.SignTxOutput(&chaincfg.MainNetParams, + redeemTx, 0, originTx.TxOut[0].PkScript, txscript.SigHashAll, + txscript.KeyClosure(lookupKey), nil, nil) + if err != nil { + fmt.Println(err) + return + } + redeemTx.TxIn[0].SignatureScript = sigScript + + // Prove that the transaction has been validly signed by executing the + // script pair. + flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures | + txscript.ScriptStrictMultiSig | + txscript.ScriptDiscourageUpgradableNops + vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, + flags, nil) + if err != nil { + fmt.Println(err) + return + } + if err := vm.Execute(); err != nil { + fmt.Println(err) + return + } + fmt.Println("Transaction successfully signed") + + // Output: + // Transaction successfully signed +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/internal_test.go b/vendor/github.com/btcsuite/btcd/txscript/internal_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2e031c392ccffb4eb81fae369992d50fd1ddda39 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/internal_test.go @@ -0,0 +1,3753 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +This test file is part of the txscript package rather than than the +txscript_test package so it can bridge access to the internals to properly test +cases which are either not possible or can't reliably be tested via the public +interface. The functions are only exported while the tests are being run. +*/ + +package txscript + +import "testing" + +// TstMaxScriptSize makes the internal maxScriptSize constant available to the +// test package. +const TstMaxScriptSize = maxScriptSize + +// TstHasCanoncialPushes makes the internal isCanonicalPush function available +// to the test package. +var TstHasCanonicalPushes = canonicalPush + +// TstParseScript makes the internal parseScript function available to the +// test package. +var TstParseScript = parseScript + +// TstCalcSignatureHash makes the internal calcSignatureHash function available +// to the test package. +var TstCalcSignatureHash = calcSignatureHash + +// TstConcatRawScript makes the ability to add the pass script directly to +// an existing script to the test package. This differs from AddData since it +// doesn't add a push data opcode. +func (b *ScriptBuilder) TstConcatRawScript(data []byte) *ScriptBuilder { + if b.err != nil { + return b + } + + b.script = append(b.script, data...) + return b +} + +// TstCheckPubKeyEncoding makes the internal checkPubKeyEncoding function +// available to the test package. Since it only really needs from the engine +// for the flags, just accept the flags and create a new engine skeleton. +func TstCheckPubKeyEncoding(pubKey []byte, flags ScriptFlags) error { + vm := Engine{flags: flags} + return vm.checkPubKeyEncoding(pubKey) +} + +// TstCheckSignatureEncoding makes the internal checkSignatureEncoding function +// available to the test package. Since it only really needs from the engine +// for the flags, just accept the flags and create a new engine skeleton with +// them. +func TstCheckSignatureEncoding(sig []byte, flags ScriptFlags) error { + vm := Engine{flags: flags} + return vm.checkSignatureEncoding(sig) +} + +// TstRemoveOpcode makes the internal removeOpcode function available to the +// test package. +func TstRemoveOpcode(pkscript []byte, opcode byte) ([]byte, error) { + pops, err := parseScript(pkscript) + if err != nil { + return nil, err + } + pops = removeOpcode(pops, opcode) + return unparseScript(pops) +} + +// TstRemoveOpcodeByData makes the internal removeOpcodeByData function +// available to the test package. +func TstRemoveOpcodeByData(pkscript []byte, data []byte) ([]byte, error) { + pops, err := parseScript(pkscript) + if err != nil { + return nil, err + } + pops = removeOpcodeByData(pops, data) + return unparseScript(pops) +} + +// TestSetPC allows the test modules to set the program counter to whatever they +// want. +func (vm *Engine) TstSetPC(script, off int) { + vm.scriptIdx = script + vm.scriptOff = off +} + +// Internal tests for opcode parsing with bad data templates. +func TestParseOpcode(t *testing.T) { + // Deep copy the array and make one of the opcodes invalid by setting it + // to the wrong length. + fakeArray := opcodeArray + fakeArray[OP_PUSHDATA4] = opcode{value: OP_PUSHDATA4, + name: "OP_PUSHDATA4", length: -8, opfunc: opcodePushData} + + // This script would be fine if -8 was a valid length. + _, err := parseScriptTemplate([]byte{OP_PUSHDATA4, 0x1, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00}, &fakeArray) + if err == nil { + t.Errorf("no error with dodgy opcode array!") + } +} + +func TestUnparsingInvalidOpcodes(t *testing.T) { + tests := []struct { + name string + pop *parsedOpcode + expectedErr error + }{ + { + name: "OP_FALSE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_FALSE], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_FALSE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_FALSE], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_1 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_1], + data: nil, + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_1", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_1], + data: make([]byte, 1), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_1 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_1], + data: make([]byte, 2), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_2 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_2], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_2", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_2], + data: make([]byte, 2), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_2 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_2], + data: make([]byte, 3), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_3 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_3], + data: make([]byte, 2), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_3", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_3], + data: make([]byte, 3), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_3 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_3], + data: make([]byte, 4), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_4 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_4], + data: make([]byte, 3), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_4", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_4], + data: make([]byte, 4), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_4 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_4], + data: make([]byte, 5), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_5 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_5], + data: make([]byte, 4), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_5", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_5], + data: make([]byte, 5), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_5 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_5], + data: make([]byte, 6), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_6 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_6], + data: make([]byte, 5), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_6", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_6], + data: make([]byte, 6), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_6 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_6], + data: make([]byte, 7), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_7 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_7], + data: make([]byte, 6), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_7", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_7], + data: make([]byte, 7), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_7 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_7], + data: make([]byte, 8), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_8 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_8], + data: make([]byte, 7), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_8", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_8], + data: make([]byte, 8), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_8 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_8], + data: make([]byte, 9), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_9 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_9], + data: make([]byte, 8), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_9", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_9], + data: make([]byte, 9), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_9 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_9], + data: make([]byte, 10), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_10 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_10], + data: make([]byte, 9), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_10", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_10], + data: make([]byte, 10), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_10 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_10], + data: make([]byte, 11), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_11 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_11], + data: make([]byte, 10), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_11", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_11], + data: make([]byte, 11), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_11 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_11], + data: make([]byte, 12), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_12 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_12], + data: make([]byte, 11), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_12", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_12], + data: make([]byte, 12), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_12 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_12], + data: make([]byte, 13), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_13 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_13], + data: make([]byte, 12), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_13", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_13], + data: make([]byte, 13), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_13 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_13], + data: make([]byte, 14), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_14 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_14], + data: make([]byte, 13), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_14", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_14], + data: make([]byte, 14), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_14 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_14], + data: make([]byte, 15), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_15 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_15], + data: make([]byte, 14), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_15", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_15], + data: make([]byte, 15), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_15 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_15], + data: make([]byte, 16), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_16 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_16], + data: make([]byte, 15), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_16", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_16], + data: make([]byte, 16), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_16 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_16], + data: make([]byte, 17), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_17 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_17], + data: make([]byte, 16), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_17", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_17], + data: make([]byte, 17), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_17 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_17], + data: make([]byte, 18), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_18 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_18], + data: make([]byte, 17), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_18", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_18], + data: make([]byte, 18), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_18 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_18], + data: make([]byte, 19), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_19 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_19], + data: make([]byte, 18), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_19", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_19], + data: make([]byte, 19), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_19 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_19], + data: make([]byte, 20), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_20 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_20], + data: make([]byte, 19), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_20", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_20], + data: make([]byte, 20), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_20 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_20], + data: make([]byte, 21), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_21 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_21], + data: make([]byte, 20), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_21", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_21], + data: make([]byte, 21), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_21 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_21], + data: make([]byte, 22), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_22 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_22], + data: make([]byte, 21), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_22", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_22], + data: make([]byte, 22), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_22 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_22], + data: make([]byte, 23), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_23 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_23], + data: make([]byte, 22), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_23", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_23], + data: make([]byte, 23), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_23 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_23], + data: make([]byte, 24), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_24 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_24], + data: make([]byte, 23), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_24", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_24], + data: make([]byte, 24), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_24 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_24], + data: make([]byte, 25), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_25 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_25], + data: make([]byte, 24), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_25", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_25], + data: make([]byte, 25), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_25 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_25], + data: make([]byte, 26), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_26 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_26], + data: make([]byte, 25), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_26", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_26], + data: make([]byte, 26), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_26 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_26], + data: make([]byte, 27), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_27 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_27], + data: make([]byte, 26), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_27", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_27], + data: make([]byte, 27), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_27 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_27], + data: make([]byte, 28), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_28 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_28], + data: make([]byte, 27), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_28", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_28], + data: make([]byte, 28), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_28 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_28], + data: make([]byte, 29), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_29 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_29], + data: make([]byte, 28), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_29", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_29], + data: make([]byte, 29), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_29 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_29], + data: make([]byte, 30), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_30 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_30], + data: make([]byte, 29), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_30", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_30], + data: make([]byte, 30), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_30 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_30], + data: make([]byte, 31), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_31 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_31], + data: make([]byte, 30), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_31", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_31], + data: make([]byte, 31), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_31 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_31], + data: make([]byte, 32), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_32 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_32], + data: make([]byte, 31), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_32", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_32], + data: make([]byte, 32), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_32 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_32], + data: make([]byte, 33), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_33 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_33], + data: make([]byte, 32), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_33", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_33], + data: make([]byte, 33), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_33 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_33], + data: make([]byte, 34), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_34 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_34], + data: make([]byte, 33), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_34", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_34], + data: make([]byte, 34), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_34 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_34], + data: make([]byte, 35), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_35 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_35], + data: make([]byte, 34), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_35", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_35], + data: make([]byte, 35), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_35 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_35], + data: make([]byte, 36), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_36 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_36], + data: make([]byte, 35), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_36", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_36], + data: make([]byte, 36), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_36 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_36], + data: make([]byte, 37), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_37 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_37], + data: make([]byte, 36), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_37", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_37], + data: make([]byte, 37), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_37 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_37], + data: make([]byte, 38), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_38 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_38], + data: make([]byte, 37), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_38", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_38], + data: make([]byte, 38), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_38 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_38], + data: make([]byte, 39), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_39 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_39], + data: make([]byte, 38), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_39", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_39], + data: make([]byte, 39), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_39 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_39], + data: make([]byte, 40), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_40 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_40], + data: make([]byte, 39), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_40", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_40], + data: make([]byte, 40), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_40 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_40], + data: make([]byte, 41), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_41 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_41], + data: make([]byte, 40), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_41", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_41], + data: make([]byte, 41), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_41 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_41], + data: make([]byte, 42), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_42 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_42], + data: make([]byte, 41), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_42", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_42], + data: make([]byte, 42), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_42 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_42], + data: make([]byte, 43), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_43 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_43], + data: make([]byte, 42), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_43", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_43], + data: make([]byte, 43), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_43 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_43], + data: make([]byte, 44), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_44 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_44], + data: make([]byte, 43), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_44", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_44], + data: make([]byte, 44), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_44 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_44], + data: make([]byte, 45), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_45 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_45], + data: make([]byte, 44), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_45", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_45], + data: make([]byte, 45), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_45 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_45], + data: make([]byte, 46), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_46 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_46], + data: make([]byte, 45), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_46", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_46], + data: make([]byte, 46), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_46 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_46], + data: make([]byte, 47), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_47 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_47], + data: make([]byte, 46), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_47", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_47], + data: make([]byte, 47), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_47 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_47], + data: make([]byte, 48), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_48 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_48], + data: make([]byte, 47), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_48", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_48], + data: make([]byte, 48), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_48 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_48], + data: make([]byte, 49), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_49 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_49], + data: make([]byte, 48), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_49", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_49], + data: make([]byte, 49), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_49 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_49], + data: make([]byte, 50), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_50 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_50], + data: make([]byte, 49), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_50", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_50], + data: make([]byte, 50), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_50 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_50], + data: make([]byte, 51), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_51 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_51], + data: make([]byte, 50), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_51", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_51], + data: make([]byte, 51), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_51 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_51], + data: make([]byte, 52), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_52 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_52], + data: make([]byte, 51), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_52", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_52], + data: make([]byte, 52), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_52 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_52], + data: make([]byte, 53), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_53 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_53], + data: make([]byte, 52), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_53", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_53], + data: make([]byte, 53), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_53 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_53], + data: make([]byte, 54), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_54 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_54], + data: make([]byte, 53), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_54", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_54], + data: make([]byte, 54), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_54 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_54], + data: make([]byte, 55), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_55 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_55], + data: make([]byte, 54), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_55", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_55], + data: make([]byte, 55), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_55 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_55], + data: make([]byte, 56), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_56 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_56], + data: make([]byte, 55), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_56", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_56], + data: make([]byte, 56), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_56 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_56], + data: make([]byte, 57), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_57 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_57], + data: make([]byte, 56), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_57", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_57], + data: make([]byte, 57), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_57 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_57], + data: make([]byte, 58), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_58 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_58], + data: make([]byte, 57), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_58", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_58], + data: make([]byte, 58), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_58 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_58], + data: make([]byte, 59), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_59 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_59], + data: make([]byte, 58), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_59", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_59], + data: make([]byte, 59), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_59 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_59], + data: make([]byte, 60), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_60 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_60], + data: make([]byte, 59), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_60", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_60], + data: make([]byte, 60), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_60 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_60], + data: make([]byte, 61), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_61 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_61], + data: make([]byte, 60), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_61", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_61], + data: make([]byte, 61), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_61 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_61], + data: make([]byte, 62), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_62 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_62], + data: make([]byte, 61), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_62", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_62], + data: make([]byte, 62), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_62 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_62], + data: make([]byte, 63), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_63 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_63], + data: make([]byte, 62), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_63", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_63], + data: make([]byte, 63), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_63 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_63], + data: make([]byte, 64), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_64 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_64], + data: make([]byte, 63), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_64", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_64], + data: make([]byte, 64), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_64 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_64], + data: make([]byte, 65), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_65 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_65], + data: make([]byte, 64), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_65", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_65], + data: make([]byte, 65), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_65 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_65], + data: make([]byte, 66), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_66 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_66], + data: make([]byte, 65), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_66", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_66], + data: make([]byte, 66), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_66 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_66], + data: make([]byte, 67), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_67 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_67], + data: make([]byte, 66), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_67", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_67], + data: make([]byte, 67), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_67 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_67], + data: make([]byte, 68), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_68 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_68], + data: make([]byte, 67), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_68", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_68], + data: make([]byte, 68), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_68 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_68], + data: make([]byte, 69), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_69 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_69], + data: make([]byte, 68), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_69", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_69], + data: make([]byte, 69), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_69 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_69], + data: make([]byte, 70), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_70 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_70], + data: make([]byte, 69), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_70", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_70], + data: make([]byte, 70), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_70 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_70], + data: make([]byte, 71), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_71 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_71], + data: make([]byte, 70), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_71", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_71], + data: make([]byte, 71), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_71 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_71], + data: make([]byte, 72), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_72 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_72], + data: make([]byte, 71), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_72", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_72], + data: make([]byte, 72), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_72 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_72], + data: make([]byte, 73), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_73 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_73], + data: make([]byte, 72), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_73", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_73], + data: make([]byte, 73), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_73 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_73], + data: make([]byte, 74), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_74 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_74], + data: make([]byte, 73), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_74", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_74], + data: make([]byte, 74), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_74 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_74], + data: make([]byte, 75), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_75 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_75], + data: make([]byte, 74), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DATA_75", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_75], + data: make([]byte, 75), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_75 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DATA_75], + data: make([]byte, 76), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_PUSHDATA1", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_PUSHDATA1], + data: []byte{0, 1, 2, 3, 4}, + }, + expectedErr: nil, + }, + { + name: "OP_PUSHDATA2", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_PUSHDATA2], + data: []byte{0, 1, 2, 3, 4}, + }, + expectedErr: nil, + }, + { + name: "OP_PUSHDATA4", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_PUSHDATA1], + data: []byte{0, 1, 2, 3, 4}, + }, + expectedErr: nil, + }, + { + name: "OP_1NEGATE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_1NEGATE], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_1NEGATE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_1NEGATE], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_RESERVED", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RESERVED], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RESERVED long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RESERVED], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_TRUE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_TRUE], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_TRUE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_TRUE], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_2", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_2", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_3", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_3], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_3 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_3], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_4", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_4], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_4 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_4], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_5", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_5], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_5 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_5], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_6", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_6], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_6 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_6], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_7", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_7], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_7 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_7], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_8", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_8], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_8 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_8], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_9", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_9], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_9 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_9], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_10", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_10], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_10 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_10], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_11", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_11], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_11 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_11], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_12", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_12], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_12 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_12], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_13", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_13], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_13 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_13], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_14", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_14], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_14 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_14], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_15", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_15], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_15 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_15], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_16", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_16], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_16 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_16], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_VER", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_VER], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_VER long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_VER], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_IF", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_IF], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_IF long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_IF], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOTIF", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOTIF], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOTIF long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOTIF], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_VERIF", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_VERIF], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_VERIF long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_VERIF], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_VERNOTIF", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_VERNOTIF], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_VERNOTIF long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_VERNOTIF], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_ELSE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_ELSE], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_ELSE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_ELSE], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_ENDIF", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_ENDIF], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_ENDIF long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_ENDIF], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_VERIFY", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_VERIFY], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_VERIFY long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_VERIFY], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_RETURN", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RETURN], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RETURN long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RETURN], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_TOALTSTACK", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_TOALTSTACK], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_TOALTSTACK long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_TOALTSTACK], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_FROMALTSTACK", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_FROMALTSTACK], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_FROMALTSTACK long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_FROMALTSTACK], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_2DROP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2DROP], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2DROP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2DROP], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_2DUP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2DUP], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2DUP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2DUP], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_3DUP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_3DUP], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_3DUP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_3DUP], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_2OVER", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2OVER], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2OVER long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2OVER], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_2ROT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2ROT], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2ROT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2ROT], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_2SWAP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2SWAP], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2SWAP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2SWAP], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_IFDUP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_IFDUP], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_IFDUP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_IFDUP], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DEPTH", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DEPTH], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_DEPTH long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DEPTH], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DROP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DROP], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_DROP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DROP], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DUP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DUP], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_DUP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DUP], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NIP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NIP], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NIP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NIP], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_OVER", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_OVER], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_OVER long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_OVER], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_PICK", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_PICK], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_PICK long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_PICK], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_ROLL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_ROLL], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_ROLL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_ROLL], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_ROT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_ROT], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_ROT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_ROT], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_SWAP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_SWAP], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_SWAP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_SWAP], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_TUCK", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_TUCK], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_TUCK long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_TUCK], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_CAT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_CAT], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_CAT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_CAT], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_SUBSTR", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_SUBSTR], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_SUBSTR long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_SUBSTR], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_LEFT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_LEFT], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_LEFT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_LEFT], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_LEFT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_LEFT], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_LEFT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_LEFT], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_RIGHT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RIGHT], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RIGHT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RIGHT], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_SIZE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_SIZE], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_SIZE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_SIZE], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_INVERT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_INVERT], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_INVERT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_INVERT], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_AND", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_AND], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_AND long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_AND], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_OR", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_OR], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_OR long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_OR], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_XOR", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_XOR], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_XOR long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_XOR], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_EQUAL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_EQUAL], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_EQUAL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_EQUAL], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_EQUALVERIFY", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_EQUALVERIFY], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_EQUALVERIFY long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_EQUALVERIFY], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_RESERVED1", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RESERVED1], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RESERVED1 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RESERVED1], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_RESERVED2", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RESERVED2], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RESERVED2 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RESERVED2], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_1ADD", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_1ADD], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_1ADD long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_1ADD], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_1SUB", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_1SUB], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_1SUB long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_1SUB], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_2MUL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2MUL], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2MUL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2MUL], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_2DIV", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2DIV], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2DIV long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_2DIV], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NEGATE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NEGATE], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NEGATE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NEGATE], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_ABS", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_ABS], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_ABS long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_ABS], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOT], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOT], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_0NOTEQUAL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_0NOTEQUAL], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_0NOTEQUAL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_0NOTEQUAL], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_ADD", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_ADD], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_ADD long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_ADD], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_SUB", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_SUB], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_SUB long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_SUB], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_MUL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_MUL], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_MUL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_MUL], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_DIV", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DIV], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_DIV long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_DIV], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_MOD", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_MOD], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_MOD long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_MOD], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_LSHIFT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_LSHIFT], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_LSHIFT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_LSHIFT], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_RSHIFT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RSHIFT], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RSHIFT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RSHIFT], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_BOOLAND", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_BOOLAND], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_BOOLAND long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_BOOLAND], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_BOOLOR", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_BOOLOR], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_BOOLOR long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_BOOLOR], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NUMEQUAL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NUMEQUAL], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NUMEQUAL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NUMEQUAL], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NUMEQUALVERIFY", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NUMEQUALVERIFY], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NUMEQUALVERIFY long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NUMEQUALVERIFY], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NUMNOTEQUAL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NUMNOTEQUAL], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NUMNOTEQUAL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NUMNOTEQUAL], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_LESSTHAN", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_LESSTHAN], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_LESSTHAN long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_LESSTHAN], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_GREATERTHAN", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_GREATERTHAN], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_GREATERTHAN long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_GREATERTHAN], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_LESSTHANOREQUAL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_LESSTHANOREQUAL], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_LESSTHANOREQUAL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_LESSTHANOREQUAL], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_GREATERTHANOREQUAL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_GREATERTHANOREQUAL], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_GREATERTHANOREQUAL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_GREATERTHANOREQUAL], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_MIN", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_MIN], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_MIN long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_MIN], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_MAX", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_MAX], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_MAX long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_MAX], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_WITHIN", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_WITHIN], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_WITHIN long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_WITHIN], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_RIPEMD160", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RIPEMD160], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RIPEMD160 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_RIPEMD160], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_SHA1", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_SHA1], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_SHA1 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_SHA1], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_SHA256", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_SHA256], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_SHA256 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_SHA256], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_HASH160", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_HASH160], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_HASH160 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_HASH160], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_HASH256", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_HASH256], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_HASH256 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_HASH256], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_CODESAPERATOR", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_CODESEPARATOR], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_CODESEPARATOR long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_CODESEPARATOR], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_CHECKSIG", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_CHECKSIG], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_CHECKSIG long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_CHECKSIG], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_CHECKSIGVERIFY", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_CHECKSIGVERIFY], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_CHECKSIGVERIFY long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_CHECKSIGVERIFY], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_CHECKMULTISIG", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_CHECKMULTISIG], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_CHECKMULTISIG long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_CHECKMULTISIG], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_CHECKMULTISIGVERIFY", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_CHECKMULTISIGVERIFY], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_CHECKMULTISIGVERIFY long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_CHECKMULTISIGVERIFY], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOP1", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP1], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP1 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP1], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOP2", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP2], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP2 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP2], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOP3", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP3], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP3 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP3], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOP4", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP4], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP4 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP4], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOP5", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP5], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP5 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP5], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOP6", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP6], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP6 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP6], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOP7", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP7], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP7 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP7], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOP8", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP8], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP8 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP8], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOP9", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP9], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP9 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP9], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_NOP10", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP10], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP10 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_NOP10], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_PUBKEYHASH", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_PUBKEYHASH], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_PUBKEYHASH long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_PUBKEYHASH], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_PUBKEY", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_PUBKEY], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_PUBKEY long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_PUBKEY], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + { + name: "OP_INVALIDOPCODE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_INVALIDOPCODE], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_INVALIDOPCODE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OP_INVALIDOPCODE], + data: make([]byte, 1), + }, + expectedErr: ErrStackInvalidOpcode, + }, + } + + for _, test := range tests { + _, err := test.pop.bytes() + if err != test.expectedErr { + t.Errorf("Parsed Opcode test '%s' failed", test.name) + t.Error(err, test.expectedErr) + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/log.go b/vendor/github.com/btcsuite/btcd/txscript/log.go new file mode 100644 index 0000000000000000000000000000000000000000..f63da7288f134cb0ee71edf6ceef0b9cfc1998e2 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/log.go @@ -0,0 +1,71 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "errors" + "io" + + "github.com/btcsuite/btclog" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + DisableLog() +} + +// DisableLog disables all library log output. Logging output is disabled +// by default until either UseLogger or SetLogWriter are called. +func DisableLog() { + log = btclog.Disabled +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} + +// SetLogWriter uses a specified io.Writer to output package logging info. +// This allows a caller to direct package logging output without needing a +// dependency on seelog. If the caller is also using btclog, UseLogger should +// be used instead. +func SetLogWriter(w io.Writer, level string) error { + if w == nil { + return errors.New("nil writer") + } + + lvl, ok := btclog.LogLevelFromString(level) + if !ok { + return errors.New("invalid log level") + } + + l, err := btclog.NewLoggerFromWriter(w, lvl) + if err != nil { + return err + } + + UseLogger(l) + return nil +} + +// LogClosure is a closure that can be printed with %v to be used to +// generate expensive-to-create data for a detailed log level and avoid doing +// the work if the data isn't printed. +type logClosure func() string + +func (c logClosure) String() string { + return c() +} + +func newLogClosure(c func() string) logClosure { + return logClosure(c) +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/log_test.go b/vendor/github.com/btcsuite/btcd/txscript/log_test.go new file mode 100644 index 0000000000000000000000000000000000000000..851f9372f9c5fb2715dc00a4d2f4e63d6665cdc0 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/log_test.go @@ -0,0 +1,66 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript_test + +import ( + "errors" + "io" + "os" + "testing" + + "github.com/btcsuite/btcd/txscript" +) + +func TestSetLogWriter(t *testing.T) { + tests := []struct { + name string + w io.Writer + level string + expected error + }{ + { + name: "nil writer", + w: nil, + level: "trace", + expected: errors.New("nil writer"), + }, + { + name: "invalid log level", + w: os.Stdout, + level: "wrong", + expected: errors.New("invalid log level"), + }, + { + name: "use off level", + w: os.Stdout, + level: "off", + expected: errors.New("min level can't be greater than max. Got min: 6, max: 5"), + }, + { + name: "pass", + w: os.Stdout, + level: "debug", + expected: nil, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + err := txscript.SetLogWriter(test.w, test.level) + if err != nil { + if err.Error() != test.expected.Error() { + t.Errorf("SetLogWriter #%d (%s) wrong result\n"+ + "got: %v\nwant: %v", i, test.name, err, + test.expected) + } + } else { + if test.expected != nil { + t.Errorf("SetLogWriter #%d (%s) wrong result\n"+ + "got: %v\nwant: %v", i, test.name, err, + test.expected) + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/opcode.go b/vendor/github.com/btcsuite/btcd/txscript/opcode.go new file mode 100644 index 0000000000000000000000000000000000000000..f27a45a7d8ae6dbc272898d127b24a1023616c37 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/opcode.go @@ -0,0 +1,2203 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "bytes" + "crypto/sha1" + "encoding/binary" + "errors" + "fmt" + "hash" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/fastsha256" + "github.com/btcsuite/golangcrypto/ripemd160" +) + +// An opcode defines the information related to a txscript opcode. opfunc, if +// present, is the function to call to perform the opcode on the script. The +// current script is passed in as a slice with the first member being the opcode +// itself. +type opcode struct { + value byte + name string + length int + opfunc func(*parsedOpcode, *Engine) error +} + +// These constants are the values of the official opcodes used on the btc wiki, +// in bitcoin core and in most if not all other references and software related +// to handling BTC scripts. +const ( + OP_0 = 0x00 // 0 + OP_FALSE = 0x00 // 0 - AKA OP_0 + OP_DATA_1 = 0x01 // 1 + OP_DATA_2 = 0x02 // 2 + OP_DATA_3 = 0x03 // 3 + OP_DATA_4 = 0x04 // 4 + OP_DATA_5 = 0x05 // 5 + OP_DATA_6 = 0x06 // 6 + OP_DATA_7 = 0x07 // 7 + OP_DATA_8 = 0x08 // 8 + OP_DATA_9 = 0x09 // 9 + OP_DATA_10 = 0x0a // 10 + OP_DATA_11 = 0x0b // 11 + OP_DATA_12 = 0x0c // 12 + OP_DATA_13 = 0x0d // 13 + OP_DATA_14 = 0x0e // 14 + OP_DATA_15 = 0x0f // 15 + OP_DATA_16 = 0x10 // 16 + OP_DATA_17 = 0x11 // 17 + OP_DATA_18 = 0x12 // 18 + OP_DATA_19 = 0x13 // 19 + OP_DATA_20 = 0x14 // 20 + OP_DATA_21 = 0x15 // 21 + OP_DATA_22 = 0x16 // 22 + OP_DATA_23 = 0x17 // 23 + OP_DATA_24 = 0x18 // 24 + OP_DATA_25 = 0x19 // 25 + OP_DATA_26 = 0x1a // 26 + OP_DATA_27 = 0x1b // 27 + OP_DATA_28 = 0x1c // 28 + OP_DATA_29 = 0x1d // 29 + OP_DATA_30 = 0x1e // 30 + OP_DATA_31 = 0x1f // 31 + OP_DATA_32 = 0x20 // 32 + OP_DATA_33 = 0x21 // 33 + OP_DATA_34 = 0x22 // 34 + OP_DATA_35 = 0x23 // 35 + OP_DATA_36 = 0x24 // 36 + OP_DATA_37 = 0x25 // 37 + OP_DATA_38 = 0x26 // 38 + OP_DATA_39 = 0x27 // 39 + OP_DATA_40 = 0x28 // 40 + OP_DATA_41 = 0x29 // 41 + OP_DATA_42 = 0x2a // 42 + OP_DATA_43 = 0x2b // 43 + OP_DATA_44 = 0x2c // 44 + OP_DATA_45 = 0x2d // 45 + OP_DATA_46 = 0x2e // 46 + OP_DATA_47 = 0x2f // 47 + OP_DATA_48 = 0x30 // 48 + OP_DATA_49 = 0x31 // 49 + OP_DATA_50 = 0x32 // 50 + OP_DATA_51 = 0x33 // 51 + OP_DATA_52 = 0x34 // 52 + OP_DATA_53 = 0x35 // 53 + OP_DATA_54 = 0x36 // 54 + OP_DATA_55 = 0x37 // 55 + OP_DATA_56 = 0x38 // 56 + OP_DATA_57 = 0x39 // 57 + OP_DATA_58 = 0x3a // 58 + OP_DATA_59 = 0x3b // 59 + OP_DATA_60 = 0x3c // 60 + OP_DATA_61 = 0x3d // 61 + OP_DATA_62 = 0x3e // 62 + OP_DATA_63 = 0x3f // 63 + OP_DATA_64 = 0x40 // 64 + OP_DATA_65 = 0x41 // 65 + OP_DATA_66 = 0x42 // 66 + OP_DATA_67 = 0x43 // 67 + OP_DATA_68 = 0x44 // 68 + OP_DATA_69 = 0x45 // 69 + OP_DATA_70 = 0x46 // 70 + OP_DATA_71 = 0x47 // 71 + OP_DATA_72 = 0x48 // 72 + OP_DATA_73 = 0x49 // 73 + OP_DATA_74 = 0x4a // 74 + OP_DATA_75 = 0x4b // 75 + OP_PUSHDATA1 = 0x4c // 76 + OP_PUSHDATA2 = 0x4d // 77 + OP_PUSHDATA4 = 0x4e // 78 + OP_1NEGATE = 0x4f // 79 + OP_RESERVED = 0x50 // 80 + OP_1 = 0x51 // 81 - AKA OP_TRUE + OP_TRUE = 0x51 // 81 + OP_2 = 0x52 // 82 + OP_3 = 0x53 // 83 + OP_4 = 0x54 // 84 + OP_5 = 0x55 // 85 + OP_6 = 0x56 // 86 + OP_7 = 0x57 // 87 + OP_8 = 0x58 // 88 + OP_9 = 0x59 // 89 + OP_10 = 0x5a // 90 + OP_11 = 0x5b // 91 + OP_12 = 0x5c // 92 + OP_13 = 0x5d // 93 + OP_14 = 0x5e // 94 + OP_15 = 0x5f // 95 + OP_16 = 0x60 // 96 + OP_NOP = 0x61 // 97 + OP_VER = 0x62 // 98 + OP_IF = 0x63 // 99 + OP_NOTIF = 0x64 // 100 + OP_VERIF = 0x65 // 101 + OP_VERNOTIF = 0x66 // 102 + OP_ELSE = 0x67 // 103 + OP_ENDIF = 0x68 // 104 + OP_VERIFY = 0x69 // 105 + OP_RETURN = 0x6a // 106 + OP_TOALTSTACK = 0x6b // 107 + OP_FROMALTSTACK = 0x6c // 108 + OP_2DROP = 0x6d // 109 + OP_2DUP = 0x6e // 110 + OP_3DUP = 0x6f // 111 + OP_2OVER = 0x70 // 112 + OP_2ROT = 0x71 // 113 + OP_2SWAP = 0x72 // 114 + OP_IFDUP = 0x73 // 115 + OP_DEPTH = 0x74 // 116 + OP_DROP = 0x75 // 117 + OP_DUP = 0x76 // 118 + OP_NIP = 0x77 // 119 + OP_OVER = 0x78 // 120 + OP_PICK = 0x79 // 121 + OP_ROLL = 0x7a // 122 + OP_ROT = 0x7b // 123 + OP_SWAP = 0x7c // 124 + OP_TUCK = 0x7d // 125 + OP_CAT = 0x7e // 126 + OP_SUBSTR = 0x7f // 127 + OP_LEFT = 0x80 // 128 + OP_RIGHT = 0x81 // 129 + OP_SIZE = 0x82 // 130 + OP_INVERT = 0x83 // 131 + OP_AND = 0x84 // 132 + OP_OR = 0x85 // 133 + OP_XOR = 0x86 // 134 + OP_EQUAL = 0x87 // 135 + OP_EQUALVERIFY = 0x88 // 136 + OP_RESERVED1 = 0x89 // 137 + OP_RESERVED2 = 0x8a // 138 + OP_1ADD = 0x8b // 139 + OP_1SUB = 0x8c // 140 + OP_2MUL = 0x8d // 141 + OP_2DIV = 0x8e // 142 + OP_NEGATE = 0x8f // 143 + OP_ABS = 0x90 // 144 + OP_NOT = 0x91 // 145 + OP_0NOTEQUAL = 0x92 // 146 + OP_ADD = 0x93 // 147 + OP_SUB = 0x94 // 148 + OP_MUL = 0x95 // 149 + OP_DIV = 0x96 // 150 + OP_MOD = 0x97 // 151 + OP_LSHIFT = 0x98 // 152 + OP_RSHIFT = 0x99 // 153 + OP_BOOLAND = 0x9a // 154 + OP_BOOLOR = 0x9b // 155 + OP_NUMEQUAL = 0x9c // 156 + OP_NUMEQUALVERIFY = 0x9d // 157 + OP_NUMNOTEQUAL = 0x9e // 158 + OP_LESSTHAN = 0x9f // 159 + OP_GREATERTHAN = 0xa0 // 160 + OP_LESSTHANOREQUAL = 0xa1 // 161 + OP_GREATERTHANOREQUAL = 0xa2 // 162 + OP_MIN = 0xa3 // 163 + OP_MAX = 0xa4 // 164 + OP_WITHIN = 0xa5 // 165 + OP_RIPEMD160 = 0xa6 // 166 + OP_SHA1 = 0xa7 // 167 + OP_SHA256 = 0xa8 // 168 + OP_HASH160 = 0xa9 // 169 + OP_HASH256 = 0xaa // 170 + OP_CODESEPARATOR = 0xab // 171 + OP_CHECKSIG = 0xac // 172 + OP_CHECKSIGVERIFY = 0xad // 173 + OP_CHECKMULTISIG = 0xae // 174 + OP_CHECKMULTISIGVERIFY = 0xaf // 175 + OP_NOP1 = 0xb0 // 176 + OP_NOP2 = 0xb1 // 177 + OP_CHECKLOCKTIMEVERIFY = 0xb1 // 177 - AKA OP_NOP2 + OP_NOP3 = 0xb2 // 178 + OP_NOP4 = 0xb3 // 179 + OP_NOP5 = 0xb4 // 180 + OP_NOP6 = 0xb5 // 181 + OP_NOP7 = 0xb6 // 182 + OP_NOP8 = 0xb7 // 183 + OP_NOP9 = 0xb8 // 184 + OP_NOP10 = 0xb9 // 185 + OP_UNKNOWN186 = 0xba // 186 + OP_UNKNOWN187 = 0xbb // 187 + OP_UNKNOWN188 = 0xbc // 188 + OP_UNKNOWN189 = 0xbd // 189 + OP_UNKNOWN190 = 0xbe // 190 + OP_UNKNOWN191 = 0xbf // 191 + OP_UNKNOWN192 = 0xc0 // 192 + OP_UNKNOWN193 = 0xc1 // 193 + OP_UNKNOWN194 = 0xc2 // 194 + OP_UNKNOWN195 = 0xc3 // 195 + OP_UNKNOWN196 = 0xc4 // 196 + OP_UNKNOWN197 = 0xc5 // 197 + OP_UNKNOWN198 = 0xc6 // 198 + OP_UNKNOWN199 = 0xc7 // 199 + OP_UNKNOWN200 = 0xc8 // 200 + OP_UNKNOWN201 = 0xc9 // 201 + OP_UNKNOWN202 = 0xca // 202 + OP_UNKNOWN203 = 0xcb // 203 + OP_UNKNOWN204 = 0xcc // 204 + OP_UNKNOWN205 = 0xcd // 205 + OP_UNKNOWN206 = 0xce // 206 + OP_UNKNOWN207 = 0xcf // 207 + OP_UNKNOWN208 = 0xd0 // 208 + OP_UNKNOWN209 = 0xd1 // 209 + OP_UNKNOWN210 = 0xd2 // 210 + OP_UNKNOWN211 = 0xd3 // 211 + OP_UNKNOWN212 = 0xd4 // 212 + OP_UNKNOWN213 = 0xd5 // 213 + OP_UNKNOWN214 = 0xd6 // 214 + OP_UNKNOWN215 = 0xd7 // 215 + OP_UNKNOWN216 = 0xd8 // 216 + OP_UNKNOWN217 = 0xd9 // 217 + OP_UNKNOWN218 = 0xda // 218 + OP_UNKNOWN219 = 0xdb // 219 + OP_UNKNOWN220 = 0xdc // 220 + OP_UNKNOWN221 = 0xdd // 221 + OP_UNKNOWN222 = 0xde // 222 + OP_UNKNOWN223 = 0xdf // 223 + OP_UNKNOWN224 = 0xe0 // 224 + OP_UNKNOWN225 = 0xe1 // 225 + OP_UNKNOWN226 = 0xe2 // 226 + OP_UNKNOWN227 = 0xe3 // 227 + OP_UNKNOWN228 = 0xe4 // 228 + OP_UNKNOWN229 = 0xe5 // 229 + OP_UNKNOWN230 = 0xe6 // 230 + OP_UNKNOWN231 = 0xe7 // 231 + OP_UNKNOWN232 = 0xe8 // 232 + OP_UNKNOWN233 = 0xe9 // 233 + OP_UNKNOWN234 = 0xea // 234 + OP_UNKNOWN235 = 0xeb // 235 + OP_UNKNOWN236 = 0xec // 236 + OP_UNKNOWN237 = 0xed // 237 + OP_UNKNOWN238 = 0xee // 238 + OP_UNKNOWN239 = 0xef // 239 + OP_UNKNOWN240 = 0xf0 // 240 + OP_UNKNOWN241 = 0xf1 // 241 + OP_UNKNOWN242 = 0xf2 // 242 + OP_UNKNOWN243 = 0xf3 // 243 + OP_UNKNOWN244 = 0xf4 // 244 + OP_UNKNOWN245 = 0xf5 // 245 + OP_UNKNOWN246 = 0xf6 // 246 + OP_UNKNOWN247 = 0xf7 // 247 + OP_UNKNOWN248 = 0xf8 // 248 + OP_SMALLDATA = 0xf9 // 249 - bitcoin core internal + OP_SMALLINTEGER = 0xfa // 250 - bitcoin core internal + OP_PUBKEYS = 0xfb // 251 - bitcoin core internal + OP_UNKNOWN252 = 0xfc // 252 + OP_PUBKEYHASH = 0xfd // 253 - bitcoin core internal + OP_PUBKEY = 0xfe // 254 - bitcoin core internal + OP_INVALIDOPCODE = 0xff // 255 - bitcoin core internal +) + +// Conditional execution constants. +const ( + OpCondFalse = 0 + OpCondTrue = 1 + OpCondSkip = 2 +) + +// opcodeArray holds details about all possible opcodes such as how many bytes +// the opcode and any associated data should take, its human-readable name, and +// the handler function. +var opcodeArray = [256]opcode{ + // Data push opcodes. + OP_FALSE: {OP_FALSE, "OP_0", 1, opcodeFalse}, + OP_DATA_1: {OP_DATA_1, "OP_DATA_1", 2, opcodePushData}, + OP_DATA_2: {OP_DATA_2, "OP_DATA_2", 3, opcodePushData}, + OP_DATA_3: {OP_DATA_3, "OP_DATA_3", 4, opcodePushData}, + OP_DATA_4: {OP_DATA_4, "OP_DATA_4", 5, opcodePushData}, + OP_DATA_5: {OP_DATA_5, "OP_DATA_5", 6, opcodePushData}, + OP_DATA_6: {OP_DATA_6, "OP_DATA_6", 7, opcodePushData}, + OP_DATA_7: {OP_DATA_7, "OP_DATA_7", 8, opcodePushData}, + OP_DATA_8: {OP_DATA_8, "OP_DATA_8", 9, opcodePushData}, + OP_DATA_9: {OP_DATA_9, "OP_DATA_9", 10, opcodePushData}, + OP_DATA_10: {OP_DATA_10, "OP_DATA_10", 11, opcodePushData}, + OP_DATA_11: {OP_DATA_11, "OP_DATA_11", 12, opcodePushData}, + OP_DATA_12: {OP_DATA_12, "OP_DATA_12", 13, opcodePushData}, + OP_DATA_13: {OP_DATA_13, "OP_DATA_13", 14, opcodePushData}, + OP_DATA_14: {OP_DATA_14, "OP_DATA_14", 15, opcodePushData}, + OP_DATA_15: {OP_DATA_15, "OP_DATA_15", 16, opcodePushData}, + OP_DATA_16: {OP_DATA_16, "OP_DATA_16", 17, opcodePushData}, + OP_DATA_17: {OP_DATA_17, "OP_DATA_17", 18, opcodePushData}, + OP_DATA_18: {OP_DATA_18, "OP_DATA_18", 19, opcodePushData}, + OP_DATA_19: {OP_DATA_19, "OP_DATA_19", 20, opcodePushData}, + OP_DATA_20: {OP_DATA_20, "OP_DATA_20", 21, opcodePushData}, + OP_DATA_21: {OP_DATA_21, "OP_DATA_21", 22, opcodePushData}, + OP_DATA_22: {OP_DATA_22, "OP_DATA_22", 23, opcodePushData}, + OP_DATA_23: {OP_DATA_23, "OP_DATA_23", 24, opcodePushData}, + OP_DATA_24: {OP_DATA_24, "OP_DATA_24", 25, opcodePushData}, + OP_DATA_25: {OP_DATA_25, "OP_DATA_25", 26, opcodePushData}, + OP_DATA_26: {OP_DATA_26, "OP_DATA_26", 27, opcodePushData}, + OP_DATA_27: {OP_DATA_27, "OP_DATA_27", 28, opcodePushData}, + OP_DATA_28: {OP_DATA_28, "OP_DATA_28", 29, opcodePushData}, + OP_DATA_29: {OP_DATA_29, "OP_DATA_29", 30, opcodePushData}, + OP_DATA_30: {OP_DATA_30, "OP_DATA_30", 31, opcodePushData}, + OP_DATA_31: {OP_DATA_31, "OP_DATA_31", 32, opcodePushData}, + OP_DATA_32: {OP_DATA_32, "OP_DATA_32", 33, opcodePushData}, + OP_DATA_33: {OP_DATA_33, "OP_DATA_33", 34, opcodePushData}, + OP_DATA_34: {OP_DATA_34, "OP_DATA_34", 35, opcodePushData}, + OP_DATA_35: {OP_DATA_35, "OP_DATA_35", 36, opcodePushData}, + OP_DATA_36: {OP_DATA_36, "OP_DATA_36", 37, opcodePushData}, + OP_DATA_37: {OP_DATA_37, "OP_DATA_37", 38, opcodePushData}, + OP_DATA_38: {OP_DATA_38, "OP_DATA_38", 39, opcodePushData}, + OP_DATA_39: {OP_DATA_39, "OP_DATA_39", 40, opcodePushData}, + OP_DATA_40: {OP_DATA_40, "OP_DATA_40", 41, opcodePushData}, + OP_DATA_41: {OP_DATA_41, "OP_DATA_41", 42, opcodePushData}, + OP_DATA_42: {OP_DATA_42, "OP_DATA_42", 43, opcodePushData}, + OP_DATA_43: {OP_DATA_43, "OP_DATA_43", 44, opcodePushData}, + OP_DATA_44: {OP_DATA_44, "OP_DATA_44", 45, opcodePushData}, + OP_DATA_45: {OP_DATA_45, "OP_DATA_45", 46, opcodePushData}, + OP_DATA_46: {OP_DATA_46, "OP_DATA_46", 47, opcodePushData}, + OP_DATA_47: {OP_DATA_47, "OP_DATA_47", 48, opcodePushData}, + OP_DATA_48: {OP_DATA_48, "OP_DATA_48", 49, opcodePushData}, + OP_DATA_49: {OP_DATA_49, "OP_DATA_49", 50, opcodePushData}, + OP_DATA_50: {OP_DATA_50, "OP_DATA_50", 51, opcodePushData}, + OP_DATA_51: {OP_DATA_51, "OP_DATA_51", 52, opcodePushData}, + OP_DATA_52: {OP_DATA_52, "OP_DATA_52", 53, opcodePushData}, + OP_DATA_53: {OP_DATA_53, "OP_DATA_53", 54, opcodePushData}, + OP_DATA_54: {OP_DATA_54, "OP_DATA_54", 55, opcodePushData}, + OP_DATA_55: {OP_DATA_55, "OP_DATA_55", 56, opcodePushData}, + OP_DATA_56: {OP_DATA_56, "OP_DATA_56", 57, opcodePushData}, + OP_DATA_57: {OP_DATA_57, "OP_DATA_57", 58, opcodePushData}, + OP_DATA_58: {OP_DATA_58, "OP_DATA_58", 59, opcodePushData}, + OP_DATA_59: {OP_DATA_59, "OP_DATA_59", 60, opcodePushData}, + OP_DATA_60: {OP_DATA_60, "OP_DATA_60", 61, opcodePushData}, + OP_DATA_61: {OP_DATA_61, "OP_DATA_61", 62, opcodePushData}, + OP_DATA_62: {OP_DATA_62, "OP_DATA_62", 63, opcodePushData}, + OP_DATA_63: {OP_DATA_63, "OP_DATA_63", 64, opcodePushData}, + OP_DATA_64: {OP_DATA_64, "OP_DATA_64", 65, opcodePushData}, + OP_DATA_65: {OP_DATA_65, "OP_DATA_65", 66, opcodePushData}, + OP_DATA_66: {OP_DATA_66, "OP_DATA_66", 67, opcodePushData}, + OP_DATA_67: {OP_DATA_67, "OP_DATA_67", 68, opcodePushData}, + OP_DATA_68: {OP_DATA_68, "OP_DATA_68", 69, opcodePushData}, + OP_DATA_69: {OP_DATA_69, "OP_DATA_69", 70, opcodePushData}, + OP_DATA_70: {OP_DATA_70, "OP_DATA_70", 71, opcodePushData}, + OP_DATA_71: {OP_DATA_71, "OP_DATA_71", 72, opcodePushData}, + OP_DATA_72: {OP_DATA_72, "OP_DATA_72", 73, opcodePushData}, + OP_DATA_73: {OP_DATA_73, "OP_DATA_73", 74, opcodePushData}, + OP_DATA_74: {OP_DATA_74, "OP_DATA_74", 75, opcodePushData}, + OP_DATA_75: {OP_DATA_75, "OP_DATA_75", 76, opcodePushData}, + OP_PUSHDATA1: {OP_PUSHDATA1, "OP_PUSHDATA1", -1, opcodePushData}, + OP_PUSHDATA2: {OP_PUSHDATA2, "OP_PUSHDATA2", -2, opcodePushData}, + OP_PUSHDATA4: {OP_PUSHDATA4, "OP_PUSHDATA4", -4, opcodePushData}, + OP_1NEGATE: {OP_1NEGATE, "OP_1NEGATE", 1, opcode1Negate}, + OP_RESERVED: {OP_RESERVED, "OP_RESERVED", 1, opcodeReserved}, + OP_TRUE: {OP_TRUE, "OP_1", 1, opcodeN}, + OP_2: {OP_2, "OP_2", 1, opcodeN}, + OP_3: {OP_3, "OP_3", 1, opcodeN}, + OP_4: {OP_4, "OP_4", 1, opcodeN}, + OP_5: {OP_5, "OP_5", 1, opcodeN}, + OP_6: {OP_6, "OP_6", 1, opcodeN}, + OP_7: {OP_7, "OP_7", 1, opcodeN}, + OP_8: {OP_8, "OP_8", 1, opcodeN}, + OP_9: {OP_9, "OP_9", 1, opcodeN}, + OP_10: {OP_10, "OP_10", 1, opcodeN}, + OP_11: {OP_11, "OP_11", 1, opcodeN}, + OP_12: {OP_12, "OP_12", 1, opcodeN}, + OP_13: {OP_13, "OP_13", 1, opcodeN}, + OP_14: {OP_14, "OP_14", 1, opcodeN}, + OP_15: {OP_15, "OP_15", 1, opcodeN}, + OP_16: {OP_16, "OP_16", 1, opcodeN}, + + // Control opcodes. + OP_NOP: {OP_NOP, "OP_NOP", 1, opcodeNop}, + OP_VER: {OP_VER, "OP_VER", 1, opcodeReserved}, + OP_IF: {OP_IF, "OP_IF", 1, opcodeIf}, + OP_NOTIF: {OP_NOTIF, "OP_NOTIF", 1, opcodeNotIf}, + OP_VERIF: {OP_VERIF, "OP_VERIF", 1, opcodeReserved}, + OP_VERNOTIF: {OP_VERNOTIF, "OP_VERNOTIF", 1, opcodeReserved}, + OP_ELSE: {OP_ELSE, "OP_ELSE", 1, opcodeElse}, + OP_ENDIF: {OP_ENDIF, "OP_ENDIF", 1, opcodeEndif}, + OP_VERIFY: {OP_VERIFY, "OP_VERIFY", 1, opcodeVerify}, + OP_RETURN: {OP_RETURN, "OP_RETURN", 1, opcodeReturn}, + OP_CHECKLOCKTIMEVERIFY: {OP_CHECKLOCKTIMEVERIFY, "OP_CHECKLOCKTIMEVERIFY", 1, opcodeCheckLockTimeVerify}, + + // Stack opcodes. + OP_TOALTSTACK: {OP_TOALTSTACK, "OP_TOALTSTACK", 1, opcodeToAltStack}, + OP_FROMALTSTACK: {OP_FROMALTSTACK, "OP_FROMALTSTACK", 1, opcodeFromAltStack}, + OP_2DROP: {OP_2DROP, "OP_2DROP", 1, opcode2Drop}, + OP_2DUP: {OP_2DUP, "OP_2DUP", 1, opcode2Dup}, + OP_3DUP: {OP_3DUP, "OP_3DUP", 1, opcode3Dup}, + OP_2OVER: {OP_2OVER, "OP_2OVER", 1, opcode2Over}, + OP_2ROT: {OP_2ROT, "OP_2ROT", 1, opcode2Rot}, + OP_2SWAP: {OP_2SWAP, "OP_2SWAP", 1, opcode2Swap}, + OP_IFDUP: {OP_IFDUP, "OP_IFDUP", 1, opcodeIfDup}, + OP_DEPTH: {OP_DEPTH, "OP_DEPTH", 1, opcodeDepth}, + OP_DROP: {OP_DROP, "OP_DROP", 1, opcodeDrop}, + OP_DUP: {OP_DUP, "OP_DUP", 1, opcodeDup}, + OP_NIP: {OP_NIP, "OP_NIP", 1, opcodeNip}, + OP_OVER: {OP_OVER, "OP_OVER", 1, opcodeOver}, + OP_PICK: {OP_PICK, "OP_PICK", 1, opcodePick}, + OP_ROLL: {OP_ROLL, "OP_ROLL", 1, opcodeRoll}, + OP_ROT: {OP_ROT, "OP_ROT", 1, opcodeRot}, + OP_SWAP: {OP_SWAP, "OP_SWAP", 1, opcodeSwap}, + OP_TUCK: {OP_TUCK, "OP_TUCK", 1, opcodeTuck}, + + // Splice opcodes. + OP_CAT: {OP_CAT, "OP_CAT", 1, opcodeDisabled}, + OP_SUBSTR: {OP_SUBSTR, "OP_SUBSTR", 1, opcodeDisabled}, + OP_LEFT: {OP_LEFT, "OP_LEFT", 1, opcodeDisabled}, + OP_RIGHT: {OP_RIGHT, "OP_RIGHT", 1, opcodeDisabled}, + OP_SIZE: {OP_SIZE, "OP_SIZE", 1, opcodeSize}, + + // Bitwise logic opcodes. + OP_INVERT: {OP_INVERT, "OP_INVERT", 1, opcodeDisabled}, + OP_AND: {OP_AND, "OP_AND", 1, opcodeDisabled}, + OP_OR: {OP_OR, "OP_OR", 1, opcodeDisabled}, + OP_XOR: {OP_XOR, "OP_XOR", 1, opcodeDisabled}, + OP_EQUAL: {OP_EQUAL, "OP_EQUAL", 1, opcodeEqual}, + OP_EQUALVERIFY: {OP_EQUALVERIFY, "OP_EQUALVERIFY", 1, opcodeEqualVerify}, + OP_RESERVED1: {OP_RESERVED1, "OP_RESERVED1", 1, opcodeReserved}, + OP_RESERVED2: {OP_RESERVED2, "OP_RESERVED2", 1, opcodeReserved}, + + // Numeric related opcodes. + OP_1ADD: {OP_1ADD, "OP_1ADD", 1, opcode1Add}, + OP_1SUB: {OP_1SUB, "OP_1SUB", 1, opcode1Sub}, + OP_2MUL: {OP_2MUL, "OP_2MUL", 1, opcodeDisabled}, + OP_2DIV: {OP_2DIV, "OP_2DIV", 1, opcodeDisabled}, + OP_NEGATE: {OP_NEGATE, "OP_NEGATE", 1, opcodeNegate}, + OP_ABS: {OP_ABS, "OP_ABS", 1, opcodeAbs}, + OP_NOT: {OP_NOT, "OP_NOT", 1, opcodeNot}, + OP_0NOTEQUAL: {OP_0NOTEQUAL, "OP_0NOTEQUAL", 1, opcode0NotEqual}, + OP_ADD: {OP_ADD, "OP_ADD", 1, opcodeAdd}, + OP_SUB: {OP_SUB, "OP_SUB", 1, opcodeSub}, + OP_MUL: {OP_MUL, "OP_MUL", 1, opcodeDisabled}, + OP_DIV: {OP_DIV, "OP_DIV", 1, opcodeDisabled}, + OP_MOD: {OP_MOD, "OP_MOD", 1, opcodeDisabled}, + OP_LSHIFT: {OP_LSHIFT, "OP_LSHIFT", 1, opcodeDisabled}, + OP_RSHIFT: {OP_RSHIFT, "OP_RSHIFT", 1, opcodeDisabled}, + OP_BOOLAND: {OP_BOOLAND, "OP_BOOLAND", 1, opcodeBoolAnd}, + OP_BOOLOR: {OP_BOOLOR, "OP_BOOLOR", 1, opcodeBoolOr}, + OP_NUMEQUAL: {OP_NUMEQUAL, "OP_NUMEQUAL", 1, opcodeNumEqual}, + OP_NUMEQUALVERIFY: {OP_NUMEQUALVERIFY, "OP_NUMEQUALVERIFY", 1, opcodeNumEqualVerify}, + OP_NUMNOTEQUAL: {OP_NUMNOTEQUAL, "OP_NUMNOTEQUAL", 1, opcodeNumNotEqual}, + OP_LESSTHAN: {OP_LESSTHAN, "OP_LESSTHAN", 1, opcodeLessThan}, + OP_GREATERTHAN: {OP_GREATERTHAN, "OP_GREATERTHAN", 1, opcodeGreaterThan}, + OP_LESSTHANOREQUAL: {OP_LESSTHANOREQUAL, "OP_LESSTHANOREQUAL", 1, opcodeLessThanOrEqual}, + OP_GREATERTHANOREQUAL: {OP_GREATERTHANOREQUAL, "OP_GREATERTHANOREQUAL", 1, opcodeGreaterThanOrEqual}, + OP_MIN: {OP_MIN, "OP_MIN", 1, opcodeMin}, + OP_MAX: {OP_MAX, "OP_MAX", 1, opcodeMax}, + OP_WITHIN: {OP_WITHIN, "OP_WITHIN", 1, opcodeWithin}, + + // Crypto opcodes. + OP_RIPEMD160: {OP_RIPEMD160, "OP_RIPEMD160", 1, opcodeRipemd160}, + OP_SHA1: {OP_SHA1, "OP_SHA1", 1, opcodeSha1}, + OP_SHA256: {OP_SHA256, "OP_SHA256", 1, opcodeSha256}, + OP_HASH160: {OP_HASH160, "OP_HASH160", 1, opcodeHash160}, + OP_HASH256: {OP_HASH256, "OP_HASH256", 1, opcodeHash256}, + OP_CODESEPARATOR: {OP_CODESEPARATOR, "OP_CODESEPARATOR", 1, opcodeCodeSeparator}, + OP_CHECKSIG: {OP_CHECKSIG, "OP_CHECKSIG", 1, opcodeCheckSig}, + OP_CHECKSIGVERIFY: {OP_CHECKSIGVERIFY, "OP_CHECKSIGVERIFY", 1, opcodeCheckSigVerify}, + OP_CHECKMULTISIG: {OP_CHECKMULTISIG, "OP_CHECKMULTISIG", 1, opcodeCheckMultiSig}, + OP_CHECKMULTISIGVERIFY: {OP_CHECKMULTISIGVERIFY, "OP_CHECKMULTISIGVERIFY", 1, opcodeCheckMultiSigVerify}, + + // Reserved opcodes. + OP_NOP1: {OP_NOP1, "OP_NOP1", 1, opcodeNop}, + OP_NOP3: {OP_NOP3, "OP_NOP3", 1, opcodeNop}, + OP_NOP4: {OP_NOP4, "OP_NOP4", 1, opcodeNop}, + OP_NOP5: {OP_NOP5, "OP_NOP5", 1, opcodeNop}, + OP_NOP6: {OP_NOP6, "OP_NOP6", 1, opcodeNop}, + OP_NOP7: {OP_NOP7, "OP_NOP7", 1, opcodeNop}, + OP_NOP8: {OP_NOP8, "OP_NOP8", 1, opcodeNop}, + OP_NOP9: {OP_NOP9, "OP_NOP9", 1, opcodeNop}, + OP_NOP10: {OP_NOP10, "OP_NOP10", 1, opcodeNop}, + + // Undefined opcodes. + OP_UNKNOWN186: {OP_UNKNOWN186, "OP_UNKNOWN186", 1, opcodeInvalid}, + OP_UNKNOWN187: {OP_UNKNOWN187, "OP_UNKNOWN187", 1, opcodeInvalid}, + OP_UNKNOWN188: {OP_UNKNOWN188, "OP_UNKNOWN188", 1, opcodeInvalid}, + OP_UNKNOWN189: {OP_UNKNOWN189, "OP_UNKNOWN189", 1, opcodeInvalid}, + OP_UNKNOWN190: {OP_UNKNOWN190, "OP_UNKNOWN190", 1, opcodeInvalid}, + OP_UNKNOWN191: {OP_UNKNOWN191, "OP_UNKNOWN191", 1, opcodeInvalid}, + OP_UNKNOWN192: {OP_UNKNOWN192, "OP_UNKNOWN192", 1, opcodeInvalid}, + OP_UNKNOWN193: {OP_UNKNOWN193, "OP_UNKNOWN193", 1, opcodeInvalid}, + OP_UNKNOWN194: {OP_UNKNOWN194, "OP_UNKNOWN194", 1, opcodeInvalid}, + OP_UNKNOWN195: {OP_UNKNOWN195, "OP_UNKNOWN195", 1, opcodeInvalid}, + OP_UNKNOWN196: {OP_UNKNOWN196, "OP_UNKNOWN196", 1, opcodeInvalid}, + OP_UNKNOWN197: {OP_UNKNOWN197, "OP_UNKNOWN197", 1, opcodeInvalid}, + OP_UNKNOWN198: {OP_UNKNOWN198, "OP_UNKNOWN198", 1, opcodeInvalid}, + OP_UNKNOWN199: {OP_UNKNOWN199, "OP_UNKNOWN199", 1, opcodeInvalid}, + OP_UNKNOWN200: {OP_UNKNOWN200, "OP_UNKNOWN200", 1, opcodeInvalid}, + OP_UNKNOWN201: {OP_UNKNOWN201, "OP_UNKNOWN201", 1, opcodeInvalid}, + OP_UNKNOWN202: {OP_UNKNOWN202, "OP_UNKNOWN202", 1, opcodeInvalid}, + OP_UNKNOWN203: {OP_UNKNOWN203, "OP_UNKNOWN203", 1, opcodeInvalid}, + OP_UNKNOWN204: {OP_UNKNOWN204, "OP_UNKNOWN204", 1, opcodeInvalid}, + OP_UNKNOWN205: {OP_UNKNOWN205, "OP_UNKNOWN205", 1, opcodeInvalid}, + OP_UNKNOWN206: {OP_UNKNOWN206, "OP_UNKNOWN206", 1, opcodeInvalid}, + OP_UNKNOWN207: {OP_UNKNOWN207, "OP_UNKNOWN207", 1, opcodeInvalid}, + OP_UNKNOWN208: {OP_UNKNOWN208, "OP_UNKNOWN208", 1, opcodeInvalid}, + OP_UNKNOWN209: {OP_UNKNOWN209, "OP_UNKNOWN209", 1, opcodeInvalid}, + OP_UNKNOWN210: {OP_UNKNOWN210, "OP_UNKNOWN210", 1, opcodeInvalid}, + OP_UNKNOWN211: {OP_UNKNOWN211, "OP_UNKNOWN211", 1, opcodeInvalid}, + OP_UNKNOWN212: {OP_UNKNOWN212, "OP_UNKNOWN212", 1, opcodeInvalid}, + OP_UNKNOWN213: {OP_UNKNOWN213, "OP_UNKNOWN213", 1, opcodeInvalid}, + OP_UNKNOWN214: {OP_UNKNOWN214, "OP_UNKNOWN214", 1, opcodeInvalid}, + OP_UNKNOWN215: {OP_UNKNOWN215, "OP_UNKNOWN215", 1, opcodeInvalid}, + OP_UNKNOWN216: {OP_UNKNOWN216, "OP_UNKNOWN216", 1, opcodeInvalid}, + OP_UNKNOWN217: {OP_UNKNOWN217, "OP_UNKNOWN217", 1, opcodeInvalid}, + OP_UNKNOWN218: {OP_UNKNOWN218, "OP_UNKNOWN218", 1, opcodeInvalid}, + OP_UNKNOWN219: {OP_UNKNOWN219, "OP_UNKNOWN219", 1, opcodeInvalid}, + OP_UNKNOWN220: {OP_UNKNOWN220, "OP_UNKNOWN220", 1, opcodeInvalid}, + OP_UNKNOWN221: {OP_UNKNOWN221, "OP_UNKNOWN221", 1, opcodeInvalid}, + OP_UNKNOWN222: {OP_UNKNOWN222, "OP_UNKNOWN222", 1, opcodeInvalid}, + OP_UNKNOWN223: {OP_UNKNOWN223, "OP_UNKNOWN223", 1, opcodeInvalid}, + OP_UNKNOWN224: {OP_UNKNOWN224, "OP_UNKNOWN224", 1, opcodeInvalid}, + OP_UNKNOWN225: {OP_UNKNOWN225, "OP_UNKNOWN225", 1, opcodeInvalid}, + OP_UNKNOWN226: {OP_UNKNOWN226, "OP_UNKNOWN226", 1, opcodeInvalid}, + OP_UNKNOWN227: {OP_UNKNOWN227, "OP_UNKNOWN227", 1, opcodeInvalid}, + OP_UNKNOWN228: {OP_UNKNOWN228, "OP_UNKNOWN228", 1, opcodeInvalid}, + OP_UNKNOWN229: {OP_UNKNOWN229, "OP_UNKNOWN229", 1, opcodeInvalid}, + OP_UNKNOWN230: {OP_UNKNOWN230, "OP_UNKNOWN230", 1, opcodeInvalid}, + OP_UNKNOWN231: {OP_UNKNOWN231, "OP_UNKNOWN231", 1, opcodeInvalid}, + OP_UNKNOWN232: {OP_UNKNOWN232, "OP_UNKNOWN232", 1, opcodeInvalid}, + OP_UNKNOWN233: {OP_UNKNOWN233, "OP_UNKNOWN233", 1, opcodeInvalid}, + OP_UNKNOWN234: {OP_UNKNOWN234, "OP_UNKNOWN234", 1, opcodeInvalid}, + OP_UNKNOWN235: {OP_UNKNOWN235, "OP_UNKNOWN235", 1, opcodeInvalid}, + OP_UNKNOWN236: {OP_UNKNOWN236, "OP_UNKNOWN236", 1, opcodeInvalid}, + OP_UNKNOWN237: {OP_UNKNOWN237, "OP_UNKNOWN237", 1, opcodeInvalid}, + OP_UNKNOWN238: {OP_UNKNOWN238, "OP_UNKNOWN238", 1, opcodeInvalid}, + OP_UNKNOWN239: {OP_UNKNOWN239, "OP_UNKNOWN239", 1, opcodeInvalid}, + OP_UNKNOWN240: {OP_UNKNOWN240, "OP_UNKNOWN240", 1, opcodeInvalid}, + OP_UNKNOWN241: {OP_UNKNOWN241, "OP_UNKNOWN241", 1, opcodeInvalid}, + OP_UNKNOWN242: {OP_UNKNOWN242, "OP_UNKNOWN242", 1, opcodeInvalid}, + OP_UNKNOWN243: {OP_UNKNOWN243, "OP_UNKNOWN243", 1, opcodeInvalid}, + OP_UNKNOWN244: {OP_UNKNOWN244, "OP_UNKNOWN244", 1, opcodeInvalid}, + OP_UNKNOWN245: {OP_UNKNOWN245, "OP_UNKNOWN245", 1, opcodeInvalid}, + OP_UNKNOWN246: {OP_UNKNOWN246, "OP_UNKNOWN246", 1, opcodeInvalid}, + OP_UNKNOWN247: {OP_UNKNOWN247, "OP_UNKNOWN247", 1, opcodeInvalid}, + OP_UNKNOWN248: {OP_UNKNOWN248, "OP_UNKNOWN248", 1, opcodeInvalid}, + + // Bitcoin Core internal use opcode. Defined here for completeness. + OP_SMALLDATA: {OP_SMALLDATA, "OP_SMALLDATA", 1, opcodeInvalid}, + OP_SMALLINTEGER: {OP_SMALLINTEGER, "OP_SMALLINTEGER", 1, opcodeInvalid}, + OP_PUBKEYS: {OP_PUBKEYS, "OP_PUBKEYS", 1, opcodeInvalid}, + OP_UNKNOWN252: {OP_UNKNOWN252, "OP_UNKNOWN252", 1, opcodeInvalid}, + OP_PUBKEYHASH: {OP_PUBKEYHASH, "OP_PUBKEYHASH", 1, opcodeInvalid}, + OP_PUBKEY: {OP_PUBKEY, "OP_PUBKEY", 1, opcodeInvalid}, + + OP_INVALIDOPCODE: {OP_INVALIDOPCODE, "OP_INVALIDOPCODE", 1, opcodeInvalid}, +} + +// opcodeOnelineRepls defines opcode names which are replaced when doing a +// one-line disassembly. This is done to match the output of the reference +// implementation while not changing the opcode names in the nicer full +// disassembly. +var opcodeOnelineRepls = map[string]string{ + "OP_1NEGATE": "-1", + "OP_0": "0", + "OP_1": "1", + "OP_2": "2", + "OP_3": "3", + "OP_4": "4", + "OP_5": "5", + "OP_6": "6", + "OP_7": "7", + "OP_8": "8", + "OP_9": "9", + "OP_10": "10", + "OP_11": "11", + "OP_12": "12", + "OP_13": "13", + "OP_14": "14", + "OP_15": "15", + "OP_16": "16", +} + +// parsedOpcode represents an opcode that has been parsed and includes any +// potential data associated with it. +type parsedOpcode struct { + opcode *opcode + data []byte +} + +// isDisabled returns whether or not the opcode is disabled and thus is always +// bad to see in the instruction stream (even if turned off by a conditional). +func (pop *parsedOpcode) isDisabled() bool { + switch pop.opcode.value { + case OP_CAT: + return true + case OP_SUBSTR: + return true + case OP_LEFT: + return true + case OP_RIGHT: + return true + case OP_INVERT: + return true + case OP_AND: + return true + case OP_OR: + return true + case OP_XOR: + return true + case OP_2MUL: + return true + case OP_2DIV: + return true + case OP_MUL: + return true + case OP_DIV: + return true + case OP_MOD: + return true + case OP_LSHIFT: + return true + case OP_RSHIFT: + return true + default: + return false + } +} + +// alwaysIllegal returns whether or not the opcode is always illegal when passed +// over by the program counter even if in a non-executed branch (it isn't a +// coincidence that they are conditionals). +func (pop *parsedOpcode) alwaysIllegal() bool { + switch pop.opcode.value { + case OP_VERIF: + return true + case OP_VERNOTIF: + return true + default: + return false + } +} + +// isConditional returns whether or not the opcode is a conditional opcode which +// changes the conditional execution stack when executed. +func (pop *parsedOpcode) isConditional() bool { + switch pop.opcode.value { + case OP_IF: + return true + case OP_NOTIF: + return true + case OP_ELSE: + return true + case OP_ENDIF: + return true + default: + return false + } +} + +// checkMinimalDataPush returns whether or not the current data push uses the +// smallest possible opcode to represent it. For example, the value 15 could +// be pushed with OP_DATA_1 15 (among other variations); however, OP_15 is a +// single opcode that represents the same value and is only a single byte versus +// two bytes. +func (pop *parsedOpcode) checkMinimalDataPush() error { + data := pop.data + dataLen := len(data) + opcode := pop.opcode.value + + if dataLen == 0 && opcode != OP_0 { + return ErrStackMinimalData + } else if dataLen == 1 && data[0] >= 1 && data[0] <= 16 { + if opcode != OP_1+data[0]-1 { + // Should have used OP_1 .. OP_16 + return ErrStackMinimalData + } + } else if dataLen == 1 && data[0] == 0x81 { + if opcode != OP_1NEGATE { + return ErrStackMinimalData + } + } else if dataLen <= 75 { + if int(opcode) != dataLen { + // Should have used a direct push + return ErrStackMinimalData + } + } else if dataLen <= 255 { + if opcode != OP_PUSHDATA1 { + return ErrStackMinimalData + } + } else if dataLen <= 65535 { + if opcode != OP_PUSHDATA2 { + return ErrStackMinimalData + } + } + return nil +} + +// print returns a human-readable string representation of the opcode for use +// in script disassembly. +func (pop *parsedOpcode) print(oneline bool) string { + // The reference implementation one-line disassembly replaces opcodes + // which represent values (e.g. OP_0 through OP_16 and OP_1NEGATE) + // with the raw value. However, when not doing a one-line dissassembly, + // we prefer to show the actual opcode names. Thus, only replace the + // opcodes in question when the oneline flag is set. + opcodeName := pop.opcode.name + if oneline { + if replName, ok := opcodeOnelineRepls[opcodeName]; ok { + opcodeName = replName + } + + // Nothing more to do for non-data push opcodes. + if pop.opcode.length == 1 { + return opcodeName + } + + return fmt.Sprintf("%x", pop.data) + } + + // Nothing more to do for non-data push opcodes. + if pop.opcode.length == 1 { + return opcodeName + } + + // Add length for the OP_PUSHDATA# opcodes. + retString := opcodeName + switch pop.opcode.length { + case -1: + retString += fmt.Sprintf(" 0x%02x", len(pop.data)) + case -2: + retString += fmt.Sprintf(" 0x%04x", len(pop.data)) + case -4: + retString += fmt.Sprintf(" 0x%08x", len(pop.data)) + } + + return fmt.Sprintf("%s 0x%02x", retString, pop.data) +} + +// bytes returns any data associated with the opcode encoded as it would be in +// a script. This is used for unparsing scripts from parsed opcodes. +func (pop *parsedOpcode) bytes() ([]byte, error) { + var retbytes []byte + if pop.opcode.length > 0 { + retbytes = make([]byte, 1, pop.opcode.length) + } else { + retbytes = make([]byte, 1, 1+len(pop.data)- + pop.opcode.length) + } + + retbytes[0] = pop.opcode.value + if pop.opcode.length == 1 { + if len(pop.data) != 0 { + return nil, ErrStackInvalidOpcode + } + return retbytes, nil + } + nbytes := pop.opcode.length + if pop.opcode.length < 0 { + l := len(pop.data) + // tempting just to hardcode to avoid the complexity here. + switch pop.opcode.length { + case -1: + retbytes = append(retbytes, byte(l)) + nbytes = int(retbytes[1]) + len(retbytes) + case -2: + retbytes = append(retbytes, byte(l&0xff), + byte(l>>8&0xff)) + nbytes = int(binary.LittleEndian.Uint16(retbytes[1:])) + + len(retbytes) + case -4: + retbytes = append(retbytes, byte(l&0xff), + byte((l>>8)&0xff), byte((l>>16)&0xff), + byte((l>>24)&0xff)) + nbytes = int(binary.LittleEndian.Uint32(retbytes[1:])) + + len(retbytes) + } + } + + retbytes = append(retbytes, pop.data...) + + if len(retbytes) != nbytes { + return nil, ErrStackInvalidOpcode + } + + return retbytes, nil +} + +// ******************************************* +// Opcode implementation functions start here. +// ******************************************* + +// opcodeDisabled is a common handler for disabled opcodes. It returns an +// appropriate error indicating the opcode is disabled. While it would +// ordinarily make more sense to detect if the script contains any disabled +// opcodes before executing in an initial parse step, the consensus rules +// dictate the script doesn't fail until the program counter passes over a +// disabled opcode (even when they appear in a branch that is not executed). +func opcodeDisabled(op *parsedOpcode, vm *Engine) error { + return ErrStackOpDisabled +} + +// opcodeReserved is a common handler for all reserved opcodes. It returns an +// appropriate error indicating the opcode is reserved. +func opcodeReserved(op *parsedOpcode, vm *Engine) error { + return ErrStackReservedOpcode +} + +// opcodeInvalid is a common handler for all invalid opcodes. It returns an +// appropriate error indicating the opcode is invalid. +func opcodeInvalid(op *parsedOpcode, vm *Engine) error { + return ErrStackInvalidOpcode +} + +// opcodeFalse pushes an empty array to the data stack to represent false. Note +// that 0, when encoded as a number according to the numeric encoding consensus +// rules, is an empty array. +func opcodeFalse(op *parsedOpcode, vm *Engine) error { + vm.dstack.PushByteArray(nil) + return nil +} + +// opcodePushData is a common handler for the vast majority of opcodes that push +// raw data (bytes) to the data stack. +func opcodePushData(op *parsedOpcode, vm *Engine) error { + vm.dstack.PushByteArray(op.data) + return nil +} + +// opcode1Negate pushes -1, encoded as a number, to the data stack. +func opcode1Negate(op *parsedOpcode, vm *Engine) error { + vm.dstack.PushInt(scriptNum(-1)) + return nil +} + +// opcodeN is a common handler for the small integer data push opcodes. It +// pushes the numeric value the opcode represents (which will be from 1 to 16) +// onto the data stack. +func opcodeN(op *parsedOpcode, vm *Engine) error { + // The opcodes are all defined consecutively, so the numeric value is + // the difference. + vm.dstack.PushInt(scriptNum((op.opcode.value - (OP_1 - 1)))) + return nil +} + +// opcodeNop is a common handler for the NOP family of opcodes. As the name +// implies it generally does nothing, however, it will return an error when +// the flag to discourage use of NOPs is set for select opcodes. +func opcodeNop(op *parsedOpcode, vm *Engine) error { + switch op.opcode.value { + case OP_NOP1, OP_NOP3, OP_NOP4, OP_NOP5, + OP_NOP6, OP_NOP7, OP_NOP8, OP_NOP9, OP_NOP10: + if vm.hasFlag(ScriptDiscourageUpgradableNops) { + return fmt.Errorf("OP_NOP%d reserved for soft-fork "+ + "upgrades", op.opcode.value-(OP_NOP1-1)) + } + } + return nil +} + +// opcodeIf treats the top item on the data stack as a boolean and removes it. +// +// An appropriate entry is added to the conditional stack depending on whether +// the boolean is true and whether this if is on an executing branch in order +// to allow proper execution of further opcodes depending on the conditional +// logic. When the boolean is true, the first branch will be executed (unless +// this opcode is nested in a non-executed branch). +// +// <expression> if [statements] [else [statements]] endif +// +// Note that, unlike for all non-conditional opcodes, this is executed even when +// it is on a non-executing branch so proper nesting is maintained. +// +// Data stack transformation: [... bool] -> [...] +// Conditional stack transformation: [...] -> [... OpCondValue] +func opcodeIf(op *parsedOpcode, vm *Engine) error { + condVal := OpCondFalse + if vm.isBranchExecuting() { + ok, err := vm.dstack.PopBool() + if err != nil { + return err + } + if ok { + condVal = OpCondTrue + } + } else { + condVal = OpCondSkip + } + vm.condStack = append(vm.condStack, condVal) + return nil +} + +// opcodeNotIf treats the top item on the data stack as a boolean and removes +// it. +// +// An appropriate entry is added to the conditional stack depending on whether +// the boolean is true and whether this if is on an executing branch in order +// to allow proper execution of further opcodes depending on the conditional +// logic. When the boolean is false, the first branch will be executed (unless +// this opcode is nested in a non-executed branch). +// +// <expression> notif [statements] [else [statements]] endif +// +// Note that, unlike for all non-conditional opcodes, this is executed even when +// it is on a non-executing branch so proper nesting is maintained. +// +// Data stack transformation: [... bool] -> [...] +// Conditional stack transformation: [...] -> [... OpCondValue] +func opcodeNotIf(op *parsedOpcode, vm *Engine) error { + condVal := OpCondFalse + if vm.isBranchExecuting() { + ok, err := vm.dstack.PopBool() + if err != nil { + return err + } + if !ok { + condVal = OpCondTrue + } + } else { + condVal = OpCondSkip + } + vm.condStack = append(vm.condStack, condVal) + return nil +} + +// opcodeElse inverts conditional execution for other half of if/else/endif. +// +// An error is returned if there has not already been a matching OP_IF. +// +// Conditional stack transformation: [... OpCondValue] -> [... !OpCondValue] +func opcodeElse(op *parsedOpcode, vm *Engine) error { + if len(vm.condStack) == 0 { + return ErrStackNoIf + } + + conditionalIdx := len(vm.condStack) - 1 + switch vm.condStack[conditionalIdx] { + case OpCondTrue: + vm.condStack[conditionalIdx] = OpCondFalse + case OpCondFalse: + vm.condStack[conditionalIdx] = OpCondTrue + case OpCondSkip: + // Value doesn't change in skip since it indicates this opcode + // is nested in a non-executed branch. + } + return nil +} + +// opcodeEndif terminates a conditional block, removing the value from the +// conditional execution stack. +// +// An error is returned if there has not already been a matching OP_IF. +// +// Conditional stack transformation: [... OpCondValue] -> [...] +func opcodeEndif(op *parsedOpcode, vm *Engine) error { + if len(vm.condStack) == 0 { + return ErrStackNoIf + } + + vm.condStack = vm.condStack[:len(vm.condStack)-1] + return nil +} + +// opcodeVerify examines the top item on the data stack as a boolean value and +// verifies it evaluates to true. An error is returned if it does not. +func opcodeVerify(op *parsedOpcode, vm *Engine) error { + verified, err := vm.dstack.PopBool() + if err != nil { + return err + } + + if verified != true { + return ErrStackVerifyFailed + } + return nil +} + +// opcodeReturn returns an appropriate error since it is always an error to +// return early from a script. +func opcodeReturn(op *parsedOpcode, vm *Engine) error { + return ErrStackEarlyReturn +} + +// opcodeCheckLockTimeVerify compares the top item on the data stack to the +// LockTime field of the transaction containing the script signature +// validating if the transaction outputs are spendable yet. If flag +// ScriptVerifyCheckLockTimeVerify is not set, the code continues as if OP_NOP2 +// were executed. +func opcodeCheckLockTimeVerify(op *parsedOpcode, vm *Engine) error { + // If the ScriptVerifyCheckLockTimeVerify script flag is not set, treat + // opcode as OP_NOP2 instead. + if !vm.hasFlag(ScriptVerifyCheckLockTimeVerify) { + if vm.hasFlag(ScriptDiscourageUpgradableNops) { + return errors.New("OP_NOP2 reserved for soft-fork " + + "upgrades") + } + return nil + } + + // The current transaction locktime is a uint32 resulting in a maximum + // locktime of 2^32-1 (the year 2106). However, scriptNums are signed + // and therefore a standard 4-byte scriptNum would only support up to a + // maximum of 2^31-1 (the year 2038). Thus, a 5-byte scriptNum is used + // here since it will support up to 2^39-1 which allows dates beyond the + // current locktime limit. + // + // PeekByteArray is used here instead of PeekInt because we do not want + // to be limited to a 4-byte integer for reasons specified above. + so, err := vm.dstack.PeekByteArray(0) + if err != nil { + return err + } + lockTime, err := makeScriptNum(so, vm.dstack.verifyMinimalData, 5) + if err != nil { + return err + } + + // In the rare event that the argument may be < 0 due to some arithmetic + // being done first, you can always use 0 OP_MAX OP_CHECKLOCKTIMEVERIFY. + if lockTime < 0 { + return fmt.Errorf("negative locktime: %d", lockTime) + } + + // The lock time field of a transaction is either a block height at + // which the transaction is finalized or a timestamp depending on if the + // value is before the txscript.LockTimeThreshold. When it is under the + // threshold it is a block height. + // + // The lockTimes in both the script and transaction must be of the same + // type. + if !((vm.tx.LockTime < LockTimeThreshold && int64(lockTime) < int64(LockTimeThreshold)) || + (vm.tx.LockTime >= LockTimeThreshold && int64(lockTime) >= int64(LockTimeThreshold))) { + return fmt.Errorf("mismatched locktime types -- tx locktime %d, stack "+ + "locktime %d", vm.tx.LockTime, lockTime) + } + + if int64(lockTime) > int64(vm.tx.LockTime) { + str := "locktime requirement not satisfied -- locktime is " + + "greater than the transaction locktime: %d > %d" + return fmt.Errorf(str, lockTime, vm.tx.LockTime) + } + + // The lock time feature can also be disabled, thereby bypassing + // OP_CHECKLOCKTIMEVERIFY, if every transaction input has been finalized by + // setting its sequence to the maximum value (wire.MaxTxInSequenceNum). This + // condition would result in the transaction being allowed into the blockchain + // making the opcode ineffective. + // + // This condition is prevented by enforcing that the input being used by + // the opcode is unlocked (its sequence number is less than the max + // value). This is sufficient to prove correctness without having to + // check every input. + // + // NOTE: This implies that even if the transaction is not finalized due to + // another input being unlocked, the opcode execution will still fail when the + // input being used by the opcode is locked. + if vm.tx.TxIn[vm.txIdx].Sequence == wire.MaxTxInSequenceNum { + return errors.New("transaction input is finalized") + } + + return nil +} + +// opcodeToAltStack removes the top item from the main data stack and pushes it +// onto the alternate data stack. +// +// Main data stack transformation: [... x1 x2 x3] -> [... x1 x2] +// Alt data stack transformation: [... y1 y2 y3] -> [... y1 y2 y3 x3] +func opcodeToAltStack(op *parsedOpcode, vm *Engine) error { + so, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + vm.astack.PushByteArray(so) + + return nil +} + +// opcodeFromAltStack removes the top item from the alternate data stack and +// pushes it onto the main data stack. +// +// Main data stack transformation: [... x1 x2 x3] -> [... x1 x2 x3 y3] +// Alt data stack transformation: [... y1 y2 y3] -> [... y1 y2] +func opcodeFromAltStack(op *parsedOpcode, vm *Engine) error { + so, err := vm.astack.PopByteArray() + if err != nil { + return err + } + vm.dstack.PushByteArray(so) + + return nil +} + +// opcode2Drop removes the top 2 items from the data stack. +// +// Stack transformation: [... x1 x2 x3] -> [... x1] +func opcode2Drop(op *parsedOpcode, vm *Engine) error { + return vm.dstack.DropN(2) +} + +// opcode2Dup duplicates the top 2 items on the data stack. +// +// Stack transformation: [... x1 x2 x3] -> [... x1 x2 x3 x2 x3] +func opcode2Dup(op *parsedOpcode, vm *Engine) error { + return vm.dstack.DupN(2) +} + +// opcode3Dup duplicates the top 3 items on the data stack. +// +// Stack transformation: [... x1 x2 x3] -> [... x1 x2 x3 x1 x2 x3] +func opcode3Dup(op *parsedOpcode, vm *Engine) error { + return vm.dstack.DupN(3) +} + +// opcode2Over duplicates the 2 items before the top 2 items on the data stack. +// +// Stack transformation: [... x1 x2 x3 x4] -> [... x1 x2 x3 x4 x1 x2] +func opcode2Over(op *parsedOpcode, vm *Engine) error { + return vm.dstack.OverN(2) +} + +// opcode2Rot rotates the top 6 items on the data stack to the left twice. +// +// Stack transformation: [... x1 x2 x3 x4 x5 x6] -> [... x3 x4 x5 x6 x1 x2] +func opcode2Rot(op *parsedOpcode, vm *Engine) error { + return vm.dstack.RotN(2) +} + +// opcode2Swap swaps the top 2 items on the data stack with the 2 that come +// before them. +// +// Stack transformation: [... x1 x2 x3 x4] -> [... x3 x4 x1 x2] +func opcode2Swap(op *parsedOpcode, vm *Engine) error { + return vm.dstack.SwapN(2) +} + +// opcodeIfDup duplicates the top item of the stack if it is not zero. +// +// Stack transformation (x1==0): [... x1] -> [... x1] +// Stack transformation (x1!=0): [... x1] -> [... x1 x1] +func opcodeIfDup(op *parsedOpcode, vm *Engine) error { + so, err := vm.dstack.PeekByteArray(0) + if err != nil { + return err + } + + // Push copy of data iff it isn't zero + if asBool(so) { + vm.dstack.PushByteArray(so) + } + + return nil +} + +// opcodeDepth pushes the depth of the data stack prior to executing this +// opcode, encoded as a number, onto the data stack. +// +// Stack transformation: [...] -> [... <num of items on the stack>] +// Example with 2 items: [x1 x2] -> [x1 x2 2] +// Example with 3 items: [x1 x2 x3] -> [x1 x2 x3 3] +func opcodeDepth(op *parsedOpcode, vm *Engine) error { + vm.dstack.PushInt(scriptNum(vm.dstack.Depth())) + return nil +} + +// opcodeDrop removes the top item from the data stack. +// +// Stack transformation: [... x1 x2 x3] -> [... x1 x2] +func opcodeDrop(op *parsedOpcode, vm *Engine) error { + return vm.dstack.DropN(1) +} + +// opcodeDup duplicates the top item on the data stack. +// +// Stack transformation: [... x1 x2 x3] -> [... x1 x2 x3 x3] +func opcodeDup(op *parsedOpcode, vm *Engine) error { + return vm.dstack.DupN(1) +} + +// opcodeNip removes the item before the top item on the data stack. +// +// Stack transformation: [... x1 x2 x3] -> [... x1 x3] +func opcodeNip(op *parsedOpcode, vm *Engine) error { + return vm.dstack.NipN(1) +} + +// opcodeOver duplicates the item before the top item on the data stack. +// +// Stack transformation: [... x1 x2 x3] -> [... x1 x2 x3 x2] +func opcodeOver(op *parsedOpcode, vm *Engine) error { + return vm.dstack.OverN(1) +} + +// opcodePick treats the top item on the data stack as an integer and duplicates +// the item on the stack that number of items back to the top. +// +// Stack transformation: [xn ... x2 x1 x0 n] -> [xn ... x2 x1 x0 xn] +// Example with n=1: [x2 x1 x0 1] -> [x2 x1 x0 x1] +// Example with n=2: [x2 x1 x0 2] -> [x2 x1 x0 x2] +func opcodePick(op *parsedOpcode, vm *Engine) error { + val, err := vm.dstack.PopInt() + if err != nil { + return err + } + + return vm.dstack.PickN(val.Int32()) +} + +// opcodeRoll treats the top item on the data stack as an integer and moves +// the item on the stack that number of items back to the top. +// +// Stack transformation: [xn ... x2 x1 x0 n] -> [... x2 x1 x0 xn] +// Example with n=1: [x2 x1 x0 1] -> [x2 x0 x1] +// Example with n=2: [x2 x1 x0 2] -> [x1 x0 x2] +func opcodeRoll(op *parsedOpcode, vm *Engine) error { + val, err := vm.dstack.PopInt() + if err != nil { + return err + } + + return vm.dstack.RollN(val.Int32()) +} + +// opcodeRot rotates the top 3 items on the data stack to the left. +// +// Stack transformation: [... x1 x2 x3] -> [... x2 x3 x1] +func opcodeRot(op *parsedOpcode, vm *Engine) error { + return vm.dstack.RotN(1) +} + +// opcodeSwap swaps the top two items on the stack. +// +// Stack transformation: [... x1 x2] -> [... x2 x1] +func opcodeSwap(op *parsedOpcode, vm *Engine) error { + return vm.dstack.SwapN(1) +} + +// opcodeTuck inserts a duplicate of the top item of the data stack before the +// second-to-top item. +// +// Stack transformation: [... x1 x2] -> [... x2 x1 x2] +func opcodeTuck(op *parsedOpcode, vm *Engine) error { + return vm.dstack.Tuck() +} + +// opcodeSize pushes the size of the top item of the data stack onto the data +// stack. +// +// Stack transformation: [... x1] -> [... x1 len(x1)] +func opcodeSize(op *parsedOpcode, vm *Engine) error { + so, err := vm.dstack.PeekByteArray(0) + if err != nil { + return err + } + + vm.dstack.PushInt(scriptNum(len(so))) + return nil +} + +// opcodeEqual removes the top 2 items of the data stack, compares them as raw +// bytes, and pushes the result, encoded as a boolean, back to the stack. +// +// Stack transformation: [... x1 x2] -> [... bool] +func opcodeEqual(op *parsedOpcode, vm *Engine) error { + a, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + b, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + + vm.dstack.PushBool(bytes.Equal(a, b)) + return nil +} + +// opcodeEqualVerify is a combination of opcodeEqual and opcodeVerify. +// Specifically, it removes the top 2 items of the data stack, compares them, +// and pushes the result, encoded as a boolean, back to the stack. Then, it +// examines the top item on the data stack as a boolean value and verifies it +// evaluates to true. An error is returned if it does not. +// +// Stack transformation: [... x1 x2] -> [... bool] -> [...] +func opcodeEqualVerify(op *parsedOpcode, vm *Engine) error { + err := opcodeEqual(op, vm) + if err == nil { + err = opcodeVerify(op, vm) + } + return err +} + +// opcode1Add treats the top item on the data stack as an integer and replaces +// it with its incremented value (plus 1). +// +// Stack transformation: [... x1 x2] -> [... x1 x2+1] +func opcode1Add(op *parsedOpcode, vm *Engine) error { + m, err := vm.dstack.PopInt() + if err != nil { + return err + } + + vm.dstack.PushInt(m + 1) + return nil +} + +// opcode1Sub treats the top item on the data stack as an integer and replaces +// it with its decremented value (minus 1). +// +// Stack transformation: [... x1 x2] -> [... x1 x2-1] +func opcode1Sub(op *parsedOpcode, vm *Engine) error { + m, err := vm.dstack.PopInt() + if err != nil { + return err + } + vm.dstack.PushInt(m - 1) + + return nil +} + +// opcodeNegate treats the top item on the data stack as an integer and replaces +// it with its negation. +// +// Stack transformation: [... x1 x2] -> [... x1 -x2] +func opcodeNegate(op *parsedOpcode, vm *Engine) error { + m, err := vm.dstack.PopInt() + if err != nil { + return err + } + + vm.dstack.PushInt(-m) + return nil +} + +// opcodeAbs treats the top item on the data stack as an integer and replaces it +// it with its absolute value. +// +// Stack transformation: [... x1 x2] -> [... x1 abs(x2)] +func opcodeAbs(op *parsedOpcode, vm *Engine) error { + m, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if m < 0 { + m = -m + } + vm.dstack.PushInt(m) + return nil +} + +// opcodeNot treats the top item on the data stack as an integer and replaces +// it with its "inverted" value (0 becomes 1, non-zero becomes 0). +// +// NOTE: While it would probably make more sense to treat the top item as a +// boolean, and push the opposite, which is really what the intention of this +// opcode is, it is extremely important that is not done because integers are +// interpreted differently than booleans and the consensus rules for this opcode +// dictate the item is interpreted as an integer. +// +// Stack transformation (x2==0): [... x1 0] -> [... x1 1] +// Stack transformation (x2!=0): [... x1 1] -> [... x1 0] +// Stack transformation (x2!=0): [... x1 17] -> [... x1 0] +func opcodeNot(op *parsedOpcode, vm *Engine) error { + m, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if m == 0 { + vm.dstack.PushInt(scriptNum(1)) + } else { + vm.dstack.PushInt(scriptNum(0)) + } + return nil +} + +// opcode0NotEqual treats the top item on the data stack as an integer and +// replaces it with either a 0 if it is zero, or a 1 if it is not zero. +// +// Stack transformation (x2==0): [... x1 0] -> [... x1 0] +// Stack transformation (x2!=0): [... x1 1] -> [... x1 1] +// Stack transformation (x2!=0): [... x1 17] -> [... x1 1] +func opcode0NotEqual(op *parsedOpcode, vm *Engine) error { + m, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if m != 0 { + m = 1 + } + vm.dstack.PushInt(m) + return nil +} + +// opcodeAdd treats the top two items on the data stack as integers and replaces +// them with their sum. +// +// Stack transformation: [... x1 x2] -> [... x1+x2] +func opcodeAdd(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() + if err != nil { + return err + } + + v1, err := vm.dstack.PopInt() + if err != nil { + return err + } + + vm.dstack.PushInt(v0 + v1) + return nil +} + +// opcodeSub treats the top two items on the data stack as integers and replaces +// them with the result of subtracting the top entry from the second-to-top +// entry. +// +// Stack transformation: [... x1 x2] -> [... x1-x2] +func opcodeSub(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() + if err != nil { + return err + } + + v1, err := vm.dstack.PopInt() + if err != nil { + return err + } + + vm.dstack.PushInt(v1 - v0) + return nil +} + +// opcodeBoolAnd treats the top two items on the data stack as integers. When +// both of them are not zero, they are replaced with a 1, otherwise a 0. +// +// Stack transformation (x1==0, x2==0): [... 0 0] -> [... 0] +// Stack transformation (x1!=0, x2==0): [... 5 0] -> [... 0] +// Stack transformation (x1==0, x2!=0): [... 0 7] -> [... 0] +// Stack transformation (x1!=0, x2!=0): [... 4 8] -> [... 1] +func opcodeBoolAnd(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() + if err != nil { + return err + } + + v1, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if v0 != 0 && v1 != 0 { + vm.dstack.PushInt(scriptNum(1)) + } else { + vm.dstack.PushInt(scriptNum(0)) + } + + return nil +} + +// opcodeBoolOr treats the top two items on the data stack as integers. When +// either of them are not zero, they are replaced with a 1, otherwise a 0. +// +// Stack transformation (x1==0, x2==0): [... 0 0] -> [... 0] +// Stack transformation (x1!=0, x2==0): [... 5 0] -> [... 1] +// Stack transformation (x1==0, x2!=0): [... 0 7] -> [... 1] +// Stack transformation (x1!=0, x2!=0): [... 4 8] -> [... 1] +func opcodeBoolOr(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() + if err != nil { + return err + } + + v1, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if v0 != 0 || v1 != 0 { + vm.dstack.PushInt(scriptNum(1)) + } else { + vm.dstack.PushInt(scriptNum(0)) + } + + return nil +} + +// opcodeNumEqual treats the top two items on the data stack as integers. When +// they are equal, they are replaced with a 1, otherwise a 0. +// +// Stack transformation (x1==x2): [... 5 5] -> [... 1] +// Stack transformation (x1!=x2): [... 5 7] -> [... 0] +func opcodeNumEqual(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() + if err != nil { + return err + } + + v1, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if v0 == v1 { + vm.dstack.PushInt(scriptNum(1)) + } else { + vm.dstack.PushInt(scriptNum(0)) + } + + return nil +} + +// opcodeNumEqualVerify is a combination of opcodeNumEqual and opcodeVerify. +// +// Specifically, treats the top two items on the data stack as integers. When +// they are equal, they are replaced with a 1, otherwise a 0. Then, it examines +// the top item on the data stack as a boolean value and verifies it evaluates +// to true. An error is returned if it does not. +// +// Stack transformation: [... x1 x2] -> [... bool] -> [...] +func opcodeNumEqualVerify(op *parsedOpcode, vm *Engine) error { + err := opcodeNumEqual(op, vm) + if err == nil { + err = opcodeVerify(op, vm) + } + return err +} + +// opcodeNumNotEqual treats the top two items on the data stack as integers. +// When they are NOT equal, they are replaced with a 1, otherwise a 0. +// +// Stack transformation (x1==x2): [... 5 5] -> [... 0] +// Stack transformation (x1!=x2): [... 5 7] -> [... 1] +func opcodeNumNotEqual(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() + if err != nil { + return err + } + + v1, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if v0 != v1 { + vm.dstack.PushInt(scriptNum(1)) + } else { + vm.dstack.PushInt(scriptNum(0)) + } + + return nil +} + +// opcodeLessThan treats the top two items on the data stack as integers. When +// the second-to-top item is less than the top item, they are replaced with a 1, +// otherwise a 0. +// +// Stack transformation: [... x1 x2] -> [... bool] +func opcodeLessThan(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() + if err != nil { + return err + } + + v1, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if v1 < v0 { + vm.dstack.PushInt(scriptNum(1)) + } else { + vm.dstack.PushInt(scriptNum(0)) + } + + return nil +} + +// opcodeGreaterThan treats the top two items on the data stack as integers. +// When the second-to-top item is greater than the top item, they are replaced +// with a 1, otherwise a 0. +// +// Stack transformation: [... x1 x2] -> [... bool] +func opcodeGreaterThan(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() + if err != nil { + return err + } + + v1, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if v1 > v0 { + vm.dstack.PushInt(scriptNum(1)) + } else { + vm.dstack.PushInt(scriptNum(0)) + } + return nil +} + +// opcodeLessThanOrEqual treats the top two items on the data stack as integers. +// When the second-to-top item is less than or equal to the top item, they are +// replaced with a 1, otherwise a 0. +// +// Stack transformation: [... x1 x2] -> [... bool] +func opcodeLessThanOrEqual(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() + if err != nil { + return err + } + + v1, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if v1 <= v0 { + vm.dstack.PushInt(scriptNum(1)) + } else { + vm.dstack.PushInt(scriptNum(0)) + } + return nil +} + +// opcodeGreaterThanOrEqual treats the top two items on the data stack as +// integers. When the second-to-top item is greater than or equal to the top +// item, they are replaced with a 1, otherwise a 0. +// +// Stack transformation: [... x1 x2] -> [... bool] +func opcodeGreaterThanOrEqual(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() + if err != nil { + return err + } + + v1, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if v1 >= v0 { + vm.dstack.PushInt(scriptNum(1)) + } else { + vm.dstack.PushInt(scriptNum(0)) + } + + return nil +} + +// opcodeMin treats the top two items on the data stack as integers and replaces +// them with the minimum of the two. +// +// Stack transformation: [... x1 x2] -> [... min(x1, x2)] +func opcodeMin(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() + if err != nil { + return err + } + + v1, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if v1 < v0 { + vm.dstack.PushInt(v1) + } else { + vm.dstack.PushInt(v0) + } + + return nil +} + +// opcodeMax treats the top two items on the data stack as integers and replaces +// them with the maximum of the two. +// +// Stack transformation: [... x1 x2] -> [... max(x1, x2)] +func opcodeMax(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() + if err != nil { + return err + } + + v1, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if v1 > v0 { + vm.dstack.PushInt(v1) + } else { + vm.dstack.PushInt(v0) + } + + return nil +} + +// opcodeWithin treats the top 3 items on the data stack as integers. When the +// value to test is within the specified range (left inclusive), they are +// replaced with a 1, otherwise a 0. +// +// The top item is the max value, the second-top-item is the minimum value, and +// the third-to-top item is the value to test. +// +// Stack transformation: [... x1 min max] -> [... bool] +func opcodeWithin(op *parsedOpcode, vm *Engine) error { + maxVal, err := vm.dstack.PopInt() + if err != nil { + return err + } + + minVal, err := vm.dstack.PopInt() + if err != nil { + return err + } + + x, err := vm.dstack.PopInt() + if err != nil { + return err + } + + if x >= minVal && x < maxVal { + vm.dstack.PushInt(scriptNum(1)) + } else { + vm.dstack.PushInt(scriptNum(0)) + } + return nil +} + +// calcHash calculates the hash of hasher over buf. +func calcHash(buf []byte, hasher hash.Hash) []byte { + hasher.Write(buf) + return hasher.Sum(nil) +} + +// opcodeRipemd160 treats the top item of the data stack as raw bytes and +// replaces it with ripemd160(data). +// +// Stack transformation: [... x1] -> [... ripemd160(x1)] +func opcodeRipemd160(op *parsedOpcode, vm *Engine) error { + buf, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + + vm.dstack.PushByteArray(calcHash(buf, ripemd160.New())) + return nil +} + +// opcodeSha1 treats the top item of the data stack as raw bytes and replaces it +// with sha1(data). +// +// Stack transformation: [... x1] -> [... sha1(x1)] +func opcodeSha1(op *parsedOpcode, vm *Engine) error { + buf, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + + hash := sha1.Sum(buf) + vm.dstack.PushByteArray(hash[:]) + return nil +} + +// opcodeSha256 treats the top item of the data stack as raw bytes and replaces +// it with sha256(data). +// +// Stack transformation: [... x1] -> [... sha256(x1)] +func opcodeSha256(op *parsedOpcode, vm *Engine) error { + buf, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + + hash := fastsha256.Sum256(buf) + vm.dstack.PushByteArray(hash[:]) + return nil +} + +// opcodeHash160 treats the top item of the data stack as raw bytes and replaces +// it with ripemd160(sha256(data)). +// +// Stack transformation: [... x1] -> [... ripemd160(sha256(x1))] +func opcodeHash160(op *parsedOpcode, vm *Engine) error { + buf, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + + hash := fastsha256.Sum256(buf) + vm.dstack.PushByteArray(calcHash(hash[:], ripemd160.New())) + return nil +} + +// opcodeHash256 treats the top item of the data stack as raw bytes and replaces +// it with sha256(sha256(data)). +// +// Stack transformation: [... x1] -> [... sha256(sha256(x1))] +func opcodeHash256(op *parsedOpcode, vm *Engine) error { + buf, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + + vm.dstack.PushByteArray(wire.DoubleSha256(buf)) + return nil +} + +// opcodeCodeSeparator stores the current script offset as the most recently +// seen OP_CODESEPARATOR which is used during signature checking. +// +// This opcode does not change the contents of the data stack. +func opcodeCodeSeparator(op *parsedOpcode, vm *Engine) error { + vm.lastCodeSep = vm.scriptOff + return nil +} + +// opcodeCheckSig treats the top 2 items on the stack as a public key and a +// signature and replaces them with a bool which indicates if the signature was +// successfully verified. +// +// The process of verifying a signature requires calculating a signature hash in +// the same way the transaction signer did. It involves hashing portions of the +// transaction based on the hash type byte (which is the final byte of the +// signature) and the portion of the script starting from the most recent +// OP_CODESEPARATOR (or the beginning of the script if there are none) to the +// end of the script (with any other OP_CODESEPARATORs removed). Once this +// "script hash" is calculated, the signature is checked using standard +// cryptographic methods against the provided public key. +// +// Stack transformation: [... signature pubkey] -> [... bool] +func opcodeCheckSig(op *parsedOpcode, vm *Engine) error { + pkBytes, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + + fullSigBytes, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + + // The signature actually needs needs to be longer than this, but at + // least 1 byte is needed for the hash type below. The full length is + // checked depending on the script flags and upon parsing the signature. + if len(fullSigBytes) < 1 { + vm.dstack.PushBool(false) + return nil + } + + // Trim off hashtype from the signature string and check if the + // signature and pubkey conform to the strict encoding requirements + // depending on the flags. + // + // NOTE: When the strict encoding flags are set, any errors in the + // signature or public encoding here result in an immediate script error + // (and thus no result bool is pushed to the data stack). This differs + // from the logic below where any errors in parsing the signature is + // treated as the signature failure resulting in false being pushed to + // the data stack. This is required because the more general script + // validation consensus rules do not have the new strict encoding + // requirements enabled by the flags. + hashType := SigHashType(fullSigBytes[len(fullSigBytes)-1]) + sigBytes := fullSigBytes[:len(fullSigBytes)-1] + if err := vm.checkHashTypeEncoding(hashType); err != nil { + return err + } + if err := vm.checkSignatureEncoding(sigBytes); err != nil { + return err + } + if err := vm.checkPubKeyEncoding(pkBytes); err != nil { + return err + } + + // Get script starting from the most recent OP_CODESEPARATOR. + subScript := vm.subScript() + + // Remove the signature since there is no way for a signature to sign + // itself. + subScript = removeOpcodeByData(subScript, fullSigBytes) + + // Generate the signature hash based on the signature hash type. + hash := calcSignatureHash(subScript, hashType, &vm.tx, vm.txIdx) + + pubKey, err := btcec.ParsePubKey(pkBytes, btcec.S256()) + if err != nil { + vm.dstack.PushBool(false) + return nil + } + + var signature *btcec.Signature + if vm.hasFlag(ScriptVerifyStrictEncoding) || + vm.hasFlag(ScriptVerifyDERSignatures) { + + signature, err = btcec.ParseDERSignature(sigBytes, btcec.S256()) + } else { + signature, err = btcec.ParseSignature(sigBytes, btcec.S256()) + } + if err != nil { + vm.dstack.PushBool(false) + return nil + } + + var valid bool + if vm.sigCache != nil { + var sigHash wire.ShaHash + copy(sigHash[:], hash) + + valid = vm.sigCache.Exists(sigHash, signature, pubKey) + if !valid && signature.Verify(hash, pubKey) { + vm.sigCache.Add(sigHash, signature, pubKey) + valid = true + } + } else { + valid = signature.Verify(hash, pubKey) + } + + vm.dstack.PushBool(valid) + return nil +} + +// opcodeCheckSigVerify is a combination of opcodeCheckSig and opcodeVerify. +// The opcodeCheckSig function is invoked followed by opcodeVerify. See the +// documentation for each of those opcodes for more details. +// +// Stack transformation: signature pubkey] -> [... bool] -> [...] +func opcodeCheckSigVerify(op *parsedOpcode, vm *Engine) error { + err := opcodeCheckSig(op, vm) + if err == nil { + err = opcodeVerify(op, vm) + } + return err +} + +// parsedSigInfo houses a raw signature along with its parsed form and a flag +// for whether or not it has already been parsed. It is used to prevent parsing +// the same signature multiple times when verifying a multisig. +type parsedSigInfo struct { + signature []byte + parsedSignature *btcec.Signature + parsed bool +} + +// opcodeCheckMultiSig treats the top item on the stack as an integer number of +// public keys, followed by that many entries as raw data representing the public +// keys, followed by the integer number of signatures, followed by that many +// entries as raw data representing the signatures. +// +// Due to a bug in the original Satoshi client implementation, an additional +// dummy argument is also required by the consensus rules, although it is not +// used. The dummy value SHOULD be an OP_0, although that is not required by +// the consensus rules. When the ScriptStrictMultiSig flag is set, it must be +// OP_0. +// +// All of the aforementioned stack items are replaced with a bool which +// indicates if the requisite number of signatures were successfully verified. +// +// See the opcodeCheckSigVerify documentation for more details about the process +// for verifying each signature. +// +// Stack transformation: +// [... dummy [sig ...] numsigs [pubkey ...] numpubkeys] -> [... bool] +func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error { + numKeys, err := vm.dstack.PopInt() + if err != nil { + return err + } + + numPubKeys := int(numKeys.Int32()) + if numPubKeys < 0 || numPubKeys > MaxPubKeysPerMultiSig { + return ErrStackTooManyPubKeys + } + vm.numOps += numPubKeys + if vm.numOps > MaxOpsPerScript { + return ErrStackTooManyOperations + } + + pubKeys := make([][]byte, 0, numPubKeys) + for i := 0; i < numPubKeys; i++ { + pubKey, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + pubKeys = append(pubKeys, pubKey) + } + + numSigs, err := vm.dstack.PopInt() + if err != nil { + return err + } + numSignatures := int(numSigs.Int32()) + if numSignatures < 0 { + return fmt.Errorf("number of signatures '%d' is less than 0", + numSignatures) + } + if numSignatures > numPubKeys { + return fmt.Errorf("more signatures than pubkeys: %d > %d", + numSignatures, numPubKeys) + } + + signatures := make([]*parsedSigInfo, 0, numSignatures) + for i := 0; i < numSignatures; i++ { + signature, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + sigInfo := &parsedSigInfo{signature: signature} + signatures = append(signatures, sigInfo) + } + + // A bug in the original Satoshi client implementation means one more + // stack value than should be used must be popped. Unfortunately, this + // buggy behavior is now part of the consensus and a hard fork would be + // required to fix it. + dummy, err := vm.dstack.PopByteArray() + if err != nil { + return err + } + + // Since the dummy argument is otherwise not checked, it could be any + // value which unfortunately provides a source of malleability. Thus, + // there is a script flag to force an error when the value is NOT 0. + if vm.hasFlag(ScriptStrictMultiSig) && len(dummy) != 0 { + return fmt.Errorf("multisig dummy argument is not zero length: %d", + len(dummy)) + } + + // Get script starting from the most recent OP_CODESEPARATOR. + script := vm.subScript() + + // Remove any of the signatures since there is no way for a signature to + // sign itself. + for _, sigInfo := range signatures { + script = removeOpcodeByData(script, sigInfo.signature) + } + + success := true + numPubKeys++ + pubKeyIdx := -1 + signatureIdx := 0 + for numSignatures > 0 { + // When there are more signatures than public keys remaining, + // there is no way to succeed since too many signatures are + // invalid, so exit early. + pubKeyIdx++ + numPubKeys-- + if numSignatures > numPubKeys { + success = false + break + } + + sigInfo := signatures[signatureIdx] + pubKey := pubKeys[pubKeyIdx] + + // The order of the signature and public key evaluation is + // important here since it can be distinguished by an + // OP_CHECKMULTISIG NOT when the strict encoding flag is set. + + rawSig := sigInfo.signature + if len(rawSig) == 0 { + // Skip to the next pubkey if signature is empty. + continue + } + + // Split the signature into hash type and signature components. + hashType := SigHashType(rawSig[len(rawSig)-1]) + signature := rawSig[:len(rawSig)-1] + + // Only parse and check the signature encoding once. + var parsedSig *btcec.Signature + if !sigInfo.parsed { + if err := vm.checkHashTypeEncoding(hashType); err != nil { + return err + } + if err := vm.checkSignatureEncoding(signature); err != nil { + return err + } + + // Parse the signature. + var err error + if vm.hasFlag(ScriptVerifyStrictEncoding) || + vm.hasFlag(ScriptVerifyDERSignatures) { + + parsedSig, err = btcec.ParseDERSignature(signature, + btcec.S256()) + } else { + parsedSig, err = btcec.ParseSignature(signature, + btcec.S256()) + } + sigInfo.parsed = true + if err != nil { + continue + } + sigInfo.parsedSignature = parsedSig + } else { + // Skip to the next pubkey if the signature is invalid. + if sigInfo.parsedSignature == nil { + continue + } + + // Use the already parsed signature. + parsedSig = sigInfo.parsedSignature + } + + if err := vm.checkPubKeyEncoding(pubKey); err != nil { + return err + } + + // Parse the pubkey. + parsedPubKey, err := btcec.ParsePubKey(pubKey, btcec.S256()) + if err != nil { + continue + } + + // Generate the signature hash based on the signature hash type. + hash := calcSignatureHash(script, hashType, &vm.tx, vm.txIdx) + + var valid bool + if vm.sigCache != nil { + var sigHash wire.ShaHash + copy(sigHash[:], hash) + + valid = vm.sigCache.Exists(sigHash, parsedSig, parsedPubKey) + if !valid && parsedSig.Verify(hash, parsedPubKey) { + vm.sigCache.Add(sigHash, parsedSig, parsedPubKey) + valid = true + } + } else { + valid = parsedSig.Verify(hash, parsedPubKey) + } + + if valid { + // PubKey verified, move on to the next signature. + signatureIdx++ + numSignatures-- + } + } + + vm.dstack.PushBool(success) + return nil +} + +// opcodeCheckMultiSigVerify is a combination of opcodeCheckMultiSig and +// opcodeVerify. The opcodeCheckMultiSig is invoked followed by opcodeVerify. +// See the documentation for each of those opcodes for more details. +// +// Stack transformation: +// [... dummy [sig ...] numsigs [pubkey ...] numpubkeys] -> [... bool] -> [...] +func opcodeCheckMultiSigVerify(op *parsedOpcode, vm *Engine) error { + err := opcodeCheckMultiSig(op, vm) + if err == nil { + err = opcodeVerify(op, vm) + } + return err +} + +// OpcodeByName is a map that can be used to lookup an opcode by its +// human-readable name (OP_CHECKMULTISIG, OP_CHECKSIG, etc). +var OpcodeByName = make(map[string]byte) + +func init() { + // Initialize the opcode name to value map using the contents of the + // opcode array. Also add entries for "OP_FALSE", "OP_TRUE", and + // "OP_NOP2" since they are aliases for "OP_0", "OP_1", + // and "OP_CHECKLOCKTIMEVERIFY" respectively. + for _, op := range opcodeArray { + OpcodeByName[op.name] = op.value + } + OpcodeByName["OP_FALSE"] = OP_FALSE + OpcodeByName["OP_TRUE"] = OP_TRUE + OpcodeByName["OP_NOP2"] = OP_CHECKLOCKTIMEVERIFY +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/opcode_test.go b/vendor/github.com/btcsuite/btcd/txscript/opcode_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bdd19e2f6d41a81a713b2d914d8b56e7379f92cc --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/opcode_test.go @@ -0,0 +1,196 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "testing" +) + +// TestOpcodeDisabled tests the opcodeDisabled function manually because all +// disabled opcodes result in a script execution failure when executed normally, +// so the function is not called under normal circumstances. +func TestOpcodeDisabled(t *testing.T) { + t.Parallel() + + tests := []byte{OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT, OP_INVERT, + OP_AND, OP_OR, OP_2MUL, OP_2DIV, OP_MUL, OP_DIV, OP_MOD, + OP_LSHIFT, OP_RSHIFT, + } + for _, opcodeVal := range tests { + pop := parsedOpcode{opcode: &opcodeArray[opcodeVal], data: nil} + if err := opcodeDisabled(&pop, nil); err != ErrStackOpDisabled { + t.Errorf("opcodeDisabled: unexpected error - got %v, "+ + "want %v", err, ErrStackOpDisabled) + return + } + } +} + +// TestOpcodeDisasm tests the print function for all opcodes in both the oneline +// and full modes to ensure it provides the expected disassembly. +func TestOpcodeDisasm(t *testing.T) { + t.Parallel() + + // First, test the oneline disassembly. + + // The expected strings for the data push opcodes are replaced in the + // test loops below since they involve repeating bytes. Also, the + // OP_NOP# and OP_UNKNOWN# are replaced below too, since it's easier + // than manually listing them here. + oneBytes := []byte{0x01} + oneStr := "01" + expectedStrings := [256]string{0x00: "0", 0x4f: "-1", + 0x50: "OP_RESERVED", 0x61: "OP_NOP", 0x62: "OP_VER", + 0x63: "OP_IF", 0x64: "OP_NOTIF", 0x65: "OP_VERIF", + 0x66: "OP_VERNOTIF", 0x67: "OP_ELSE", 0x68: "OP_ENDIF", + 0x69: "OP_VERIFY", 0x6a: "OP_RETURN", 0x6b: "OP_TOALTSTACK", + 0x6c: "OP_FROMALTSTACK", 0x6d: "OP_2DROP", 0x6e: "OP_2DUP", + 0x6f: "OP_3DUP", 0x70: "OP_2OVER", 0x71: "OP_2ROT", + 0x72: "OP_2SWAP", 0x73: "OP_IFDUP", 0x74: "OP_DEPTH", + 0x75: "OP_DROP", 0x76: "OP_DUP", 0x77: "OP_NIP", + 0x78: "OP_OVER", 0x79: "OP_PICK", 0x7a: "OP_ROLL", + 0x7b: "OP_ROT", 0x7c: "OP_SWAP", 0x7d: "OP_TUCK", + 0x7e: "OP_CAT", 0x7f: "OP_SUBSTR", 0x80: "OP_LEFT", + 0x81: "OP_RIGHT", 0x82: "OP_SIZE", 0x83: "OP_INVERT", + 0x84: "OP_AND", 0x85: "OP_OR", 0x86: "OP_XOR", + 0x87: "OP_EQUAL", 0x88: "OP_EQUALVERIFY", 0x89: "OP_RESERVED1", + 0x8a: "OP_RESERVED2", 0x8b: "OP_1ADD", 0x8c: "OP_1SUB", + 0x8d: "OP_2MUL", 0x8e: "OP_2DIV", 0x8f: "OP_NEGATE", + 0x90: "OP_ABS", 0x91: "OP_NOT", 0x92: "OP_0NOTEQUAL", + 0x93: "OP_ADD", 0x94: "OP_SUB", 0x95: "OP_MUL", 0x96: "OP_DIV", + 0x97: "OP_MOD", 0x98: "OP_LSHIFT", 0x99: "OP_RSHIFT", + 0x9a: "OP_BOOLAND", 0x9b: "OP_BOOLOR", 0x9c: "OP_NUMEQUAL", + 0x9d: "OP_NUMEQUALVERIFY", 0x9e: "OP_NUMNOTEQUAL", + 0x9f: "OP_LESSTHAN", 0xa0: "OP_GREATERTHAN", + 0xa1: "OP_LESSTHANOREQUAL", 0xa2: "OP_GREATERTHANOREQUAL", + 0xa3: "OP_MIN", 0xa4: "OP_MAX", 0xa5: "OP_WITHIN", + 0xa6: "OP_RIPEMD160", 0xa7: "OP_SHA1", 0xa8: "OP_SHA256", + 0xa9: "OP_HASH160", 0xaa: "OP_HASH256", 0xab: "OP_CODESEPARATOR", + 0xac: "OP_CHECKSIG", 0xad: "OP_CHECKSIGVERIFY", + 0xae: "OP_CHECKMULTISIG", 0xaf: "OP_CHECKMULTISIGVERIFY", + 0xf9: "OP_SMALLDATA", 0xfa: "OP_SMALLINTEGER", + 0xfb: "OP_PUBKEYS", 0xfd: "OP_PUBKEYHASH", 0xfe: "OP_PUBKEY", + 0xff: "OP_INVALIDOPCODE", + } + for opcodeVal, expectedStr := range expectedStrings { + var data []byte + switch { + // OP_DATA_1 through OP_DATA_65 display the pushed data. + case opcodeVal >= 0x01 && opcodeVal < 0x4c: + data = bytes.Repeat(oneBytes, opcodeVal) + expectedStr = strings.Repeat(oneStr, opcodeVal) + + // OP_PUSHDATA1. + case opcodeVal == 0x4c: + data = bytes.Repeat(oneBytes, 1) + expectedStr = strings.Repeat(oneStr, 1) + + // OP_PUSHDATA2. + case opcodeVal == 0x4d: + data = bytes.Repeat(oneBytes, 2) + expectedStr = strings.Repeat(oneStr, 2) + + // OP_PUSHDATA4. + case opcodeVal == 0x4e: + data = bytes.Repeat(oneBytes, 3) + expectedStr = strings.Repeat(oneStr, 3) + + // OP_1 through OP_16 display the numbers themselves. + case opcodeVal >= 0x51 && opcodeVal <= 0x60: + val := byte(opcodeVal - (0x51 - 1)) + data = []byte{val} + expectedStr = strconv.Itoa(int(val)) + + // OP_NOP1 through OP_NOP10. + case opcodeVal >= 0xb0 && opcodeVal <= 0xb9: + // OP_NOP2 is an alias of OP_CHECKLOCKTIMEVERIFY + if opcodeVal == 0xb1 { + expectedStr = "OP_CHECKLOCKTIMEVERIFY" + } else { + val := byte(opcodeVal - (0xb0 - 1)) + expectedStr = "OP_NOP" + strconv.Itoa(int(val)) + } + + // OP_UNKNOWN#. + case opcodeVal >= 0xba && opcodeVal <= 0xf8 || opcodeVal == 0xfc: + expectedStr = "OP_UNKNOWN" + strconv.Itoa(int(opcodeVal)) + } + + pop := parsedOpcode{opcode: &opcodeArray[opcodeVal], data: data} + gotStr := pop.print(true) + if gotStr != expectedStr { + t.Errorf("pop.print (opcode %x): Unexpected disasm "+ + "string - got %v, want %v", opcodeVal, gotStr, + expectedStr) + continue + } + } + + // Now, replace the relevant fields and test the full disassembly. + expectedStrings[0x00] = "OP_0" + expectedStrings[0x4f] = "OP_1NEGATE" + for opcodeVal, expectedStr := range expectedStrings { + var data []byte + switch { + // OP_DATA_1 through OP_DATA_65 display the opcode followed by + // the pushed data. + case opcodeVal >= 0x01 && opcodeVal < 0x4c: + data = bytes.Repeat(oneBytes, opcodeVal) + expectedStr = fmt.Sprintf("OP_DATA_%d 0x%s", opcodeVal, + strings.Repeat(oneStr, opcodeVal)) + + // OP_PUSHDATA1. + case opcodeVal == 0x4c: + data = bytes.Repeat(oneBytes, 1) + expectedStr = fmt.Sprintf("OP_PUSHDATA1 0x%02x 0x%s", + len(data), strings.Repeat(oneStr, 1)) + + // OP_PUSHDATA2. + case opcodeVal == 0x4d: + data = bytes.Repeat(oneBytes, 2) + expectedStr = fmt.Sprintf("OP_PUSHDATA2 0x%04x 0x%s", + len(data), strings.Repeat(oneStr, 2)) + + // OP_PUSHDATA4. + case opcodeVal == 0x4e: + data = bytes.Repeat(oneBytes, 3) + expectedStr = fmt.Sprintf("OP_PUSHDATA4 0x%08x 0x%s", + len(data), strings.Repeat(oneStr, 3)) + + // OP_1 through OP_16. + case opcodeVal >= 0x51 && opcodeVal <= 0x60: + val := byte(opcodeVal - (0x51 - 1)) + data = []byte{val} + expectedStr = "OP_" + strconv.Itoa(int(val)) + + // OP_NOP1 through OP_NOP10. + case opcodeVal >= 0xb0 && opcodeVal <= 0xb9: + // OP_NOP2 is an alias of OP_CHECKLOCKTIMEVERIFY + if opcodeVal == 0xb1 { + expectedStr = "OP_CHECKLOCKTIMEVERIFY" + } else { + val := byte(opcodeVal - (0xb0 - 1)) + expectedStr = "OP_NOP" + strconv.Itoa(int(val)) + } + + // OP_UNKNOWN#. + case opcodeVal >= 0xba && opcodeVal <= 0xf8 || opcodeVal == 0xfc: + expectedStr = "OP_UNKNOWN" + strconv.Itoa(int(opcodeVal)) + } + + pop := parsedOpcode{opcode: &opcodeArray[opcodeVal], data: data} + gotStr := pop.print(false) + if gotStr != expectedStr { + t.Errorf("pop.print (opcode %x): Unexpected disasm "+ + "string - got %v, want %v", opcodeVal, gotStr, + expectedStr) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/reference_test.go b/vendor/github.com/btcsuite/btcd/txscript/reference_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9abab7d0f706cb54420127039b7ecc39cf350f85 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/reference_test.go @@ -0,0 +1,665 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript_test + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "strconv" + "strings" + "testing" + + . "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// testName returns a descriptive test name for the given reference test data. +func testName(test []string) (string, error) { + var name string + + if len(test) < 3 || len(test) > 4 { + return name, fmt.Errorf("invalid test length %d", len(test)) + } + + if len(test) == 4 { + name = fmt.Sprintf("test (%s)", test[3]) + } else { + name = fmt.Sprintf("test ([%s, %s, %s])", test[0], test[1], + test[2]) + } + return name, nil +} + +// parse hex string into a []byte. +func parseHex(tok string) ([]byte, error) { + if !strings.HasPrefix(tok, "0x") { + return nil, errors.New("not a hex number") + } + return hex.DecodeString(tok[2:]) +} + +// shortFormOps holds a map of opcode names to values for use in short form +// parsing. It is declared here so it only needs to be created once. +var shortFormOps map[string]byte + +// parseShortForm parses a string as as used in the Bitcoin Core reference tests +// into the script it came from. +// +// The format used for these tests is pretty simple if ad-hoc: +// - Opcodes other than the push opcodes and unknown are present as +// either OP_NAME or just NAME +// - Plain numbers are made into push operations +// - Numbers beginning with 0x are inserted into the []byte as-is (so +// 0x14 is OP_DATA_20) +// - Single quoted strings are pushed as data +// - Anything else is an error +func parseShortForm(script string) ([]byte, error) { + // Only create the short form opcode map once. + if shortFormOps == nil { + ops := make(map[string]byte) + for opcodeName, opcodeValue := range OpcodeByName { + if strings.Contains(opcodeName, "OP_UNKNOWN") { + continue + } + ops[opcodeName] = opcodeValue + + // The opcodes named OP_# can't have the OP_ prefix + // stripped or they would conflict with the plain + // numbers. Also, since OP_FALSE and OP_TRUE are + // aliases for the OP_0, and OP_1, respectively, they + // have the same value, so detect those by name and + // allow them. + if (opcodeName == "OP_FALSE" || opcodeName == "OP_TRUE") || + (opcodeValue != OP_0 && (opcodeValue < OP_1 || + opcodeValue > OP_16)) { + + ops[strings.TrimPrefix(opcodeName, "OP_")] = opcodeValue + } + } + shortFormOps = ops + } + + // Split only does one separator so convert all \n and tab into space. + script = strings.Replace(script, "\n", " ", -1) + script = strings.Replace(script, "\t", " ", -1) + tokens := strings.Split(script, " ") + builder := NewScriptBuilder() + + for _, tok := range tokens { + if len(tok) == 0 { + continue + } + // if parses as a plain number + if num, err := strconv.ParseInt(tok, 10, 64); err == nil { + builder.AddInt64(num) + continue + } else if bts, err := parseHex(tok); err == nil { + builder.TstConcatRawScript(bts) + } else if len(tok) >= 2 && + tok[0] == '\'' && tok[len(tok)-1] == '\'' { + builder.AddFullData([]byte(tok[1 : len(tok)-1])) + } else if opcode, ok := shortFormOps[tok]; ok { + builder.AddOp(opcode) + } else { + return nil, fmt.Errorf("bad token \"%s\"", tok) + } + + } + return builder.Script() +} + +// parseScriptFlags parses the provided flags string from the format used in the +// reference tests into ScriptFlags suitable for use in the script engine. +func parseScriptFlags(flagStr string) (ScriptFlags, error) { + var flags ScriptFlags + + sFlags := strings.Split(flagStr, ",") + for _, flag := range sFlags { + switch flag { + case "": + // Nothing. + case "CHECKLOCKTIMEVERIFY": + flags |= ScriptVerifyCheckLockTimeVerify + case "CLEANSTACK": + flags |= ScriptVerifyCleanStack + case "DERSIG": + flags |= ScriptVerifyDERSignatures + case "DISCOURAGE_UPGRADABLE_NOPS": + flags |= ScriptDiscourageUpgradableNops + case "LOW_S": + flags |= ScriptVerifyLowS + case "MINIMALDATA": + flags |= ScriptVerifyMinimalData + case "NONE": + // Nothing. + case "NULLDUMMY": + flags |= ScriptStrictMultiSig + case "P2SH": + flags |= ScriptBip16 + case "SIGPUSHONLY": + flags |= ScriptVerifySigPushOnly + case "STRICTENC": + flags |= ScriptVerifyStrictEncoding + default: + return flags, fmt.Errorf("invalid flag: %s", flag) + } + } + return flags, nil +} + +// createSpendTx generates a basic spending transaction given the passed +// signature and public key scripts. +func createSpendingTx(sigScript, pkScript []byte) *wire.MsgTx { + coinbaseTx := wire.NewMsgTx() + + outPoint := wire.NewOutPoint(&wire.ShaHash{}, ^uint32(0)) + txIn := wire.NewTxIn(outPoint, []byte{OP_0, OP_0}) + txOut := wire.NewTxOut(0, pkScript) + coinbaseTx.AddTxIn(txIn) + coinbaseTx.AddTxOut(txOut) + + spendingTx := wire.NewMsgTx() + coinbaseTxSha := coinbaseTx.TxSha() + outPoint = wire.NewOutPoint(&coinbaseTxSha, 0) + txIn = wire.NewTxIn(outPoint, sigScript) + txOut = wire.NewTxOut(0, nil) + + spendingTx.AddTxIn(txIn) + spendingTx.AddTxOut(txOut) + + return spendingTx +} + +// TestScriptInvalidTests ensures all of the tests in script_invalid.json fail +// as expected. +func TestScriptInvalidTests(t *testing.T) { + file, err := ioutil.ReadFile("data/script_invalid.json") + if err != nil { + t.Errorf("TestBitcoindInvalidTests: %v\n", err) + return + } + + var tests [][]string + err = json.Unmarshal(file, &tests) + if err != nil { + t.Errorf("TestBitcoindInvalidTests couldn't Unmarshal: %v", + err) + return + } + sigCache := NewSigCache(10) + + sigCacheToggle := []bool{true, false} + for _, useSigCache := range sigCacheToggle { + for i, test := range tests { + // Skip comments + if len(test) == 1 { + continue + } + name, err := testName(test) + if err != nil { + t.Errorf("TestBitcoindInvalidTests: invalid test #%d", + i) + continue + } + scriptSig, err := parseShortForm(test[0]) + if err != nil { + t.Errorf("%s: can't parse scriptSig; %v", name, err) + continue + } + scriptPubKey, err := parseShortForm(test[1]) + if err != nil { + t.Errorf("%s: can't parse scriptPubkey; %v", name, err) + continue + } + flags, err := parseScriptFlags(test[2]) + if err != nil { + t.Errorf("%s: %v", name, err) + continue + } + tx := createSpendingTx(scriptSig, scriptPubKey) + + var vm *Engine + if useSigCache { + vm, err = NewEngine(scriptPubKey, tx, 0, flags, sigCache) + } else { + vm, err = NewEngine(scriptPubKey, tx, 0, flags, nil) + } + + if err == nil { + if err := vm.Execute(); err == nil { + t.Errorf("%s test succeeded when it "+ + "should have failed\n", name) + } + continue + } + } + } +} + +// TestScriptValidTests ensures all of the tests in script_valid.json pass as +// expected. +func TestScriptValidTests(t *testing.T) { + file, err := ioutil.ReadFile("data/script_valid.json") + if err != nil { + t.Errorf("TestBitcoinValidTests: %v\n", err) + return + } + + var tests [][]string + err = json.Unmarshal(file, &tests) + if err != nil { + t.Errorf("TestBitcoindValidTests couldn't Unmarshal: %v", + err) + return + } + + sigCache := NewSigCache(10) + + sigCacheToggle := []bool{true, false} + for _, useSigCache := range sigCacheToggle { + for i, test := range tests { + // Skip comments + if len(test) == 1 { + continue + } + name, err := testName(test) + if err != nil { + t.Errorf("TestBitcoindValidTests: invalid test #%d", + i) + continue + } + scriptSig, err := parseShortForm(test[0]) + if err != nil { + t.Errorf("%s: can't parse scriptSig; %v", name, err) + continue + } + scriptPubKey, err := parseShortForm(test[1]) + if err != nil { + t.Errorf("%s: can't parse scriptPubkey; %v", name, err) + continue + } + flags, err := parseScriptFlags(test[2]) + if err != nil { + t.Errorf("%s: %v", name, err) + continue + } + tx := createSpendingTx(scriptSig, scriptPubKey) + + var vm *Engine + if useSigCache { + vm, err = NewEngine(scriptPubKey, tx, 0, flags, sigCache) + } else { + vm, err = NewEngine(scriptPubKey, tx, 0, flags, nil) + } + + if err != nil { + t.Errorf("%s failed to create script: %v", name, err) + continue + } + err = vm.Execute() + if err != nil { + t.Errorf("%s failed to execute: %v", name, err) + continue + } + } + } +} + +// testVecF64ToUint32 properly handles conversion of float64s read from the JSON +// test data to unsigned 32-bit integers. This is necessary because some of the +// test data uses -1 as a shortcut to mean max uint32 and direct conversion of a +// negative float to an unsigned int is implementation dependent and therefore +// doesn't result in the expected value on all platforms. This function woks +// around that limitation by converting to a 32-bit signed integer first and +// then to a 32-bit unsigned integer which results in the expected behavior on +// all platforms. +func testVecF64ToUint32(f float64) uint32 { + return uint32(int32(f)) +} + +// TestTxInvalidTests ensures all of the tests in tx_invalid.json fail as +// expected. +func TestTxInvalidTests(t *testing.T) { + file, err := ioutil.ReadFile("data/tx_invalid.json") + if err != nil { + t.Errorf("TestTxInvalidTests: %v\n", err) + return + } + + var tests [][]interface{} + err = json.Unmarshal(file, &tests) + if err != nil { + t.Errorf("TestTxInvalidTests couldn't Unmarshal: %v\n", err) + return + } + + // form is either: + // ["this is a comment "] + // or: + // [[[previous hash, previous index, previous scriptPubKey]...,] + // serializedTransaction, verifyFlags] +testloop: + for i, test := range tests { + inputs, ok := test[0].([]interface{}) + if !ok { + continue + } + + if len(test) != 3 { + t.Errorf("bad test (bad length) %d: %v", i, test) + continue + + } + serializedhex, ok := test[1].(string) + if !ok { + t.Errorf("bad test (arg 2 not string) %d: %v", i, test) + continue + } + serializedTx, err := hex.DecodeString(serializedhex) + if err != nil { + t.Errorf("bad test (arg 2 not hex %v) %d: %v", err, i, + test) + continue + } + + tx, err := btcutil.NewTxFromBytes(serializedTx) + if err != nil { + t.Errorf("bad test (arg 2 not msgtx %v) %d: %v", err, + i, test) + continue + } + + verifyFlags, ok := test[2].(string) + if !ok { + t.Errorf("bad test (arg 3 not string) %d: %v", i, test) + continue + } + + flags, err := parseScriptFlags(verifyFlags) + if err != nil { + t.Errorf("bad test %d: %v", i, err) + continue + } + + prevOuts := make(map[wire.OutPoint][]byte) + for j, iinput := range inputs { + input, ok := iinput.([]interface{}) + if !ok { + t.Errorf("bad test (%dth input not array)"+ + "%d: %v", j, i, test) + continue testloop + } + + if len(input) != 3 { + t.Errorf("bad test (%dth input wrong length)"+ + "%d: %v", j, i, test) + continue testloop + } + + previoustx, ok := input[0].(string) + if !ok { + t.Errorf("bad test (%dth input sha not string)"+ + "%d: %v", j, i, test) + continue testloop + } + + prevhash, err := wire.NewShaHashFromStr(previoustx) + if err != nil { + t.Errorf("bad test (%dth input sha not sha %v)"+ + "%d: %v", j, err, i, test) + continue testloop + } + + idxf, ok := input[1].(float64) + if !ok { + t.Errorf("bad test (%dth input idx not number)"+ + "%d: %v", j, i, test) + continue testloop + } + idx := testVecF64ToUint32(idxf) + + oscript, ok := input[2].(string) + if !ok { + t.Errorf("bad test (%dth input script not "+ + "string) %d: %v", j, i, test) + continue testloop + } + + script, err := parseShortForm(oscript) + if err != nil { + t.Errorf("bad test (%dth input script doesn't "+ + "parse %v) %d: %v", j, err, i, test) + continue testloop + } + + prevOuts[*wire.NewOutPoint(prevhash, idx)] = script + } + + for k, txin := range tx.MsgTx().TxIn { + pkScript, ok := prevOuts[txin.PreviousOutPoint] + if !ok { + t.Errorf("bad test (missing %dth input) %d:%v", + k, i, test) + continue testloop + } + // These are meant to fail, so as soon as the first + // input fails the transaction has failed. (some of the + // test txns have good inputs, too.. + vm, err := NewEngine(pkScript, tx.MsgTx(), k, flags, nil) + if err != nil { + continue testloop + } + + err = vm.Execute() + if err != nil { + continue testloop + } + + } + t.Errorf("test (%d:%v) succeeded when should fail", + i, test) + } +} + +// TestTxValidTests ensures all of the tests in tx_valid.json pass as expected. +func TestTxValidTests(t *testing.T) { + file, err := ioutil.ReadFile("data/tx_valid.json") + if err != nil { + t.Errorf("TestTxValidTests: %v\n", err) + return + } + + var tests [][]interface{} + err = json.Unmarshal(file, &tests) + if err != nil { + t.Errorf("TestTxValidTests couldn't Unmarshal: %v\n", err) + return + } + + // form is either: + // ["this is a comment "] + // or: + // [[[previous hash, previous index, previous scriptPubKey]...,] + // serializedTransaction, verifyFlags] +testloop: + for i, test := range tests { + inputs, ok := test[0].([]interface{}) + if !ok { + continue + } + + if len(test) != 3 { + t.Errorf("bad test (bad length) %d: %v", i, test) + continue + } + serializedhex, ok := test[1].(string) + if !ok { + t.Errorf("bad test (arg 2 not string) %d: %v", i, test) + continue + } + serializedTx, err := hex.DecodeString(serializedhex) + if err != nil { + t.Errorf("bad test (arg 2 not hex %v) %d: %v", err, i, + test) + continue + } + + tx, err := btcutil.NewTxFromBytes(serializedTx) + if err != nil { + t.Errorf("bad test (arg 2 not msgtx %v) %d: %v", err, + i, test) + continue + } + + verifyFlags, ok := test[2].(string) + if !ok { + t.Errorf("bad test (arg 3 not string) %d: %v", i, test) + continue + } + + flags, err := parseScriptFlags(verifyFlags) + if err != nil { + t.Errorf("bad test %d: %v", i, err) + continue + } + + prevOuts := make(map[wire.OutPoint][]byte) + for j, iinput := range inputs { + input, ok := iinput.([]interface{}) + if !ok { + t.Errorf("bad test (%dth input not array)"+ + "%d: %v", j, i, test) + continue + } + + if len(input) != 3 { + t.Errorf("bad test (%dth input wrong length)"+ + "%d: %v", j, i, test) + continue + } + + previoustx, ok := input[0].(string) + if !ok { + t.Errorf("bad test (%dth input sha not string)"+ + "%d: %v", j, i, test) + continue + } + + prevhash, err := wire.NewShaHashFromStr(previoustx) + if err != nil { + t.Errorf("bad test (%dth input sha not sha %v)"+ + "%d: %v", j, err, i, test) + continue + } + + idxf, ok := input[1].(float64) + if !ok { + t.Errorf("bad test (%dth input idx not number)"+ + "%d: %v", j, i, test) + continue + } + idx := testVecF64ToUint32(idxf) + + oscript, ok := input[2].(string) + if !ok { + t.Errorf("bad test (%dth input script not "+ + "string) %d: %v", j, i, test) + continue + } + + script, err := parseShortForm(oscript) + if err != nil { + t.Errorf("bad test (%dth input script doesn't "+ + "parse %v) %d: %v", j, err, i, test) + continue + } + + prevOuts[*wire.NewOutPoint(prevhash, idx)] = script + } + + for k, txin := range tx.MsgTx().TxIn { + pkScript, ok := prevOuts[txin.PreviousOutPoint] + if !ok { + t.Errorf("bad test (missing %dth input) %d:%v", + k, i, test) + continue testloop + } + vm, err := NewEngine(pkScript, tx.MsgTx(), k, flags, nil) + if err != nil { + t.Errorf("test (%d:%v:%d) failed to create "+ + "script: %v", i, test, k, err) + continue + } + + err = vm.Execute() + if err != nil { + t.Errorf("test (%d:%v:%d) failed to execute: "+ + "%v", i, test, k, err) + continue + } + } + } +} + +// TestCalcSignatureHash runs the Bitcoin Core signature hash calculation tests +// in sighash.json. +// https://github.com/bitcoin/bitcoin/blob/master/src/test/data/sighash.json +func TestCalcSignatureHash(t *testing.T) { + file, err := ioutil.ReadFile("data/sighash.json") + if err != nil { + t.Errorf("TestCalcSignatureHash: %v\n", err) + return + } + + var tests [][]interface{} + err = json.Unmarshal(file, &tests) + if err != nil { + t.Errorf("TestCalcSignatureHash couldn't Unmarshal: %v\n", + err) + return + } + + for i, test := range tests { + if i == 0 { + // Skip first line -- contains comments only. + continue + } + if len(test) != 5 { + t.Fatalf("TestCalcSignatureHash: Test #%d has "+ + "wrong length.", i) + } + tx := wire.NewMsgTx() + rawTx, _ := hex.DecodeString(test[0].(string)) + err := tx.Deserialize(bytes.NewReader(rawTx)) + if err != nil { + t.Errorf("TestCalcSignatureHash failed test #%d: "+ + "Failed to parse transaction: %v", i, err) + continue + } + + subScript, _ := hex.DecodeString(test[1].(string)) + parsedScript, err := TstParseScript(subScript) + if err != nil { + t.Errorf("TestCalcSignatureHash failed test #%d: "+ + "Failed to parse sub-script: %v", i, err) + continue + } + + hashType := SigHashType(testVecF64ToUint32(test[3].(float64))) + hash := TstCalcSignatureHash(parsedScript, hashType, tx, + int(test[2].(float64))) + + expectedHash, _ := wire.NewShaHashFromStr(test[4].(string)) + if !bytes.Equal(hash, expectedHash.Bytes()) { + t.Errorf("TestCalcSignatureHash failed test #%d: "+ + "Signature hash mismatch.", i) + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/script.go b/vendor/github.com/btcsuite/btcd/txscript/script.go new file mode 100644 index 0000000000000000000000000000000000000000..ffac280430b82f0fe9e93f4a327b451044da5a3c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/script.go @@ -0,0 +1,473 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "bytes" + "encoding/binary" + "fmt" + "time" + + "github.com/btcsuite/btcd/wire" +) + +// Bip16Activation is the timestamp where BIP0016 is valid to use in the +// blockchain. To be used to determine if BIP0016 should be called for or not. +// This timestamp corresponds to Sun Apr 1 00:00:00 UTC 2012. +var Bip16Activation = time.Unix(1333238400, 0) + +// SigHashType represents hash type bits at the end of a signature. +type SigHashType uint32 + +// Hash type bits from the end of a signature. +const ( + SigHashOld SigHashType = 0x0 + SigHashAll SigHashType = 0x1 + SigHashNone SigHashType = 0x2 + SigHashSingle SigHashType = 0x3 + SigHashAnyOneCanPay SigHashType = 0x80 + + // sigHashMask defines the number of bits of the hash type which is used + // to identify which outputs are signed. + sigHashMask = 0x1f +) + +// These are the constants specified for maximums in individual scripts. +const ( + MaxOpsPerScript = 201 // Max number of non-push operations. + MaxPubKeysPerMultiSig = 20 // Multisig can't have more sigs than this. + MaxScriptElementSize = 520 // Max bytes pushable to the stack. +) + +// isSmallInt returns whether or not the opcode is considered a small integer, +// which is an OP_0, or OP_1 through OP_16. +func isSmallInt(op *opcode) bool { + if op.value == OP_0 || (op.value >= OP_1 && op.value <= OP_16) { + return true + } + return false +} + +// isScriptHash returns true if the script passed is a pay-to-script-hash +// transaction, false otherwise. +func isScriptHash(pops []parsedOpcode) bool { + return len(pops) == 3 && + pops[0].opcode.value == OP_HASH160 && + pops[1].opcode.value == OP_DATA_20 && + pops[2].opcode.value == OP_EQUAL +} + +// IsPayToScriptHash returns true if the script is in the standard +// pay-to-script-hash (P2SH) format, false otherwise. +func IsPayToScriptHash(script []byte) bool { + pops, err := parseScript(script) + if err != nil { + return false + } + return isScriptHash(pops) +} + +// isPushOnly returns true if the script only pushes data, false otherwise. +func isPushOnly(pops []parsedOpcode) bool { + // NOTE: This function does NOT verify opcodes directly since it is + // internal and is only called with parsed opcodes for scripts that did + // not have any parse errors. Thus, consensus is properly maintained. + + for _, pop := range pops { + // All opcodes up to OP_16 are data push instructions. + // NOTE: This does consider OP_RESERVED to be a data push + // instruction, but execution of OP_RESERVED will fail anyways + // and matches the behavior required by consensus. + if pop.opcode.value > OP_16 { + return false + } + } + return true +} + +// IsPushOnlyScript returns whether or not the passed script only pushes data. +// +// False will be returned when the script does not parse. +func IsPushOnlyScript(script []byte) bool { + pops, err := parseScript(script) + if err != nil { + return false + } + return isPushOnly(pops) +} + +// parseScriptTemplate is the same as parseScript but allows the passing of the +// template list for testing purposes. When there are parse errors, it returns +// the list of parsed opcodes up to the point of failure along with the error. +func parseScriptTemplate(script []byte, opcodes *[256]opcode) ([]parsedOpcode, error) { + retScript := make([]parsedOpcode, 0, len(script)) + for i := 0; i < len(script); { + instr := script[i] + op := opcodes[instr] + pop := parsedOpcode{opcode: &op} + + // Parse data out of instruction. + switch { + // No additional data. Note that some of the opcodes, notably + // OP_1NEGATE, OP_0, and OP_[1-16] represent the data + // themselves. + case op.length == 1: + i++ + + // Data pushes of specific lengths -- OP_DATA_[1-75]. + case op.length > 1: + if len(script[i:]) < op.length { + return retScript, ErrStackShortScript + } + + // Slice out the data. + pop.data = script[i+1 : i+op.length] + i += op.length + + // Data pushes with parsed lengths -- OP_PUSHDATAP{1,2,4}. + case op.length < 0: + var l uint + off := i + 1 + + if len(script[off:]) < -op.length { + return retScript, ErrStackShortScript + } + + // Next -length bytes are little endian length of data. + switch op.length { + case -1: + l = uint(script[off]) + case -2: + l = ((uint(script[off+1]) << 8) | + uint(script[off])) + case -4: + l = ((uint(script[off+3]) << 24) | + (uint(script[off+2]) << 16) | + (uint(script[off+1]) << 8) | + uint(script[off])) + default: + return retScript, + fmt.Errorf("invalid opcode length %d", + op.length) + } + + // Move offset to beginning of the data. + off += -op.length + + // Disallow entries that do not fit script or were + // sign extended. + if int(l) > len(script[off:]) || int(l) < 0 { + return retScript, ErrStackShortScript + } + + pop.data = script[off : off+int(l)] + i += 1 - op.length + int(l) + } + + retScript = append(retScript, pop) + } + + return retScript, nil +} + +// parseScript preparses the script in bytes into a list of parsedOpcodes while +// applying a number of sanity checks. +func parseScript(script []byte) ([]parsedOpcode, error) { + return parseScriptTemplate(script, &opcodeArray) +} + +// unparseScript reversed the action of parseScript and returns the +// parsedOpcodes as a list of bytes +func unparseScript(pops []parsedOpcode) ([]byte, error) { + script := make([]byte, 0, len(pops)) + for _, pop := range pops { + b, err := pop.bytes() + if err != nil { + return nil, err + } + script = append(script, b...) + } + return script, nil +} + +// DisasmString formats a disassembled script for one line printing. When the +// script fails to parse, the returned string will contain the disassembled +// script up to the point the failure occurred along with the string '[error]' +// appended. In addition, the reason the script failed to parse is returned +// if the caller wants more information about the failure. +func DisasmString(buf []byte) (string, error) { + var disbuf bytes.Buffer + opcodes, err := parseScript(buf) + for _, pop := range opcodes { + disbuf.WriteString(pop.print(true)) + disbuf.WriteByte(' ') + } + if disbuf.Len() > 0 { + disbuf.Truncate(disbuf.Len() - 1) + } + if err != nil { + disbuf.WriteString("[error]") + } + return disbuf.String(), err +} + +// removeOpcode will remove any opcode matching ``opcode'' from the opcode +// stream in pkscript +func removeOpcode(pkscript []parsedOpcode, opcode byte) []parsedOpcode { + retScript := make([]parsedOpcode, 0, len(pkscript)) + for _, pop := range pkscript { + if pop.opcode.value != opcode { + retScript = append(retScript, pop) + } + } + return retScript +} + +// canonicalPush returns true if the object is either not a push instruction +// or the push instruction contained wherein is matches the canonical form +// or using the smallest instruction to do the job. False otherwise. +func canonicalPush(pop parsedOpcode) bool { + opcode := pop.opcode.value + data := pop.data + dataLen := len(pop.data) + if opcode > OP_16 { + return true + } + + if opcode < OP_PUSHDATA1 && opcode > OP_0 && (dataLen == 1 && data[0] <= 16) { + return false + } + if opcode == OP_PUSHDATA1 && dataLen < OP_PUSHDATA1 { + return false + } + if opcode == OP_PUSHDATA2 && dataLen <= 0xff { + return false + } + if opcode == OP_PUSHDATA4 && dataLen <= 0xffff { + return false + } + return true +} + +// removeOpcodeByData will return the script minus any opcodes that would push +// the passed data to the stack. +func removeOpcodeByData(pkscript []parsedOpcode, data []byte) []parsedOpcode { + retScript := make([]parsedOpcode, 0, len(pkscript)) + for _, pop := range pkscript { + if !canonicalPush(pop) || !bytes.Contains(pop.data, data) { + retScript = append(retScript, pop) + } + } + return retScript + +} + +// calcSignatureHash will, given a script and hash type for the current script +// engine instance, calculate the signature hash to be used for signing and +// verification. +func calcSignatureHash(script []parsedOpcode, hashType SigHashType, tx *wire.MsgTx, idx int) []byte { + // The SigHashSingle signature type signs only the corresponding input + // and output (the output with the same index number as the input). + // + // Since transactions can have more inputs than outputs, this means it + // is improper to use SigHashSingle on input indices that don't have a + // corresponding output. + // + // A bug in the original Satoshi client implementation means specifying + // an index that is out of range results in a signature hash of 1 (as a + // uint256 little endian). The original intent appeared to be to + // indicate failure, but unfortunately, it was never checked and thus is + // treated as the actual signature hash. This buggy behavior is now + // part of the consensus and a hard fork would be required to fix it. + // + // Due to this, care must be taken by software that creates transactions + // which make use of SigHashSingle because it can lead to an extremely + // dangerous situation where the invalid inputs will end up signing a + // hash of 1. This in turn presents an opportunity for attackers to + // cleverly construct transactions which can steal those coins provided + // they can reuse signatures. + if hashType&sigHashMask == SigHashSingle && idx >= len(tx.TxOut) { + var hash wire.ShaHash + hash[0] = 0x01 + return hash[:] + } + + // Remove all instances of OP_CODESEPARATOR from the script. + script = removeOpcode(script, OP_CODESEPARATOR) + + // Make a deep copy of the transaction, zeroing out the script for all + // inputs that are not currently being processed. + txCopy := tx.Copy() + for i := range txCopy.TxIn { + if i == idx { + // UnparseScript cannot fail here because removeOpcode + // above only returns a valid script. + sigScript, _ := unparseScript(script) + txCopy.TxIn[idx].SignatureScript = sigScript + } else { + txCopy.TxIn[i].SignatureScript = nil + } + } + + switch hashType & sigHashMask { + case SigHashNone: + txCopy.TxOut = txCopy.TxOut[0:0] // Empty slice. + for i := range txCopy.TxIn { + if i != idx { + txCopy.TxIn[i].Sequence = 0 + } + } + + case SigHashSingle: + // Resize output array to up to and including requested index. + txCopy.TxOut = txCopy.TxOut[:idx+1] + + // All but current output get zeroed out. + for i := 0; i < idx; i++ { + txCopy.TxOut[i].Value = -1 + txCopy.TxOut[i].PkScript = nil + } + + // Sequence on all other inputs is 0, too. + for i := range txCopy.TxIn { + if i != idx { + txCopy.TxIn[i].Sequence = 0 + } + } + + default: + // Consensus treats undefined hashtypes like normal SigHashAll + // for purposes of hash generation. + fallthrough + case SigHashOld: + fallthrough + case SigHashAll: + // Nothing special here. + } + if hashType&SigHashAnyOneCanPay != 0 { + txCopy.TxIn = txCopy.TxIn[idx : idx+1] + idx = 0 + } + + // The final hash is the double sha256 of both the serialized modified + // transaction and the hash type (encoded as a 4-byte little-endian + // value) appended. + var wbuf bytes.Buffer + txCopy.Serialize(&wbuf) + binary.Write(&wbuf, binary.LittleEndian, hashType) + return wire.DoubleSha256(wbuf.Bytes()) +} + +// asSmallInt returns the passed opcode, which must be true according to +// isSmallInt(), as an integer. +func asSmallInt(op *opcode) int { + if op.value == OP_0 { + return 0 + } + + return int(op.value - (OP_1 - 1)) +} + +// getSigOpCount is the implementation function for counting the number of +// signature operations in the script provided by pops. If precise mode is +// requested then we attempt to count the number of operations for a multisig +// op. Otherwise we use the maximum. +func getSigOpCount(pops []parsedOpcode, precise bool) int { + nSigs := 0 + for i, pop := range pops { + switch pop.opcode.value { + case OP_CHECKSIG: + fallthrough + case OP_CHECKSIGVERIFY: + nSigs++ + case OP_CHECKMULTISIG: + fallthrough + case OP_CHECKMULTISIGVERIFY: + // If we are being precise then look for familiar + // patterns for multisig, for now all we recognize is + // OP_1 - OP_16 to signify the number of pubkeys. + // Otherwise, we use the max of 20. + if precise && i > 0 && + pops[i-1].opcode.value >= OP_1 && + pops[i-1].opcode.value <= OP_16 { + nSigs += asSmallInt(pops[i-1].opcode) + } else { + nSigs += MaxPubKeysPerMultiSig + } + default: + // Not a sigop. + } + } + + return nSigs +} + +// GetSigOpCount provides a quick count of the number of signature operations +// in a script. a CHECKSIG operations counts for 1, and a CHECK_MULTISIG for 20. +// If the script fails to parse, then the count up to the point of failure is +// returned. +func GetSigOpCount(script []byte) int { + // Don't check error since parseScript returns the parsed-up-to-error + // list of pops. + pops, _ := parseScript(script) + return getSigOpCount(pops, false) +} + +// GetPreciseSigOpCount returns the number of signature operations in +// scriptPubKey. If bip16 is true then scriptSig may be searched for the +// Pay-To-Script-Hash script in order to find the precise number of signature +// operations in the transaction. If the script fails to parse, then the count +// up to the point of failure is returned. +func GetPreciseSigOpCount(scriptSig, scriptPubKey []byte, bip16 bool) int { + // Don't check error since parseScript returns the parsed-up-to-error + // list of pops. + pops, _ := parseScript(scriptPubKey) + + // Treat non P2SH transactions as normal. + if !(bip16 && isScriptHash(pops)) { + return getSigOpCount(pops, true) + } + + // The public key script is a pay-to-script-hash, so parse the signature + // script to get the final item. Scripts that fail to fully parse count + // as 0 signature operations. + sigPops, err := parseScript(scriptSig) + if err != nil { + return 0 + } + + // The signature script must only push data to the stack for P2SH to be + // a valid pair, so the signature operation count is 0 when that is not + // the case. + if !isPushOnly(sigPops) || len(sigPops) == 0 { + return 0 + } + + // The P2SH script is the last item the signature script pushes to the + // stack. When the script is empty, there are no signature operations. + shScript := sigPops[len(sigPops)-1].data + if len(shScript) == 0 { + return 0 + } + + // Parse the P2SH script and don't check the error since parseScript + // returns the parsed-up-to-error list of pops and the consensus rules + // dictate signature operations are counted up to the first parse + // failure. + shPops, _ := parseScript(shScript) + return getSigOpCount(shPops, true) +} + +// IsUnspendable returns whether the passed public key script is unspendable, or +// guaranteed to fail at execution. This allows inputs to be pruned instantly +// when entering the UTXO set. +func IsUnspendable(pkScript []byte) bool { + pops, err := parseScript(pkScript) + if err != nil { + return true + } + + return len(pops) > 0 && pops[0].opcode.value == OP_RETURN +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/script_test.go b/vendor/github.com/btcsuite/btcd/txscript/script_test.go new file mode 100644 index 0000000000000000000000000000000000000000..012d07f87713a9f6aa8f8593cc02dd2064767cef --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/script_test.go @@ -0,0 +1,523 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript_test + +import ( + "bytes" + "reflect" + "testing" + + "github.com/btcsuite/btcd/txscript" +) + +// TestPushedData ensured the PushedData function extracts the expected data out +// of various scripts. +func TestPushedData(t *testing.T) { + t.Parallel() + + var tests = []struct { + script string + out [][]byte + valid bool + }{ + { + "0 IF 0 ELSE 2 ENDIF", + [][]byte{nil, nil}, + true, + }, + { + "16777216 10000000", + [][]byte{ + {0x00, 0x00, 0x00, 0x01}, // 16777216 + {0x80, 0x96, 0x98, 0x00}, // 10000000 + }, + true, + }, + { + "DUP HASH160 '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem' EQUALVERIFY CHECKSIG", + [][]byte{ + // 17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem + { + 0x31, 0x37, 0x56, 0x5a, 0x4e, 0x58, 0x31, 0x53, 0x4e, 0x35, + 0x4e, 0x74, 0x4b, 0x61, 0x38, 0x55, 0x51, 0x46, 0x78, 0x77, + 0x51, 0x62, 0x46, 0x65, 0x46, 0x63, 0x33, 0x69, 0x71, 0x52, + 0x59, 0x68, 0x65, 0x6d, + }, + }, + true, + }, + { + "PUSHDATA4 1000 EQUAL", + nil, + false, + }, + } + + for i, test := range tests { + script := mustParseShortForm(test.script) + data, err := txscript.PushedData(script) + if test.valid && err != nil { + t.Errorf("TestPushedData failed test #%d: %v\n", i, err) + continue + } else if !test.valid && err == nil { + t.Errorf("TestPushedData failed test #%d: test should "+ + "be invalid\n", i) + continue + } + if !reflect.DeepEqual(data, test.out) { + t.Errorf("TestPushedData failed test #%d: want: %x "+ + "got: %x\n", i, test.out, data) + } + } +} + +// TestHasCanonicalPush ensures the canonicalPush function works as expected. +func TestHasCanonicalPush(t *testing.T) { + t.Parallel() + + for i := 0; i < 65535; i++ { + builder := txscript.NewScriptBuilder() + builder.AddInt64(int64(i)) + script, err := builder.Script() + if err != nil { + t.Errorf("Script: test #%d unexpected error: %v\n", i, + err) + continue + } + if result := txscript.IsPushOnlyScript(script); !result { + t.Errorf("IsPushOnlyScript: test #%d failed: %x\n", i, + script) + continue + } + pops, err := txscript.TstParseScript(script) + if err != nil { + t.Errorf("TstParseScript: #%d failed: %v", i, err) + continue + } + for _, pop := range pops { + if result := txscript.TstHasCanonicalPushes(pop); !result { + t.Errorf("TstHasCanonicalPushes: test #%d "+ + "failed: %x\n", i, script) + break + } + } + } + for i := 0; i <= txscript.MaxScriptElementSize; i++ { + builder := txscript.NewScriptBuilder() + builder.AddData(bytes.Repeat([]byte{0x49}, i)) + script, err := builder.Script() + if err != nil { + t.Errorf("StandardPushesTests test #%d unexpected error: %v\n", i, err) + continue + } + if result := txscript.IsPushOnlyScript(script); !result { + t.Errorf("StandardPushesTests IsPushOnlyScript test #%d failed: %x\n", i, script) + continue + } + pops, err := txscript.TstParseScript(script) + if err != nil { + t.Errorf("StandardPushesTests #%d failed to TstParseScript: %v", i, err) + continue + } + for _, pop := range pops { + if result := txscript.TstHasCanonicalPushes(pop); !result { + t.Errorf("StandardPushesTests TstHasCanonicalPushes test #%d failed: %x\n", i, script) + break + } + } + } +} + +// TestGetPreciseSigOps ensures the more precise signature operation counting +// mechanism which includes signatures in P2SH scripts works as expected. +func TestGetPreciseSigOps(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + scriptSig []byte + nSigOps int + err error + }{ + { + name: "scriptSig doesn't parse", + scriptSig: []byte{txscript.OP_PUSHDATA1, 2}, + err: txscript.ErrStackShortScript, + }, + { + name: "scriptSig isn't push only", + scriptSig: []byte{txscript.OP_1, txscript.OP_DUP}, + nSigOps: 0, + }, + { + name: "scriptSig length 0", + scriptSig: nil, + nSigOps: 0, + }, + { + name: "No script at the end", + // No script at end but still push only. + scriptSig: []byte{txscript.OP_1, txscript.OP_1}, + nSigOps: 0, + }, + { + name: "pushed script doesn't parse", + scriptSig: []byte{txscript.OP_DATA_2, + txscript.OP_PUSHDATA1, 2}, + err: txscript.ErrStackShortScript, + }, + } + + // The signature in the p2sh script is nonsensical for the tests since + // this script will never be executed. What matters is that it matches + // the right pattern. + pkScript := mustParseShortForm("HASH160 DATA_20 0x433ec2ac1ffa1b7b7d0" + + "27f564529c57197f9ae88 EQUAL") + for _, test := range tests { + count := txscript.GetPreciseSigOpCount(test.scriptSig, pkScript, + true) + if count != test.nSigOps { + t.Errorf("%s: expected count of %d, got %d", test.name, + test.nSigOps, count) + + } + } +} + +// TestRemoveOpcodes ensures that removing opcodes from scripts behaves as +// expected. +func TestRemoveOpcodes(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + before string + remove byte + err error + after string + }{ + { + // Nothing to remove. + name: "nothing to remove", + before: "NOP", + remove: txscript.OP_CODESEPARATOR, + after: "NOP", + }, + { + // Test basic opcode removal. + name: "codeseparator 1", + before: "NOP CODESEPARATOR TRUE", + remove: txscript.OP_CODESEPARATOR, + after: "NOP TRUE", + }, + { + // The opcode in question is actually part of the data + // in a previous opcode. + name: "codeseparator by coincidence", + before: "NOP DATA_1 CODESEPARATOR TRUE", + remove: txscript.OP_CODESEPARATOR, + after: "NOP DATA_1 CODESEPARATOR TRUE", + }, + { + name: "invalid opcode", + before: "CAT", + remove: txscript.OP_CODESEPARATOR, + after: "CAT", + }, + { + name: "invalid length (insruction)", + before: "PUSHDATA1", + remove: txscript.OP_CODESEPARATOR, + err: txscript.ErrStackShortScript, + }, + { + name: "invalid length (data)", + before: "PUSHDATA1 0xff 0xfe", + remove: txscript.OP_CODESEPARATOR, + err: txscript.ErrStackShortScript, + }, + } + + for _, test := range tests { + before := mustParseShortForm(test.before) + after := mustParseShortForm(test.after) + result, err := txscript.TstRemoveOpcode(before, test.remove) + if test.err != nil { + if err != test.err { + t.Errorf("%s: got unexpected error. exp: \"%v\" "+ + "got: \"%v\"", test.name, test.err, err) + } + return + } + if err != nil { + t.Errorf("%s: unexpected failure: \"%v\"", test.name, err) + return + } + if !bytes.Equal(after, result) { + t.Errorf("%s: value does not equal expected: exp: \"%v\""+ + " got: \"%v\"", test.name, after, result) + } + } +} + +// TestRemoveOpcodeByData ensures that removing data carrying opcodes based on +// the data they contain works as expected. +func TestRemoveOpcodeByData(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + before []byte + remove []byte + err error + after []byte + }{ + { + name: "nothing to do", + before: []byte{txscript.OP_NOP}, + remove: []byte{1, 2, 3, 4}, + after: []byte{txscript.OP_NOP}, + }, + { + name: "simple case", + before: []byte{txscript.OP_DATA_4, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 4}, + after: nil, + }, + { + name: "simple case (miss)", + before: []byte{txscript.OP_DATA_4, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 5}, + after: []byte{txscript.OP_DATA_4, 1, 2, 3, 4}, + }, + { + // padded to keep it canonical. + name: "simple case (pushdata1)", + before: append(append([]byte{txscript.OP_PUSHDATA1, 76}, + bytes.Repeat([]byte{0}, 72)...), + []byte{1, 2, 3, 4}...), + remove: []byte{1, 2, 3, 4}, + after: nil, + }, + { + name: "simple case (pushdata1 miss)", + before: append(append([]byte{txscript.OP_PUSHDATA1, 76}, + bytes.Repeat([]byte{0}, 72)...), + []byte{1, 2, 3, 4}...), + remove: []byte{1, 2, 3, 5}, + after: append(append([]byte{txscript.OP_PUSHDATA1, 76}, + bytes.Repeat([]byte{0}, 72)...), + []byte{1, 2, 3, 4}...), + }, + { + name: "simple case (pushdata1 miss noncanonical)", + before: []byte{txscript.OP_PUSHDATA1, 4, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 4}, + after: []byte{txscript.OP_PUSHDATA1, 4, 1, 2, 3, 4}, + }, + { + name: "simple case (pushdata2)", + before: append(append([]byte{txscript.OP_PUSHDATA2, 0, 1}, + bytes.Repeat([]byte{0}, 252)...), + []byte{1, 2, 3, 4}...), + remove: []byte{1, 2, 3, 4}, + after: nil, + }, + { + name: "simple case (pushdata2 miss)", + before: append(append([]byte{txscript.OP_PUSHDATA2, 0, 1}, + bytes.Repeat([]byte{0}, 252)...), + []byte{1, 2, 3, 4}...), + remove: []byte{1, 2, 3, 4, 5}, + after: append(append([]byte{txscript.OP_PUSHDATA2, 0, 1}, + bytes.Repeat([]byte{0}, 252)...), + []byte{1, 2, 3, 4}...), + }, + { + name: "simple case (pushdata2 miss noncanonical)", + before: []byte{txscript.OP_PUSHDATA2, 4, 0, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 4}, + after: []byte{txscript.OP_PUSHDATA2, 4, 0, 1, 2, 3, 4}, + }, + { + // This is padded to make the push canonical. + name: "simple case (pushdata4)", + before: append(append([]byte{txscript.OP_PUSHDATA4, 0, 0, 1, 0}, + bytes.Repeat([]byte{0}, 65532)...), + []byte{1, 2, 3, 4}...), + remove: []byte{1, 2, 3, 4}, + after: nil, + }, + { + name: "simple case (pushdata4 miss noncanonical)", + before: []byte{txscript.OP_PUSHDATA4, 4, 0, 0, 0, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 4}, + after: []byte{txscript.OP_PUSHDATA4, 4, 0, 0, 0, 1, 2, 3, 4}, + }, + { + // This is padded to make the push canonical. + name: "simple case (pushdata4 miss)", + before: append(append([]byte{txscript.OP_PUSHDATA4, 0, 0, 1, 0}, + bytes.Repeat([]byte{0}, 65532)...), []byte{1, 2, 3, 4}...), + remove: []byte{1, 2, 3, 4, 5}, + after: append(append([]byte{txscript.OP_PUSHDATA4, 0, 0, 1, 0}, + bytes.Repeat([]byte{0}, 65532)...), []byte{1, 2, 3, 4}...), + }, + { + name: "invalid opcode ", + before: []byte{txscript.OP_UNKNOWN187}, + remove: []byte{1, 2, 3, 4}, + after: []byte{txscript.OP_UNKNOWN187}, + }, + { + name: "invalid length (instruction)", + before: []byte{txscript.OP_PUSHDATA1}, + remove: []byte{1, 2, 3, 4}, + err: txscript.ErrStackShortScript, + }, + { + name: "invalid length (data)", + before: []byte{txscript.OP_PUSHDATA1, 255, 254}, + remove: []byte{1, 2, 3, 4}, + err: txscript.ErrStackShortScript, + }, + } + + for _, test := range tests { + result, err := txscript.TstRemoveOpcodeByData(test.before, + test.remove) + if test.err != nil { + if err != test.err { + t.Errorf("%s: got unexpected error. exp: \"%v\" "+ + "got: \"%v\"", test.name, test.err, err) + } + return + } + if err != nil { + t.Errorf("%s: unexpected failure: \"%v\"", test.name, err) + return + } + if !bytes.Equal(test.after, result) { + t.Errorf("%s: value does not equal expected: exp: \"%v\""+ + " got: \"%v\"", test.name, test.after, result) + } + } +} + +// TestIsPayToScriptHash ensures the IsPayToScriptHash function returns the +// expected results for all the scripts in scriptClassTests. +func TestIsPayToScriptHash(t *testing.T) { + t.Parallel() + + for _, test := range scriptClassTests { + script := mustParseShortForm(test.script) + shouldBe := (test.class == txscript.ScriptHashTy) + p2sh := txscript.IsPayToScriptHash(script) + if p2sh != shouldBe { + t.Errorf("%s: epxected p2sh %v, got %v", test.name, + shouldBe, p2sh) + } + } +} + +// TestHasCanonicalPushes ensures the canonicalPush function properly determines +// what is considered a canonical push for the purposes of removeOpcodeByData. +func TestHasCanonicalPushes(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + script string + expected bool + }{ + { + name: "does not parse", + script: "0x046708afdb0fe5548271967f1a67130b7105cd6a82" + + "8e03909a67962e0ea1f61d", + expected: false, + }, + { + name: "non-canonical push", + script: "PUSHDATA1 0x04 0x01020304", + expected: false, + }, + } + + for i, test := range tests { + script := mustParseShortForm(test.script) + pops, err := txscript.TstParseScript(script) + if err != nil { + if test.expected { + t.Errorf("TstParseScript #%d failed: %v", i, err) + } + continue + } + for _, pop := range pops { + if txscript.TstHasCanonicalPushes(pop) != test.expected { + t.Errorf("TstHasCanonicalPushes: #%d (%s) "+ + "wrong result\ngot: %v\nwant: %v", i, + test.name, true, test.expected) + break + } + } + } +} + +// TestIsPushOnlyScript ensures the IsPushOnlyScript function returns the +// expected results. +func TestIsPushOnlyScript(t *testing.T) { + t.Parallel() + + test := struct { + name string + script []byte + expected bool + }{ + name: "does not parse", + script: mustParseShortForm("0x046708afdb0fe5548271967f1a67130" + + "b7105cd6a828e03909a67962e0ea1f61d"), + expected: false, + } + + if txscript.IsPushOnlyScript(test.script) != test.expected { + t.Errorf("IsPushOnlyScript (%s) wrong result\ngot: %v\nwant: "+ + "%v", test.name, true, test.expected) + } +} + +// TestIsUnspendable ensures the IsUnspendable function returns the expected +// results. +func TestIsUnspendable(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + pkScript []byte + expected bool + }{ + { + // Unspendable + pkScript: []byte{0x6a, 0x04, 0x74, 0x65, 0x73, 0x74}, + expected: true, + }, + { + // Spendable + pkScript: []byte{0x76, 0xa9, 0x14, 0x29, 0x95, 0xa0, + 0xfe, 0x68, 0x43, 0xfa, 0x9b, 0x95, 0x45, + 0x97, 0xf0, 0xdc, 0xa7, 0xa4, 0x4d, 0xf6, + 0xfa, 0x0b, 0x5c, 0x88, 0xac}, + expected: false, + }, + } + + for i, test := range tests { + res := txscript.IsUnspendable(test.pkScript) + if res != test.expected { + t.Errorf("TestIsUnspendable #%d failed: got %v want %v", + i, res, test.expected) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/scriptbuilder.go b/vendor/github.com/btcsuite/btcd/txscript/scriptbuilder.go new file mode 100644 index 0000000000000000000000000000000000000000..6c8f641e06e2c4129a88b47742066cd416d1c203 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/scriptbuilder.go @@ -0,0 +1,253 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "encoding/binary" + "fmt" +) + +const ( + // defaultScriptAlloc is the default size used for the backing array + // for a script being built by the ScriptBuilder. The array will + // dynamically grow as needed, but this figure is intended to provide + // enough space for vast majority of scripts without needing to grow the + // backing array multiple times. + defaultScriptAlloc = 500 +) + +// ErrScriptNotCanonical identifies a non-canonical script. The caller can use +// a type assertion to detect this error type. +type ErrScriptNotCanonical string + +// Error implements the error interface. +func (e ErrScriptNotCanonical) Error() string { + return string(e) +} + +// ScriptBuilder provides a facility for building custom scripts. It allows +// you to push opcodes, ints, and data while respecting canonical encoding. In +// general it does not ensure the script will execute correctly, however any +// data pushes which would exceed the maximum allowed script engine limits and +// are therefore guaranteed not to execute will not be pushed and will result in +// the Script function returning an error. +// +// For example, the following would build a 2-of-3 multisig script for usage in +// a pay-to-script-hash (although in this situation MultiSigScript() would be a +// better choice to generate the script): +// builder := txscript.NewScriptBuilder() +// builder.AddOp(txscript.OP_2).AddData(pubKey1).AddData(pubKey2) +// builder.AddData(pubKey3).AddOp(txscript.OP_3) +// builder.AddOp(txscript.OP_CHECKMULTISIG) +// script, err := builder.Script() +// if err != nil { +// // Handle the error. +// return +// } +// fmt.Printf("Final multi-sig script: %x\n", script) +type ScriptBuilder struct { + script []byte + err error +} + +// AddOp pushes the passed opcode to the end of the script. The script will not +// be modified if pushing the opcode would cause the script to exceed the +// maximum allowed script engine size. +func (b *ScriptBuilder) AddOp(opcode byte) *ScriptBuilder { + if b.err != nil { + return b + } + + // Pushes that would cause the script to exceed the largest allowed + // script size would result in a non-canonical script. + if len(b.script)+1 > maxScriptSize { + str := fmt.Sprintf("adding an opcode would exceed the maximum "+ + "allowed canonical script length of %d", maxScriptSize) + b.err = ErrScriptNotCanonical(str) + return b + } + + b.script = append(b.script, opcode) + return b +} + +// canonicalDataSize returns the number of bytes the canonical encoding of the +// data will take. +func canonicalDataSize(data []byte) int { + dataLen := len(data) + + // When the data consists of a single number that can be represented + // by one of the "small integer" opcodes, that opcode will be instead + // of a data push opcode followed by the number. + if dataLen == 0 { + return 1 + } else if dataLen == 1 && data[0] <= 16 { + return 1 + } else if dataLen == 1 && data[0] == 0x81 { + return 1 + } + + if dataLen < OP_PUSHDATA1 { + return 1 + dataLen + } else if dataLen <= 0xff { + return 2 + dataLen + } else if dataLen <= 0xffff { + return 3 + dataLen + } + + return 5 + dataLen +} + +// addData is the internal function that actually pushes the passed data to the +// end of the script. It automatically chooses canonical opcodes depending on +// the length of the data. A zero length buffer will lead to a push of empty +// data onto the stack (OP_0). No data limits are enforced with this function. +func (b *ScriptBuilder) addData(data []byte) *ScriptBuilder { + dataLen := len(data) + + // When the data consists of a single number that can be represented + // by one of the "small integer" opcodes, use that opcode instead of + // a data push opcode followed by the number. + if dataLen == 0 || dataLen == 1 && data[0] == 0 { + b.script = append(b.script, OP_0) + return b + } else if dataLen == 1 && data[0] <= 16 { + b.script = append(b.script, byte((OP_1-1)+data[0])) + return b + } else if dataLen == 1 && data[0] == 0x81 { + b.script = append(b.script, byte(OP_1NEGATE)) + return b + } + + // Use one of the OP_DATA_# opcodes if the length of the data is small + // enough so the data push instruction is only a single byte. + // Otherwise, choose the smallest possible OP_PUSHDATA# opcode that + // can represent the length of the data. + if dataLen < OP_PUSHDATA1 { + b.script = append(b.script, byte((OP_DATA_1-1)+dataLen)) + } else if dataLen <= 0xff { + b.script = append(b.script, OP_PUSHDATA1, byte(dataLen)) + } else if dataLen <= 0xffff { + buf := make([]byte, 2) + binary.LittleEndian.PutUint16(buf, uint16(dataLen)) + b.script = append(b.script, OP_PUSHDATA2) + b.script = append(b.script, buf...) + } else { + buf := make([]byte, 4) + binary.LittleEndian.PutUint32(buf, uint32(dataLen)) + b.script = append(b.script, OP_PUSHDATA4) + b.script = append(b.script, buf...) + } + + // Append the actual data. + b.script = append(b.script, data...) + + return b +} + +// AddFullData should not typically be used by ordinary users as it does not +// include the checks which prevent data pushes larger than the maximum allowed +// sizes which leads to scripts that can't be executed. This is provided for +// testing purposes such as regression tests where sizes are intentionally made +// larger than allowed. +// +// Use AddData instead. +func (b *ScriptBuilder) AddFullData(data []byte) *ScriptBuilder { + if b.err != nil { + return b + } + + return b.addData(data) +} + +// AddData pushes the passed data to the end of the script. It automatically +// chooses canonical opcodes depending on the length of the data. A zero length +// buffer will lead to a push of empty data onto the stack (OP_0) and any push +// of data greater than MaxScriptElementSize will not modify the script since +// that is not allowed by the script engine. Also, the script will not be +// modified if pushing the data would cause the script to exceed the maximum +// allowed script engine size. +func (b *ScriptBuilder) AddData(data []byte) *ScriptBuilder { + if b.err != nil { + return b + } + + // Pushes that would cause the script to exceed the largest allowed + // script size would result in a non-canonical script. + dataSize := canonicalDataSize(data) + if len(b.script)+dataSize > maxScriptSize { + str := fmt.Sprintf("adding %d bytes of data would exceed the "+ + "maximum allowed canonical script length of %d", + dataSize, maxScriptSize) + b.err = ErrScriptNotCanonical(str) + return b + } + + // Pushes larger than the max script element size would result in a + // script that is not canonical. + dataLen := len(data) + if dataLen > MaxScriptElementSize { + str := fmt.Sprintf("adding a data element of %d bytes would "+ + "exceed the maximum allowed script element size of %d", + dataLen, maxScriptSize) + b.err = ErrScriptNotCanonical(str) + return b + } + + return b.addData(data) +} + +// AddInt64 pushes the passed integer to the end of the script. The script will +// not be modified if pushing the data would cause the script to exceed the +// maximum allowed script engine size. +func (b *ScriptBuilder) AddInt64(val int64) *ScriptBuilder { + if b.err != nil { + return b + } + + // Pushes that would cause the script to exceed the largest allowed + // script size would result in a non-canonical script. + if len(b.script)+1 > maxScriptSize { + str := fmt.Sprintf("adding an integer would exceed the "+ + "maximum allow canonical script length of %d", + maxScriptSize) + b.err = ErrScriptNotCanonical(str) + return b + } + + // Fast path for small integers and OP_1NEGATE. + if val == 0 { + b.script = append(b.script, OP_0) + return b + } + if val == -1 || (val >= 1 && val <= 16) { + b.script = append(b.script, byte((OP_1-1)+val)) + return b + } + + return b.AddData(scriptNum(val).Bytes()) +} + +// Reset resets the script so it has no content. +func (b *ScriptBuilder) Reset() *ScriptBuilder { + b.script = b.script[0:0] + b.err = nil + return b +} + +// Script returns the currently built script. When any errors occurred while +// building the script, the script will be returned up the point of the first +// error along with the error. +func (b *ScriptBuilder) Script() ([]byte, error) { + return b.script, b.err +} + +// NewScriptBuilder returns a new instance of a script builder. See +// ScriptBuilder for details. +func NewScriptBuilder() *ScriptBuilder { + return &ScriptBuilder{ + script: make([]byte, 0, defaultScriptAlloc), + } +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/scriptbuilder_test.go b/vendor/github.com/btcsuite/btcd/txscript/scriptbuilder_test.go new file mode 100644 index 0000000000000000000000000000000000000000..677c0cf0aec8b1ec7612c32d0c1fbcf231e61a17 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/scriptbuilder_test.go @@ -0,0 +1,395 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript_test + +import ( + "bytes" + "testing" + + "github.com/btcsuite/btcd/txscript" +) + +// TestScriptBuilderAddOp tests that pushing opcodes to a script via the +// ScriptBuilder API works as expected. +func TestScriptBuilderAddOp(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + opcodes []byte + expected []byte + }{ + { + name: "push OP_0", + opcodes: []byte{txscript.OP_0}, + expected: []byte{txscript.OP_0}, + }, + { + name: "push OP_1 OP_2", + opcodes: []byte{txscript.OP_1, txscript.OP_2}, + expected: []byte{txscript.OP_1, txscript.OP_2}, + }, + { + name: "push OP_HASH160 OP_EQUAL", + opcodes: []byte{txscript.OP_HASH160, txscript.OP_EQUAL}, + expected: []byte{txscript.OP_HASH160, txscript.OP_EQUAL}, + }, + } + + builder := txscript.NewScriptBuilder() + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + builder.Reset() + for _, opcode := range test.opcodes { + builder.AddOp(opcode) + } + result, err := builder.Script() + if err != nil { + t.Errorf("ScriptBuilder.AddOp #%d (%s) unexpected "+ + "error: %v", i, test.name, err) + continue + } + if !bytes.Equal(result, test.expected) { + t.Errorf("ScriptBuilder.AddOp #%d (%s) wrong result\n"+ + "got: %x\nwant: %x", i, test.name, result, + test.expected) + continue + } + } +} + +// TestScriptBuilderAddInt64 tests that pushing signed integers to a script via +// the ScriptBuilder API works as expected. +func TestScriptBuilderAddInt64(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + val int64 + expected []byte + }{ + {name: "push -1", val: -1, expected: []byte{txscript.OP_1NEGATE}}, + {name: "push small int 0", val: 0, expected: []byte{txscript.OP_0}}, + {name: "push small int 1", val: 1, expected: []byte{txscript.OP_1}}, + {name: "push small int 2", val: 2, expected: []byte{txscript.OP_2}}, + {name: "push small int 3", val: 3, expected: []byte{txscript.OP_3}}, + {name: "push small int 4", val: 4, expected: []byte{txscript.OP_4}}, + {name: "push small int 5", val: 5, expected: []byte{txscript.OP_5}}, + {name: "push small int 6", val: 6, expected: []byte{txscript.OP_6}}, + {name: "push small int 7", val: 7, expected: []byte{txscript.OP_7}}, + {name: "push small int 8", val: 8, expected: []byte{txscript.OP_8}}, + {name: "push small int 9", val: 9, expected: []byte{txscript.OP_9}}, + {name: "push small int 10", val: 10, expected: []byte{txscript.OP_10}}, + {name: "push small int 11", val: 11, expected: []byte{txscript.OP_11}}, + {name: "push small int 12", val: 12, expected: []byte{txscript.OP_12}}, + {name: "push small int 13", val: 13, expected: []byte{txscript.OP_13}}, + {name: "push small int 14", val: 14, expected: []byte{txscript.OP_14}}, + {name: "push small int 15", val: 15, expected: []byte{txscript.OP_15}}, + {name: "push small int 16", val: 16, expected: []byte{txscript.OP_16}}, + {name: "push 17", val: 17, expected: []byte{txscript.OP_DATA_1, 0x11}}, + {name: "push 65", val: 65, expected: []byte{txscript.OP_DATA_1, 0x41}}, + {name: "push 127", val: 127, expected: []byte{txscript.OP_DATA_1, 0x7f}}, + {name: "push 128", val: 128, expected: []byte{txscript.OP_DATA_2, 0x80, 0}}, + {name: "push 255", val: 255, expected: []byte{txscript.OP_DATA_2, 0xff, 0}}, + {name: "push 256", val: 256, expected: []byte{txscript.OP_DATA_2, 0, 0x01}}, + {name: "push 32767", val: 32767, expected: []byte{txscript.OP_DATA_2, 0xff, 0x7f}}, + {name: "push 32768", val: 32768, expected: []byte{txscript.OP_DATA_3, 0, 0x80, 0}}, + {name: "push -2", val: -2, expected: []byte{txscript.OP_DATA_1, 0x82}}, + {name: "push -3", val: -3, expected: []byte{txscript.OP_DATA_1, 0x83}}, + {name: "push -4", val: -4, expected: []byte{txscript.OP_DATA_1, 0x84}}, + {name: "push -5", val: -5, expected: []byte{txscript.OP_DATA_1, 0x85}}, + {name: "push -17", val: -17, expected: []byte{txscript.OP_DATA_1, 0x91}}, + {name: "push -65", val: -65, expected: []byte{txscript.OP_DATA_1, 0xc1}}, + {name: "push -127", val: -127, expected: []byte{txscript.OP_DATA_1, 0xff}}, + {name: "push -128", val: -128, expected: []byte{txscript.OP_DATA_2, 0x80, 0x80}}, + {name: "push -255", val: -255, expected: []byte{txscript.OP_DATA_2, 0xff, 0x80}}, + {name: "push -256", val: -256, expected: []byte{txscript.OP_DATA_2, 0x00, 0x81}}, + {name: "push -32767", val: -32767, expected: []byte{txscript.OP_DATA_2, 0xff, 0xff}}, + {name: "push -32768", val: -32768, expected: []byte{txscript.OP_DATA_3, 0x00, 0x80, 0x80}}, + } + + builder := txscript.NewScriptBuilder() + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + builder.Reset().AddInt64(test.val) + result, err := builder.Script() + if err != nil { + t.Errorf("ScriptBuilder.AddInt64 #%d (%s) unexpected "+ + "error: %v", i, test.name, err) + continue + } + if !bytes.Equal(result, test.expected) { + t.Errorf("ScriptBuilder.AddInt64 #%d (%s) wrong result\n"+ + "got: %x\nwant: %x", i, test.name, result, + test.expected) + continue + } + } +} + +// TestScriptBuilderAddData tests that pushing data to a script via the +// ScriptBuilder API works as expected and conforms to BIP0062. +func TestScriptBuilderAddData(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + data []byte + expected []byte + useFull bool // use AddFullData instead of AddData. + }{ + // BIP0062: Pushing an empty byte sequence must use OP_0. + {name: "push empty byte sequence", data: nil, expected: []byte{txscript.OP_0}}, + {name: "push 1 byte 0x00", data: []byte{0x00}, expected: []byte{txscript.OP_0}}, + + // BIP0062: Pushing a 1-byte sequence of byte 0x01 through 0x10 must use OP_n. + {name: "push 1 byte 0x01", data: []byte{0x01}, expected: []byte{txscript.OP_1}}, + {name: "push 1 byte 0x02", data: []byte{0x02}, expected: []byte{txscript.OP_2}}, + {name: "push 1 byte 0x03", data: []byte{0x03}, expected: []byte{txscript.OP_3}}, + {name: "push 1 byte 0x04", data: []byte{0x04}, expected: []byte{txscript.OP_4}}, + {name: "push 1 byte 0x05", data: []byte{0x05}, expected: []byte{txscript.OP_5}}, + {name: "push 1 byte 0x06", data: []byte{0x06}, expected: []byte{txscript.OP_6}}, + {name: "push 1 byte 0x07", data: []byte{0x07}, expected: []byte{txscript.OP_7}}, + {name: "push 1 byte 0x08", data: []byte{0x08}, expected: []byte{txscript.OP_8}}, + {name: "push 1 byte 0x09", data: []byte{0x09}, expected: []byte{txscript.OP_9}}, + {name: "push 1 byte 0x0a", data: []byte{0x0a}, expected: []byte{txscript.OP_10}}, + {name: "push 1 byte 0x0b", data: []byte{0x0b}, expected: []byte{txscript.OP_11}}, + {name: "push 1 byte 0x0c", data: []byte{0x0c}, expected: []byte{txscript.OP_12}}, + {name: "push 1 byte 0x0d", data: []byte{0x0d}, expected: []byte{txscript.OP_13}}, + {name: "push 1 byte 0x0e", data: []byte{0x0e}, expected: []byte{txscript.OP_14}}, + {name: "push 1 byte 0x0f", data: []byte{0x0f}, expected: []byte{txscript.OP_15}}, + {name: "push 1 byte 0x10", data: []byte{0x10}, expected: []byte{txscript.OP_16}}, + + // BIP0062: Pushing the byte 0x81 must use OP_1NEGATE. + {name: "push 1 byte 0x81", data: []byte{0x81}, expected: []byte{txscript.OP_1NEGATE}}, + + // BIP0062: Pushing any other byte sequence up to 75 bytes must + // use the normal data push (opcode byte n, with n the number of + // bytes, followed n bytes of data being pushed). + {name: "push 1 byte 0x11", data: []byte{0x11}, expected: []byte{txscript.OP_DATA_1, 0x11}}, + {name: "push 1 byte 0x80", data: []byte{0x80}, expected: []byte{txscript.OP_DATA_1, 0x80}}, + {name: "push 1 byte 0x82", data: []byte{0x82}, expected: []byte{txscript.OP_DATA_1, 0x82}}, + {name: "push 1 byte 0xff", data: []byte{0xff}, expected: []byte{txscript.OP_DATA_1, 0xff}}, + { + name: "push data len 17", + data: bytes.Repeat([]byte{0x49}, 17), + expected: append([]byte{txscript.OP_DATA_17}, bytes.Repeat([]byte{0x49}, 17)...), + }, + { + name: "push data len 75", + data: bytes.Repeat([]byte{0x49}, 75), + expected: append([]byte{txscript.OP_DATA_75}, bytes.Repeat([]byte{0x49}, 75)...), + }, + + // BIP0062: Pushing 76 to 255 bytes must use OP_PUSHDATA1. + { + name: "push data len 76", + data: bytes.Repeat([]byte{0x49}, 76), + expected: append([]byte{txscript.OP_PUSHDATA1, 76}, bytes.Repeat([]byte{0x49}, 76)...), + }, + { + name: "push data len 255", + data: bytes.Repeat([]byte{0x49}, 255), + expected: append([]byte{txscript.OP_PUSHDATA1, 255}, bytes.Repeat([]byte{0x49}, 255)...), + }, + + // BIP0062: Pushing 256 to 520 bytes must use OP_PUSHDATA2. + { + name: "push data len 256", + data: bytes.Repeat([]byte{0x49}, 256), + expected: append([]byte{txscript.OP_PUSHDATA2, 0, 1}, bytes.Repeat([]byte{0x49}, 256)...), + }, + { + name: "push data len 520", + data: bytes.Repeat([]byte{0x49}, 520), + expected: append([]byte{txscript.OP_PUSHDATA2, 0x08, 0x02}, bytes.Repeat([]byte{0x49}, 520)...), + }, + + // BIP0062: OP_PUSHDATA4 can never be used, as pushes over 520 + // bytes are not allowed, and those below can be done using + // other operators. + { + name: "push data len 521", + data: bytes.Repeat([]byte{0x49}, 521), + expected: nil, + }, + { + name: "push data len 32767 (canonical)", + data: bytes.Repeat([]byte{0x49}, 32767), + expected: nil, + }, + { + name: "push data len 65536 (canonical)", + data: bytes.Repeat([]byte{0x49}, 65536), + expected: nil, + }, + + // Additional tests for the PushFullData function that + // intentionally allows data pushes to exceed the limit for + // regression testing purposes. + + // 3-byte data push via OP_PUSHDATA_2. + { + name: "push data len 32767 (non-canonical)", + data: bytes.Repeat([]byte{0x49}, 32767), + expected: append([]byte{txscript.OP_PUSHDATA2, 255, 127}, bytes.Repeat([]byte{0x49}, 32767)...), + useFull: true, + }, + + // 5-byte data push via OP_PUSHDATA_4. + { + name: "push data len 65536 (non-canonical)", + data: bytes.Repeat([]byte{0x49}, 65536), + expected: append([]byte{txscript.OP_PUSHDATA4, 0, 0, 1, 0}, bytes.Repeat([]byte{0x49}, 65536)...), + useFull: true, + }, + } + + builder := txscript.NewScriptBuilder() + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + if !test.useFull { + builder.Reset().AddData(test.data) + } else { + builder.Reset().AddFullData(test.data) + } + result, _ := builder.Script() + if !bytes.Equal(result, test.expected) { + t.Errorf("ScriptBuilder.AddData #%d (%s) wrong result\n"+ + "got: %x\nwant: %x", i, test.name, result, + test.expected) + continue + } + } +} + +// TestExceedMaxScriptSize ensures that all of the functions that can be used +// to add data to a script don't allow the script to exceed the max allowed +// size. +func TestExceedMaxScriptSize(t *testing.T) { + t.Parallel() + + // Start off by constructing a max size script. + maxScriptSize := txscript.TstMaxScriptSize + builder := txscript.NewScriptBuilder() + builder.Reset().AddFullData(make([]byte, maxScriptSize-3)) + origScript, err := builder.Script() + if err != nil { + t.Fatalf("Unexpected error for max size script: %v", err) + } + + // Ensure adding data that would exceed the maximum size of the script + // does not add the data. + script, err := builder.AddData([]byte{0x00}).Script() + if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil { + t.Fatalf("ScriptBuilder.AddData allowed exceeding max script "+ + "size: %v", len(script)) + } + if !bytes.Equal(script, origScript) { + t.Fatalf("ScriptBuilder.AddData unexpected modified script - "+ + "got len %d, want len %d", len(script), len(origScript)) + } + + // Ensure adding an opcode that would exceed the maximum size of the + // script does not add the data. + builder.Reset().AddFullData(make([]byte, maxScriptSize-3)) + script, err = builder.AddOp(txscript.OP_0).Script() + if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil { + t.Fatalf("ScriptBuilder.AddOp unexpected modified script - "+ + "got len %d, want len %d", len(script), len(origScript)) + } + if !bytes.Equal(script, origScript) { + t.Fatalf("ScriptBuilder.AddOp unexpected modified script - "+ + "got len %d, want len %d", len(script), len(origScript)) + } + + // Ensure adding an integer that would exceed the maximum size of the + // script does not add the data. + builder.Reset().AddFullData(make([]byte, maxScriptSize-3)) + script, err = builder.AddInt64(0).Script() + if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil { + t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+ + "got len %d, want len %d", len(script), len(origScript)) + } + if !bytes.Equal(script, origScript) { + t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+ + "got len %d, want len %d", len(script), len(origScript)) + } +} + +// TestErroredScript ensures that all of the functions that can be used to add +// data to a script don't modify the script once an error has happened. +func TestErroredScript(t *testing.T) { + t.Parallel() + + // Start off by constructing a near max size script that has enough + // space left to add each data type without an error and force an + // initial error condition. + maxScriptSize := txscript.TstMaxScriptSize + builder := txscript.NewScriptBuilder() + builder.Reset().AddFullData(make([]byte, maxScriptSize-8)) + origScript, err := builder.Script() + if err != nil { + t.Fatalf("ScriptBuilder.AddFullData unexpected error: %v", err) + } + script, err := builder.AddData([]byte{0x00, 0x00, 0x00, 0x00, 0x00}).Script() + if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil { + t.Fatalf("ScriptBuilder.AddData allowed exceeding max script "+ + "size: %v", len(script)) + } + if !bytes.Equal(script, origScript) { + t.Fatalf("ScriptBuilder.AddData unexpected modified script - "+ + "got len %d, want len %d", len(script), len(origScript)) + } + + // Ensure adding data, even using the non-canonical path, to a script + // that has errored doesn't succeed. + script, err = builder.AddFullData([]byte{0x00}).Script() + if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil { + t.Fatal("ScriptBuilder.AddFullData succeeded on errored script") + } + if !bytes.Equal(script, origScript) { + t.Fatalf("ScriptBuilder.AddFullData unexpected modified "+ + "script - got len %d, want len %d", len(script), + len(origScript)) + } + + // Ensure adding data to a script that has errored doesn't succeed. + script, err = builder.AddData([]byte{0x00}).Script() + if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil { + t.Fatal("ScriptBuilder.AddData succeeded on errored script") + } + if !bytes.Equal(script, origScript) { + t.Fatalf("ScriptBuilder.AddData unexpected modified "+ + "script - got len %d, want len %d", len(script), + len(origScript)) + } + + // Ensure adding an opcode to a script that has errored doesn't succeed. + script, err = builder.AddOp(txscript.OP_0).Script() + if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil { + t.Fatal("ScriptBuilder.AddOp succeeded on errored script") + } + if !bytes.Equal(script, origScript) { + t.Fatalf("ScriptBuilder.AddOp unexpected modified script - "+ + "got len %d, want len %d", len(script), len(origScript)) + } + + // Ensure adding an integer to a script that has errored doesn't + // succeed. + script, err = builder.AddInt64(0).Script() + if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil { + t.Fatal("ScriptBuilder.AddInt64 succeeded on errored script") + } + if !bytes.Equal(script, origScript) { + t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+ + "got len %d, want len %d", len(script), len(origScript)) + } + + // Ensure the error has a message set. + if err.Error() == "" { + t.Fatal("ErrScriptNotCanonical.Error does not have any text") + } +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/scriptnum.go b/vendor/github.com/btcsuite/btcd/txscript/scriptnum.go new file mode 100644 index 0000000000000000000000000000000000000000..9d11ea219990f833740f89b92e69694d96a52923 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/scriptnum.go @@ -0,0 +1,216 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +const ( + maxInt32 = 1<<31 - 1 + minInt32 = -1 << 31 + + // defaultScriptNumLen is the default number of bytes + // data being interpreted as an integer may be. + defaultScriptNumLen = 4 +) + +// scriptNum represents a numeric value used in the scripting engine with +// special handling to deal with the subtle semantics required by consensus. +// +// All numbers are stored on the data and alternate stacks encoded as little +// endian with a sign bit. All numeric opcodes such as OP_ADD, OP_SUB, +// and OP_MUL, are only allowed to operate on 4-byte integers in the range +// [-2^31 + 1, 2^31 - 1], however the results of numeric operations may overflow +// and remain valid so long as they are not used as inputs to other numeric +// operations or otherwise interpreted as an integer. +// +// For example, it is possible for OP_ADD to have 2^31 - 1 for its two operands +// resulting 2^32 - 2, which overflows, but is still pushed to the stack as the +// result of the addition. That value can then be used as input to OP_VERIFY +// which will succeed because the data is being interpreted as a boolean. +// However, if that same value were to be used as input to another numeric +// opcode, such as OP_SUB, it must fail. +// +// This type handles the aforementioned requirements by storing all numeric +// operation results as an int64 to handle overflow and provides the Bytes +// method to get the serialized representation (including values that overflow). +// +// Then, whenever data is interpreted as an integer, it is converted to this +// type by using the makeScriptNum function which will return an error if the +// number is out of range or not minimally encoded depending on parameters. +// Since all numeric opcodes involve pulling data from the stack and +// interpreting it as an integer, it provides the required behavior. +type scriptNum int64 + +// checkMinimalDataEncoding returns whether or not the passed byte array adheres +// to the minimal encoding requirements. +func checkMinimalDataEncoding(v []byte) error { + if len(v) == 0 { + return nil + } + + // Check that the number is encoded with the minimum possible + // number of bytes. + // + // If the most-significant-byte - excluding the sign bit - is zero + // then we're not minimal. Note how this test also rejects the + // negative-zero encoding, [0x80]. + if v[len(v)-1]&0x7f == 0 { + // One exception: if there's more than one byte and the most + // significant bit of the second-most-significant-byte is set + // it would conflict with the sign bit. An example of this case + // is +-255, which encode to 0xff00 and 0xff80 respectively. + // (big-endian). + if len(v) == 1 || v[len(v)-2]&0x80 == 0 { + return ErrStackMinimalData + } + } + + return nil +} + +// Bytes returns the number serialized as a little endian with a sign bit. +// +// Example encodings: +// 127 -> [0x7f] +// -127 -> [0xff] +// 128 -> [0x80 0x00] +// -128 -> [0x80 0x80] +// 129 -> [0x81 0x00] +// -129 -> [0x81 0x80] +// 256 -> [0x00 0x01] +// -256 -> [0x00 0x81] +// 32767 -> [0xff 0x7f] +// -32767 -> [0xff 0xff] +// 32768 -> [0x00 0x80 0x00] +// -32768 -> [0x00 0x80 0x80] +func (n scriptNum) Bytes() []byte { + // Zero encodes as an empty byte slice. + if n == 0 { + return nil + } + + // Take the absolute value and keep track of whether it was originally + // negative. + isNegative := n < 0 + if isNegative { + n = -n + } + + // Encode to little endian. The maximum number of encoded bytes is 9 + // (8 bytes for max int64 plus a potential byte for sign extension). + result := make([]byte, 0, 9) + for n > 0 { + result = append(result, byte(n&0xff)) + n >>= 8 + } + + // When the most significant byte already has the high bit set, an + // additional high byte is required to indicate whether the number is + // negative or positive. The additional byte is removed when converting + // back to an integral and its high bit is used to denote the sign. + // + // Otherwise, when the most significant byte does not already have the + // high bit set, use it to indicate the value is negative, if needed. + if result[len(result)-1]&0x80 != 0 { + extraByte := byte(0x00) + if isNegative { + extraByte = 0x80 + } + result = append(result, extraByte) + + } else if isNegative { + result[len(result)-1] |= 0x80 + } + + return result +} + +// Int32 returns the script number clamped to a valid int32. That is to say +// when the script number is higher than the max allowed int32, the max int32 +// value is returned and vice versa for the minimum value. Note that this +// behavior is different from a simple int32 cast because that truncates +// and the consensus rules dictate numbers which are directly cast to ints +// provide this behavior. +// +// In practice, for most opcodes, the number should never be out of range since +// it will have been created with makeScriptNum using the defaultScriptLen +// value, which rejects them. In case something in the future ends up calling +// this function against the result of some arithmetic, which IS allowed to be +// out of range before being reinterpreted as an integer, this will provide the +// correct behavior. +func (n scriptNum) Int32() int32 { + if n > maxInt32 { + return maxInt32 + } + + if n < minInt32 { + return minInt32 + } + + return int32(n) +} + +// makeScriptNum interprets the passed serialized bytes as an encoded integer +// and returns the result as a script number. +// +// Since the consensus rules dictate that serialized bytes interpreted as ints +// are only allowed to be in the range determined by a maximum number of bytes, +// on a per opcode basis, an error will be returned when the provided bytes +// would result in a number outside of that range. In particular, the range for +// the vast majority of opcodes dealing with numeric values are limited to 4 +// bytes and therefore will pass that value to this function resulting in an +// allowed range of [-2^31 + 1, 2^31 - 1]. +// +// The requireMinimal flag causes an error to be returned if additional checks +// on the encoding determine it is not represented with the smallest possible +// number of bytes or is the negative 0 encoding, [0x80]. For example, consider +// the number 127. It could be encoded as [0x7f], [0x7f 0x00], +// [0x7f 0x00 0x00 ...], etc. All forms except [0x7f] will return an error with +// requireMinimal enabled. +// +// The scriptNumLen is the maximum number of bytes the encoded value can be +// before an ErrStackNumberTooBig is returned. This effectively limits the +// range of allowed values. +// WARNING: Great care should be taken if passing a value larger than +// defaultScriptNumLen, which could lead to addition and multiplication +// overflows. +// +// See the Bytes function documentation for example encodings. +func makeScriptNum(v []byte, requireMinimal bool, scriptNumLen int) (scriptNum, error) { + // Interpreting data requires that it is not larger than + // the the passed scriptNumLen value. + if len(v) > scriptNumLen { + return 0, ErrStackNumberTooBig + } + + // Enforce minimal encoded if requested. + if requireMinimal { + if err := checkMinimalDataEncoding(v); err != nil { + return 0, err + } + } + + // Zero is encoded as an empty byte slice. + if len(v) == 0 { + return 0, nil + } + + // Decode from little endian. + var result int64 + for i, val := range v { + result |= int64(val) << uint8(8*i) + } + + // When the most significant byte of the input bytes has the sign bit + // set, the result is negative. So, remove the sign bit from the result + // and make it negative. + if v[len(v)-1]&0x80 != 0 { + // The maximum length of v has already been determined to be 4 + // above, so uint8 is enough to cover the max possible shift + // value of 24. + result &= ^(int64(0x80) << uint8(8*(len(v)-1))) + return scriptNum(-result), nil + } + + return scriptNum(result), nil +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/scriptnum_test.go b/vendor/github.com/btcsuite/btcd/txscript/scriptnum_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ee7cab566a4a9f8fcbd558290cda0f3144955736 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/scriptnum_test.go @@ -0,0 +1,268 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "bytes" + "encoding/hex" + "testing" +) + +// hexToBytes converts the passed hex string into bytes and will panic if there +// is an error. This is only provided for the hard-coded constants so errors in +// the source code can be detected. It will only (and must only) be called with +// hard-coded values. +func hexToBytes(s string) []byte { + b, err := hex.DecodeString(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + return b +} + +// TestScriptNumBytes ensures that converting from integral script numbers to +// byte representations works as expected. +func TestScriptNumBytes(t *testing.T) { + t.Parallel() + + tests := []struct { + num scriptNum + serialized []byte + }{ + {0, nil}, + {1, hexToBytes("01")}, + {-1, hexToBytes("81")}, + {127, hexToBytes("7f")}, + {-127, hexToBytes("ff")}, + {128, hexToBytes("8000")}, + {-128, hexToBytes("8080")}, + {129, hexToBytes("8100")}, + {-129, hexToBytes("8180")}, + {256, hexToBytes("0001")}, + {-256, hexToBytes("0081")}, + {32767, hexToBytes("ff7f")}, + {-32767, hexToBytes("ffff")}, + {32768, hexToBytes("008000")}, + {-32768, hexToBytes("008080")}, + {65535, hexToBytes("ffff00")}, + {-65535, hexToBytes("ffff80")}, + {524288, hexToBytes("000008")}, + {-524288, hexToBytes("000088")}, + {7340032, hexToBytes("000070")}, + {-7340032, hexToBytes("0000f0")}, + {8388608, hexToBytes("00008000")}, + {-8388608, hexToBytes("00008080")}, + {2147483647, hexToBytes("ffffff7f")}, + {-2147483647, hexToBytes("ffffffff")}, + + // Values that are out of range for data that is interpreted as + // numbers, but are allowed as the result of numeric operations. + {2147483648, hexToBytes("0000008000")}, + {-2147483648, hexToBytes("0000008080")}, + {2415919104, hexToBytes("0000009000")}, + {-2415919104, hexToBytes("0000009080")}, + {4294967295, hexToBytes("ffffffff00")}, + {-4294967295, hexToBytes("ffffffff80")}, + {4294967296, hexToBytes("0000000001")}, + {-4294967296, hexToBytes("0000000081")}, + {281474976710655, hexToBytes("ffffffffffff00")}, + {-281474976710655, hexToBytes("ffffffffffff80")}, + {72057594037927935, hexToBytes("ffffffffffffff00")}, + {-72057594037927935, hexToBytes("ffffffffffffff80")}, + {9223372036854775807, hexToBytes("ffffffffffffff7f")}, + {-9223372036854775807, hexToBytes("ffffffffffffffff")}, + } + + for _, test := range tests { + gotBytes := test.num.Bytes() + if !bytes.Equal(gotBytes, test.serialized) { + t.Errorf("Bytes: did not get expected bytes for %d - "+ + "got %x, want %x", test.num, gotBytes, + test.serialized) + continue + } + } +} + +// TestMakeScriptNum ensures that converting from byte representations to +// integral script numbers works as expected. +func TestMakeScriptNum(t *testing.T) { + t.Parallel() + + tests := []struct { + serialized []byte + num scriptNum + numLen int + minimalEncoding bool + err error + }{ + // Minimal encoding must reject negative 0. + {hexToBytes("80"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, + + // Minimally encoded valid values with minimal encoding flag. + // Should not error and return expected integral number. + {nil, 0, defaultScriptNumLen, true, nil}, + {hexToBytes("01"), 1, defaultScriptNumLen, true, nil}, + {hexToBytes("81"), -1, defaultScriptNumLen, true, nil}, + {hexToBytes("7f"), 127, defaultScriptNumLen, true, nil}, + {hexToBytes("ff"), -127, defaultScriptNumLen, true, nil}, + {hexToBytes("8000"), 128, defaultScriptNumLen, true, nil}, + {hexToBytes("8080"), -128, defaultScriptNumLen, true, nil}, + {hexToBytes("8100"), 129, defaultScriptNumLen, true, nil}, + {hexToBytes("8180"), -129, defaultScriptNumLen, true, nil}, + {hexToBytes("0001"), 256, defaultScriptNumLen, true, nil}, + {hexToBytes("0081"), -256, defaultScriptNumLen, true, nil}, + {hexToBytes("ff7f"), 32767, defaultScriptNumLen, true, nil}, + {hexToBytes("ffff"), -32767, defaultScriptNumLen, true, nil}, + {hexToBytes("008000"), 32768, defaultScriptNumLen, true, nil}, + {hexToBytes("008080"), -32768, defaultScriptNumLen, true, nil}, + {hexToBytes("ffff00"), 65535, defaultScriptNumLen, true, nil}, + {hexToBytes("ffff80"), -65535, defaultScriptNumLen, true, nil}, + {hexToBytes("000008"), 524288, defaultScriptNumLen, true, nil}, + {hexToBytes("000088"), -524288, defaultScriptNumLen, true, nil}, + {hexToBytes("000070"), 7340032, defaultScriptNumLen, true, nil}, + {hexToBytes("0000f0"), -7340032, defaultScriptNumLen, true, nil}, + {hexToBytes("00008000"), 8388608, defaultScriptNumLen, true, nil}, + {hexToBytes("00008080"), -8388608, defaultScriptNumLen, true, nil}, + {hexToBytes("ffffff7f"), 2147483647, defaultScriptNumLen, true, nil}, + {hexToBytes("ffffffff"), -2147483647, defaultScriptNumLen, true, nil}, + {hexToBytes("ffffffff7f"), 549755813887, 5, true, nil}, + {hexToBytes("ffffffffff"), -549755813887, 5, true, nil}, + {hexToBytes("ffffffffffffff7f"), 9223372036854775807, 8, true, nil}, + {hexToBytes("ffffffffffffffff"), -9223372036854775807, 8, true, nil}, + {hexToBytes("ffffffffffffffff7f"), -1, 9, true, nil}, + {hexToBytes("ffffffffffffffffff"), 1, 9, true, nil}, + {hexToBytes("ffffffffffffffffff7f"), -1, 10, true, nil}, + {hexToBytes("ffffffffffffffffffff"), 1, 10, true, nil}, + + // Minimally encoded values that are out of range for data that + // is interpreted as script numbers with the minimal encoding + // flag set. Should error and return 0. + {hexToBytes("0000008000"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("0000008080"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("0000009000"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("0000009080"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("ffffffff00"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("ffffffff80"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("0000000001"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("0000000081"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("ffffffffffff00"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("ffffffffffff80"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("ffffffffffffff00"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("ffffffffffffff80"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("ffffffffffffff7f"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + {hexToBytes("ffffffffffffffff"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig}, + + // Non-minimally encoded, but otherwise valid values with + // minimal encoding flag. Should error and return 0. + {hexToBytes("00"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 0 + {hexToBytes("0100"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 1 + {hexToBytes("7f00"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 127 + {hexToBytes("800000"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 128 + {hexToBytes("810000"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 129 + {hexToBytes("000100"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 256 + {hexToBytes("ff7f00"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 32767 + {hexToBytes("00800000"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 32768 + {hexToBytes("ffff0000"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 65535 + {hexToBytes("00000800"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 524288 + {hexToBytes("00007000"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 7340032 + {hexToBytes("0009000100"), 0, 5, true, ErrStackMinimalData}, // 16779520 + + // Non-minimally encoded, but otherwise valid values without + // minimal encoding flag. Should not error and return expected + // integral number. + {hexToBytes("00"), 0, defaultScriptNumLen, false, nil}, + {hexToBytes("0100"), 1, defaultScriptNumLen, false, nil}, + {hexToBytes("7f00"), 127, defaultScriptNumLen, false, nil}, + {hexToBytes("800000"), 128, defaultScriptNumLen, false, nil}, + {hexToBytes("810000"), 129, defaultScriptNumLen, false, nil}, + {hexToBytes("000100"), 256, defaultScriptNumLen, false, nil}, + {hexToBytes("ff7f00"), 32767, defaultScriptNumLen, false, nil}, + {hexToBytes("00800000"), 32768, defaultScriptNumLen, false, nil}, + {hexToBytes("ffff0000"), 65535, defaultScriptNumLen, false, nil}, + {hexToBytes("00000800"), 524288, defaultScriptNumLen, false, nil}, + {hexToBytes("00007000"), 7340032, defaultScriptNumLen, false, nil}, + {hexToBytes("0009000100"), 16779520, 5, false, nil}, + } + + for _, test := range tests { + gotNum, err := makeScriptNum(test.serialized, test.minimalEncoding, + test.numLen) + if err != test.err { + t.Errorf("makeScriptNum: did not received expected "+ + "error for %x - got %v, want %v", + test.serialized, err, test.err) + continue + } + + if gotNum != test.num { + t.Errorf("makeScriptNum: did not get expected number "+ + "for %x - got %d, want %d", test.serialized, + gotNum, test.num) + continue + } + } +} + +// TestScriptNumInt32 ensures that the Int32 function on script number behaves +// as expected. +func TestScriptNumInt32(t *testing.T) { + t.Parallel() + + tests := []struct { + in scriptNum + want int32 + }{ + // Values inside the valid int32 range are just the values + // themselves cast to an int32. + {0, 0}, + {1, 1}, + {-1, -1}, + {127, 127}, + {-127, -127}, + {128, 128}, + {-128, -128}, + {129, 129}, + {-129, -129}, + {256, 256}, + {-256, -256}, + {32767, 32767}, + {-32767, -32767}, + {32768, 32768}, + {-32768, -32768}, + {65535, 65535}, + {-65535, -65535}, + {524288, 524288}, + {-524288, -524288}, + {7340032, 7340032}, + {-7340032, -7340032}, + {8388608, 8388608}, + {-8388608, -8388608}, + {2147483647, 2147483647}, + {-2147483647, -2147483647}, + {-2147483648, -2147483648}, + + // Values outside of the valid int32 range are limited to int32. + {2147483648, 2147483647}, + {-2147483649, -2147483648}, + {1152921504606846975, 2147483647}, + {-1152921504606846975, -2147483648}, + {2305843009213693951, 2147483647}, + {-2305843009213693951, -2147483648}, + {4611686018427387903, 2147483647}, + {-4611686018427387903, -2147483648}, + {9223372036854775807, 2147483647}, + {-9223372036854775808, -2147483648}, + } + + for _, test := range tests { + got := test.in.Int32() + if got != test.want { + t.Errorf("Int32: did not get expected value for %d - "+ + "got %d, want %d", test.in, got, test.want) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/sigcache.go b/vendor/github.com/btcsuite/btcd/txscript/sigcache.go new file mode 100644 index 0000000000000000000000000000000000000000..028a267cc16901535c341bf933edf238e72ede0d --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/sigcache.go @@ -0,0 +1,102 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "sync" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/wire" +) + +// sigCacheEntry represents an entry in the SigCache. Entries within the +// SigCache are keyed according to the sigHash of the signature. In the +// scenario of a cache-hit (according to the sigHash), an additional comparison +// of the signature, and public key will be executed in order to ensure a complete +// match. In the occasion that two sigHashes collide, the newer sigHash will +// simply overwrite the existing entry. +type sigCacheEntry struct { + sig *btcec.Signature + pubKey *btcec.PublicKey +} + +// SigCache implements an ECDSA signature verification cache with a randomized +// entry eviction policy. Only valid signatures will be added to the cache. The +// benefits of SigCache are two fold. Firstly, usage of SigCache mitigates a DoS +// attack wherein an attack causes a victim's client to hang due to worst-case +// behavior triggered while processing attacker crafted invalid transactions. A +// detailed description of the mitigated DoS attack can be found here: +// https://bitslog.wordpress.com/2013/01/23/fixed-bitcoin-vulnerability-explanation-why-the-signature-cache-is-a-dos-protection/. +// Secondly, usage of the SigCache introduces a signature verification +// optimization which speeds up the validation of transactions within a block, +// if they've already been seen and verified within the mempool. +type SigCache struct { + sync.RWMutex + validSigs map[wire.ShaHash]sigCacheEntry + maxEntries uint +} + +// NewSigCache creates and initializes a new instance of SigCache. Its sole +// parameter 'maxEntries' represents the maximum number of entries allowed to +// exist in the SigCache at any particular moment. Random entries are evicted +// to make room for new entries that would cause the number of entries in the +// cache to exceed the max. +func NewSigCache(maxEntries uint) *SigCache { + return &SigCache{ + validSigs: make(map[wire.ShaHash]sigCacheEntry, maxEntries), + maxEntries: maxEntries, + } +} + +// Exists returns true if an existing entry of 'sig' over 'sigHash' for public +// key 'pubKey' is found within the SigCache. Otherwise, false is returned. +// +// NOTE: This function is safe for concurrent access. Readers won't be blocked +// unless there exists a writer, adding an entry to the SigCache. +func (s *SigCache) Exists(sigHash wire.ShaHash, sig *btcec.Signature, pubKey *btcec.PublicKey) bool { + s.RLock() + defer s.RUnlock() + + if entry, ok := s.validSigs[sigHash]; ok { + return entry.pubKey.IsEqual(pubKey) && entry.sig.IsEqual(sig) + } + + return false +} + +// Add adds an entry for a signature over 'sigHash' under public key 'pubKey' +// to the signature cache. In the event that the SigCache is 'full', an +// existing entry is randomly chosen to be evicted in order to make space for +// the new entry. +// +// NOTE: This function is safe for concurrent access. Writers will block +// simultaneous readers until function execution has concluded. +func (s *SigCache) Add(sigHash wire.ShaHash, sig *btcec.Signature, pubKey *btcec.PublicKey) { + s.Lock() + defer s.Unlock() + + if s.maxEntries <= 0 { + return + } + + // If adding this new entry will put us over the max number of allowed + // entries, then evict an entry. + if uint(len(s.validSigs)+1) > s.maxEntries { + // Remove a random entry from the map relaying on the random + // starting point of Go's map iteration. It's worth noting that + // the random iteration starting point is not 100% guaranteed + // by the spec, however most Go compilers support it. + // Ultimately, the iteration order isn't important here because + // in order to manipulate which items are evicted, an adversary + // would need to be able to execute preimage attacks on the + // hashing function in order to start eviction at a specific + // entry. + for sigEntry := range s.validSigs { + delete(s.validSigs, sigEntry) + break + } + } + s.validSigs[sigHash] = sigCacheEntry{sig, pubKey} +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/sigcache_test.go b/vendor/github.com/btcsuite/btcd/txscript/sigcache_test.go new file mode 100644 index 0000000000000000000000000000000000000000..afe23de5ce6e5d941f1c7da4a0ddcb92ddb1c1b8 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/sigcache_test.go @@ -0,0 +1,140 @@ +// Copyright (c) 2015 The btcsuite 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" + "testing" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/wire" +) + +// genRandomSig returns a random message, a signature of the message under the +// public key and the public key. This function is used to generate randomized +// test data. +func genRandomSig() (*wire.ShaHash, *btcec.Signature, *btcec.PublicKey, error) { + privKey, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + return nil, nil, nil, err + } + + var msgHash wire.ShaHash + if _, err := rand.Read(msgHash[:]); err != nil { + return nil, nil, nil, err + } + + sig, err := privKey.Sign(msgHash[:]) + if err != nil { + return nil, nil, nil, err + } + + return &msgHash, sig, privKey.PubKey(), nil +} + +// TestSigCacheAddExists tests the ability to add, and later check the +// existence of a signature triplet in the signature cache. +func TestSigCacheAddExists(t *testing.T) { + sigCache := NewSigCache(200) + + // Generate a random sigCache entry triplet. + msg1, sig1, key1, err := genRandomSig() + if err != nil { + t.Errorf("unable to generate random signature test data") + } + + // Add the triplet to the signature cache. + sigCache.Add(*msg1, sig1, key1) + + // The previously added triplet should now be found within the sigcache. + sig1Copy, _ := btcec.ParseSignature(sig1.Serialize(), btcec.S256()) + key1Copy, _ := btcec.ParsePubKey(key1.SerializeCompressed(), btcec.S256()) + if !sigCache.Exists(*msg1, sig1Copy, key1Copy) { + t.Errorf("previously added item not found in signature cache") + } +} + +// TestSigCacheAddEvictEntry tests the eviction case where a new signature +// triplet is added to a full signature cache which should trigger randomized +// eviction, followed by adding the new element to the cache. +func TestSigCacheAddEvictEntry(t *testing.T) { + // Create a sigcache that can hold up to 100 entries. + sigCacheSize := uint(100) + sigCache := NewSigCache(sigCacheSize) + + // Fill the sigcache up with some random sig triplets. + for i := uint(0); i < sigCacheSize; i++ { + msg, sig, key, err := genRandomSig() + if err != nil { + t.Fatalf("unable to generate random signature test data") + } + + sigCache.Add(*msg, sig, key) + + sigCopy, _ := btcec.ParseSignature(sig.Serialize(), btcec.S256()) + keyCopy, _ := btcec.ParsePubKey(key.SerializeCompressed(), btcec.S256()) + if !sigCache.Exists(*msg, sigCopy, keyCopy) { + t.Errorf("previously added item not found in signature" + + "cache") + } + } + + // The sigcache should now have sigCacheSize entries within it. + if uint(len(sigCache.validSigs)) != sigCacheSize { + t.Fatalf("sigcache should now have %v entries, instead it has %v", + sigCacheSize, len(sigCache.validSigs)) + } + + // Add a new entry, this should cause eviction of a randomly chosen + // previous entry. + msgNew, sigNew, keyNew, err := genRandomSig() + if err != nil { + t.Fatalf("unable to generate random signature test data") + } + sigCache.Add(*msgNew, sigNew, keyNew) + + // The sigcache should still have sigCache entries. + if uint(len(sigCache.validSigs)) != sigCacheSize { + t.Fatalf("sigcache should now have %v entries, instead it has %v", + sigCacheSize, len(sigCache.validSigs)) + } + + // The entry added above should be found within the sigcache. + sigNewCopy, _ := btcec.ParseSignature(sigNew.Serialize(), btcec.S256()) + keyNewCopy, _ := btcec.ParsePubKey(keyNew.SerializeCompressed(), btcec.S256()) + if !sigCache.Exists(*msgNew, sigNewCopy, keyNewCopy) { + t.Fatalf("previously added item not found in signature cache") + } +} + +// TestSigCacheAddMaxEntriesZeroOrNegative tests that if a sigCache is created +// with a max size <= 0, then no entries are added to the sigcache at all. +func TestSigCacheAddMaxEntriesZeroOrNegative(t *testing.T) { + // Create a sigcache that can hold up to 0 entries. + sigCache := NewSigCache(0) + + // Generate a random sigCache entry triplet. + msg1, sig1, key1, err := genRandomSig() + if err != nil { + t.Errorf("unable to generate random signature test data") + } + + // Add the triplet to the signature cache. + sigCache.Add(*msg1, sig1, key1) + + // The generated triplet should not be found. + sig1Copy, _ := btcec.ParseSignature(sig1.Serialize(), btcec.S256()) + key1Copy, _ := btcec.ParsePubKey(key1.SerializeCompressed(), btcec.S256()) + if sigCache.Exists(*msg1, sig1Copy, key1Copy) { + t.Errorf("previously added signature found in sigcache, but" + + "shouldn't have been") + } + + // There shouldn't be any entries in the sigCache. + if len(sigCache.validSigs) != 0 { + t.Errorf("%v items found in sigcache, no items should have"+ + "been added", len(sigCache.validSigs)) + } +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/sign.go b/vendor/github.com/btcsuite/btcd/txscript/sign.go new file mode 100644 index 0000000000000000000000000000000000000000..e810bd0cd4d3130d724069ab3222c53071d0354e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/sign.go @@ -0,0 +1,411 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "errors" + "fmt" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// RawTxInSignature returns the serialized ECDSA signature for the input idx of +// the given transaction, with hashType appended to it. +func RawTxInSignature(tx *wire.MsgTx, idx int, subScript []byte, + hashType SigHashType, key *btcec.PrivateKey) ([]byte, error) { + + parsedScript, err := parseScript(subScript) + if err != nil { + return nil, fmt.Errorf("cannot parse output script: %v", err) + } + hash := calcSignatureHash(parsedScript, hashType, tx, idx) + signature, err := key.Sign(hash) + if err != nil { + return nil, fmt.Errorf("cannot sign tx input: %s", err) + } + + return append(signature.Serialize(), byte(hashType)), nil +} + +// SignatureScript creates an input signature script for tx to spend BTC sent +// from a previous output to the owner of privKey. tx must include all +// transaction inputs and outputs, however txin scripts are allowed to be filled +// or empty. The returned script is calculated to be used as the idx'th txin +// sigscript for tx. subscript is the PkScript of the previous output being used +// as the idx'th input. privKey is serialized in either a compressed or +// uncompressed format based on compress. This format must match the same format +// used to generate the payment address, or the script validation will fail. +func SignatureScript(tx *wire.MsgTx, idx int, subscript []byte, hashType SigHashType, privKey *btcec.PrivateKey, compress bool) ([]byte, error) { + sig, err := RawTxInSignature(tx, idx, subscript, hashType, privKey) + if err != nil { + return nil, err + } + + pk := (*btcec.PublicKey)(&privKey.PublicKey) + var pkData []byte + if compress { + pkData = pk.SerializeCompressed() + } else { + pkData = pk.SerializeUncompressed() + } + + return NewScriptBuilder().AddData(sig).AddData(pkData).Script() +} + +func p2pkSignatureScript(tx *wire.MsgTx, idx int, subScript []byte, hashType SigHashType, privKey *btcec.PrivateKey) ([]byte, error) { + sig, err := RawTxInSignature(tx, idx, subScript, hashType, privKey) + if err != nil { + return nil, err + } + + return NewScriptBuilder().AddData(sig).Script() +} + +// signMultiSig signs as many of the outputs in the provided multisig script as +// possible. It returns the generated script and a boolean if the script fulfils +// the contract (i.e. nrequired signatures are provided). Since it is arguably +// legal to not be able to sign any of the outputs, no error is returned. +func signMultiSig(tx *wire.MsgTx, idx int, subScript []byte, hashType SigHashType, + addresses []btcutil.Address, nRequired int, kdb KeyDB) ([]byte, bool) { + // We start with a single OP_FALSE to work around the (now standard) + // but in the reference implementation that causes a spurious pop at + // the end of OP_CHECKMULTISIG. + builder := NewScriptBuilder().AddOp(OP_FALSE) + signed := 0 + for _, addr := range addresses { + key, _, err := kdb.GetKey(addr) + if err != nil { + continue + } + sig, err := RawTxInSignature(tx, idx, subScript, hashType, key) + if err != nil { + continue + } + + builder.AddData(sig) + signed++ + if signed == nRequired { + break + } + + } + + script, _ := builder.Script() + return script, signed == nRequired +} + +func sign(chainParams *chaincfg.Params, tx *wire.MsgTx, idx int, + subScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB) ([]byte, + ScriptClass, []btcutil.Address, int, error) { + + class, addresses, nrequired, err := ExtractPkScriptAddrs(subScript, + chainParams) + if err != nil { + return nil, NonStandardTy, nil, 0, err + } + + switch class { + case PubKeyTy: + // look up key for address + key, _, err := kdb.GetKey(addresses[0]) + if err != nil { + return nil, class, nil, 0, err + } + + script, err := p2pkSignatureScript(tx, idx, subScript, hashType, + key) + if err != nil { + return nil, class, nil, 0, err + } + + return script, class, addresses, nrequired, nil + case PubKeyHashTy: + // look up key for address + key, compressed, err := kdb.GetKey(addresses[0]) + if err != nil { + return nil, class, nil, 0, err + } + + script, err := SignatureScript(tx, idx, subScript, hashType, + key, compressed) + if err != nil { + return nil, class, nil, 0, err + } + + return script, class, addresses, nrequired, nil + case ScriptHashTy: + script, err := sdb.GetScript(addresses[0]) + if err != nil { + return nil, class, nil, 0, err + } + + return script, class, addresses, nrequired, nil + case MultiSigTy: + script, _ := signMultiSig(tx, idx, subScript, hashType, + addresses, nrequired, kdb) + return script, class, addresses, nrequired, nil + case NullDataTy: + return nil, class, nil, 0, + errors.New("can't sign NULLDATA transactions") + default: + return nil, class, nil, 0, + errors.New("can't sign unknown transactions") + } +} + +// mergeScripts merges sigScript and prevScript assuming they are both +// partial solutions for pkScript spending output idx of tx. class, addresses +// and nrequired are the result of extracting the addresses from pkscript. +// The return value is the best effort merging of the two scripts. Calling this +// function with addresses, class and nrequired that do not match pkScript is +// an error and results in undefined behaviour. +func mergeScripts(chainParams *chaincfg.Params, tx *wire.MsgTx, idx int, + pkScript []byte, class ScriptClass, addresses []btcutil.Address, + nRequired int, sigScript, prevScript []byte) []byte { + + // TODO(oga) the scripthash and multisig paths here are overly + // inefficient in that they will recompute already known data. + // some internal refactoring could probably make this avoid needless + // extra calculations. + switch class { + case ScriptHashTy: + // Remove the last push in the script and then recurse. + // this could be a lot less inefficient. + sigPops, err := parseScript(sigScript) + if err != nil || len(sigPops) == 0 { + return prevScript + } + prevPops, err := parseScript(prevScript) + if err != nil || len(prevPops) == 0 { + return sigScript + } + + // assume that script in sigPops is the correct one, we just + // made it. + script := sigPops[len(sigPops)-1].data + + // We already know this information somewhere up the stack. + class, addresses, nrequired, err := + ExtractPkScriptAddrs(script, chainParams) + + // regenerate scripts. + sigScript, _ := unparseScript(sigPops) + prevScript, _ := unparseScript(prevPops) + + // Merge + mergedScript := mergeScripts(chainParams, tx, idx, script, + class, addresses, nrequired, sigScript, prevScript) + + // Reappend the script and return the result. + builder := NewScriptBuilder() + builder.script = mergedScript + builder.AddData(script) + finalScript, _ := builder.Script() + return finalScript + case MultiSigTy: + return mergeMultiSig(tx, idx, addresses, nRequired, pkScript, + sigScript, prevScript) + + // It doesn't actually make sense to merge anything other than multiig + // and scripthash (because it could contain multisig). Everything else + // has either zero signature, can't be spent, or has a single signature + // which is either present or not. The other two cases are handled + // above. In the conflict case here we just assume the longest is + // correct (this matches behaviour of the reference implementation). + default: + if len(sigScript) > len(prevScript) { + return sigScript + } + return prevScript + } +} + +// mergeMultiSig combines the two signature scripts sigScript and prevScript +// that both provide signatures for pkScript in output idx of tx. addresses +// and nRequired should be the results from extracting the addresses from +// pkScript. Since this function is internal only we assume that the arguments +// have come from other functions internally and thus are all consistent with +// each other, behaviour is undefined if this contract is broken. +func mergeMultiSig(tx *wire.MsgTx, idx int, addresses []btcutil.Address, + nRequired int, pkScript, sigScript, prevScript []byte) []byte { + + // This is an internal only function and we already parsed this script + // as ok for multisig (this is how we got here), so if this fails then + // all assumptions are broken and who knows which way is up? + pkPops, _ := parseScript(pkScript) + + sigPops, err := parseScript(sigScript) + if err != nil || len(sigPops) == 0 { + return prevScript + } + + prevPops, err := parseScript(prevScript) + if err != nil || len(prevPops) == 0 { + return sigScript + } + + // Convenience function to avoid duplication. + extractSigs := func(pops []parsedOpcode, sigs [][]byte) [][]byte { + for _, pop := range pops { + if len(pop.data) != 0 { + sigs = append(sigs, pop.data) + } + } + return sigs + } + + possibleSigs := make([][]byte, 0, len(sigPops)+len(prevPops)) + possibleSigs = extractSigs(sigPops, possibleSigs) + possibleSigs = extractSigs(prevPops, possibleSigs) + + // Now we need to match the signatures to pubkeys, the only real way to + // do that is to try to verify them all and match it to the pubkey + // that verifies it. we then can go through the addresses in order + // to build our script. Anything that doesn't parse or doesn't verify we + // throw away. + addrToSig := make(map[string][]byte) +sigLoop: + for _, sig := range possibleSigs { + + // can't have a valid signature that doesn't at least have a + // hashtype, in practise it is even longer than this. but + // that'll be checked next. + if len(sig) < 1 { + continue + } + tSig := sig[:len(sig)-1] + hashType := SigHashType(sig[len(sig)-1]) + + pSig, err := btcec.ParseDERSignature(tSig, btcec.S256()) + if err != nil { + continue + } + + // We have to do this each round since hash types may vary + // between signatures and so the hash will vary. We can, + // however, assume no sigs etc are in the script since that + // would make the transaction nonstandard and thus not + // MultiSigTy, so we just need to hash the full thing. + hash := calcSignatureHash(pkPops, hashType, tx, idx) + + for _, addr := range addresses { + // All multisig addresses should be pubkey addreses + // it is an error to call this internal function with + // bad input. + pkaddr := addr.(*btcutil.AddressPubKey) + + pubKey := pkaddr.PubKey() + + // If it matches we put it in the map. We only + // can take one signature per public key so if we + // already have one, we can throw this away. + if pSig.Verify(hash, pubKey) { + aStr := addr.EncodeAddress() + if _, ok := addrToSig[aStr]; !ok { + addrToSig[aStr] = sig + } + continue sigLoop + } + } + } + + // Extra opcode to handle the extra arg consumed (due to previous bugs + // in the reference implementation). + builder := NewScriptBuilder().AddOp(OP_FALSE) + doneSigs := 0 + // This assumes that addresses are in the same order as in the script. + for _, addr := range addresses { + sig, ok := addrToSig[addr.EncodeAddress()] + if !ok { + continue + } + builder.AddData(sig) + doneSigs++ + if doneSigs == nRequired { + break + } + } + + // padding for missing ones. + for i := doneSigs; i < nRequired; i++ { + builder.AddOp(OP_0) + } + + script, _ := builder.Script() + return script +} + +// KeyDB is an interface type provided to SignTxOutput, it encapsulates +// any user state required to get the private keys for an address. +type KeyDB interface { + GetKey(btcutil.Address) (*btcec.PrivateKey, bool, error) +} + +// KeyClosure implements KeyDB with a closure. +type KeyClosure func(btcutil.Address) (*btcec.PrivateKey, bool, error) + +// GetKey implements KeyDB by returning the result of calling the closure. +func (kc KeyClosure) GetKey(address btcutil.Address) (*btcec.PrivateKey, + bool, error) { + return kc(address) +} + +// ScriptDB is an interface type provided to SignTxOutput, it encapsulates any +// user state required to get the scripts for an pay-to-script-hash address. +type ScriptDB interface { + GetScript(btcutil.Address) ([]byte, error) +} + +// ScriptClosure implements ScriptDB with a closure. +type ScriptClosure func(btcutil.Address) ([]byte, error) + +// GetScript implements ScriptDB by returning the result of calling the closure. +func (sc ScriptClosure) GetScript(address btcutil.Address) ([]byte, error) { + return sc(address) +} + +// SignTxOutput signs output idx of the given tx to resolve the script given in +// pkScript with a signature type of hashType. Any keys required will be +// looked up by calling getKey() with the string of the given address. +// Any pay-to-script-hash signatures will be similarly looked up by calling +// getScript. If previousScript is provided then the results in previousScript +// will be merged in a type-dependent manner with the newly generated. +// signature script. +func SignTxOutput(chainParams *chaincfg.Params, tx *wire.MsgTx, idx int, + pkScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB, + previousScript []byte) ([]byte, error) { + + sigScript, class, addresses, nrequired, err := sign(chainParams, tx, + idx, pkScript, hashType, kdb, sdb) + if err != nil { + return nil, err + } + + if class == ScriptHashTy { + // TODO keep the sub addressed and pass down to merge. + realSigScript, _, _, _, err := sign(chainParams, tx, idx, + sigScript, hashType, kdb, sdb) + if err != nil { + return nil, err + } + + // This is a bad thing. Append the p2sh script as the last + // push in the script. + builder := NewScriptBuilder() + builder.script = realSigScript + builder.AddData(sigScript) + + sigScript, _ = builder.Script() + // TODO keep a copy of the script for merging. + } + + // Merge scripts. with any previous data, if any. + mergedScript := mergeScripts(chainParams, tx, idx, pkScript, class, + addresses, nrequired, sigScript, previousScript) + return mergedScript, nil +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/sign_test.go b/vendor/github.com/btcsuite/btcd/txscript/sign_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7609bffb0823cf0fdce3d3cdf2e2253741ed0b15 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/sign_test.go @@ -0,0 +1,1714 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript_test + +import ( + "errors" + "fmt" + "testing" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +type addressToKey struct { + key *btcec.PrivateKey + compressed bool +} + +func mkGetKey(keys map[string]addressToKey) txscript.KeyDB { + if keys == nil { + return txscript.KeyClosure(func(addr btcutil.Address) (*btcec.PrivateKey, + bool, error) { + return nil, false, errors.New("nope") + }) + } + return txscript.KeyClosure(func(addr btcutil.Address) (*btcec.PrivateKey, + bool, error) { + a2k, ok := keys[addr.EncodeAddress()] + if !ok { + return nil, false, errors.New("nope") + } + return a2k.key, a2k.compressed, nil + }) +} + +func mkGetScript(scripts map[string][]byte) txscript.ScriptDB { + if scripts == nil { + return txscript.ScriptClosure(func(addr btcutil.Address) ( + []byte, error) { + return nil, errors.New("nope") + }) + } + return txscript.ScriptClosure(func(addr btcutil.Address) ([]byte, + error) { + script, ok := scripts[addr.EncodeAddress()] + if !ok { + return nil, errors.New("nope") + } + return script, nil + }) +} + +func checkScripts(msg string, tx *wire.MsgTx, idx int, sigScript, pkScript []byte) error { + tx.TxIn[idx].SignatureScript = sigScript + vm, err := txscript.NewEngine(pkScript, tx, idx, + txscript.ScriptBip16|txscript.ScriptVerifyDERSignatures, 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 txscript.SigHashType, kdb txscript.KeyDB, sdb txscript.ScriptDB, + previousScript []byte) error { + + sigScript, err := txscript.SignTxOutput(&chaincfg.TestNet3Params, tx, + idx, pkScript, hashType, kdb, sdb, nil) + if err != nil { + return fmt.Errorf("failed to sign output %s: %v", msg, err) + } + + 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 := []txscript.SigHashType{ + txscript.SigHashOld, // no longer used but should act like all + txscript.SigHashAll, + txscript.SigHashNone, + txscript.SigHashSingle, + txscript.SigHashAll | txscript.SigHashAnyOneCanPay, + txscript.SigHashNone | txscript.SigHashAnyOneCanPay, + txscript.SigHashSingle | txscript.SigHashAnyOneCanPay, + } + tx := &wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash{}, + Index: 0, + }, + Sequence: 4294967295, + }, + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash{}, + Index: 1, + }, + Sequence: 4294967295, + }, + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash{}, + Index: 2, + }, + Sequence: 4294967295, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 1, + }, + { + Value: 2, + }, + { + Value: 3, + }, + }, + LockTime: 0, + } + + // Pay to Pubkey Hash (uncompressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.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.EncodeAddress(): {key, false}, + }), mkGetScript(nil), nil); err != nil { + t.Error(err) + break + } + } + } + + // Pay to Pubkey Hash (uncompressed) (merging with correct) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + sigScript, err := txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), nil) + 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 = txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), sigScript) + 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 i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.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.EncodeAddress(): {key, true}, + }), mkGetScript(nil), nil); err != nil { + t.Error(err) + break + } + } + } + + // Pay to Pubkey Hash (compressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + sigScript, err := txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), nil) + 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 = txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), sigScript) + 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 (uncompressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKey(pk, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.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.EncodeAddress(): {key, false}, + }), mkGetScript(nil), nil); err != nil { + t.Error(err) + break + } + } + } + + // Pay to PubKey (uncompressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKey(pk, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + sigScript, err := txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), nil) + 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 = txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), sigScript) + 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 (compressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKey(pk, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.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.EncodeAddress(): {key, true}, + }), mkGetScript(nil), nil); err != nil { + t.Error(err) + break + } + } + } + + // Pay to PubKey (compressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKey(pk, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + sigScript, err := txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), nil) + 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 = txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), sigScript) + 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 i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + break + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := txscript.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.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil); err != nil { + t.Error(err) + break + } + } + } + + // Pay to Pubkey Hash (uncompressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + break + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := txscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + sigScript, err := txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil) + 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 = txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil) + 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 i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := txscript.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.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil); err != nil { + t.Error(err) + break + } + } + } + + // Pay to Pubkey Hash (compressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := txscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + sigScript, err := txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil) + 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 = txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil) + 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 i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKey(pk, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := txscript.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.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil); err != nil { + t.Error(err) + break + } + } + } + + // Pay to PubKey (uncompressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKey(pk, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := txscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + sigScript, err := txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil) + 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 = txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil) + 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 i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKey(pk, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := txscript.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.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil); err != nil { + t.Error(err) + break + } + } + } + + // Pay to PubKey (compressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKey(pk, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := txscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + sigScript, err := txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil) + 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 = txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil) + 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) + + key1, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk1 := (*btcec.PublicKey)(&key1.PublicKey). + SerializeCompressed() + address1, err := btcutil.NewAddressPubKey(pk1, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + key2, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey 2 for %s: %v", + msg, err) + break + } + + pk2 := (*btcec.PublicKey)(&key2.PublicKey). + SerializeCompressed() + address2, err := btcutil.NewAddressPubKey(pk2, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address 2 for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.MultiSigScript( + []*btcutil.AddressPubKey{address1, address2}, + 2) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := txscript.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.EncodeAddress(): {key1, true}, + address2.EncodeAddress(): {key2, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil); err != nil { + t.Error(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) + + key1, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk1 := (*btcec.PublicKey)(&key1.PublicKey). + SerializeCompressed() + address1, err := btcutil.NewAddressPubKey(pk1, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + key2, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey 2 for %s: %v", + msg, err) + break + } + + pk2 := (*btcec.PublicKey)(&key2.PublicKey). + SerializeCompressed() + address2, err := btcutil.NewAddressPubKey(pk2, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address 2 for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.MultiSigScript( + []*btcutil.AddressPubKey{address1, address2}, + 2) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := txscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + sigScript, err := txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address1.EncodeAddress(): {key1, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil) + 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 = txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address2.EncodeAddress(): {key2, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), sigScript) + 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) + + key1, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey for %s: %v", + msg, err) + break + } + + pk1 := (*btcec.PublicKey)(&key1.PublicKey). + SerializeCompressed() + address1, err := btcutil.NewAddressPubKey(pk1, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + key2, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + t.Errorf("failed to make privKey 2 for %s: %v", + msg, err) + break + } + + pk2 := (*btcec.PublicKey)(&key2.PublicKey). + SerializeCompressed() + address2, err := btcutil.NewAddressPubKey(pk2, + &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make address 2 for %s: %v", + msg, err) + break + } + + pkScript, err := txscript.MultiSigScript( + []*btcutil.AddressPubKey{address1, address2}, + 2) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, &chaincfg.TestNet3Params) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := txscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + sigScript, err := txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address1.EncodeAddress(): {key1, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), nil) + 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 = txscript.SignTxOutput( + &chaincfg.TestNet3Params, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address1.EncodeAddress(): {key1, true}, + address2.EncodeAddress(): {key2, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), sigScript) + 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 txscript.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} + pubkeyX = []byte{0xb2, 0x52, 0xf0, 0x49, 0x85, 0x78, 0x03, 0x03, 0xc8, + 0x7d, 0xce, 0x51, 0x7f, 0xa8, 0x69, 0x0b, 0x91, 0x95, 0xf4, + 0xf3, 0x5c, 0x26, 0x73, 0x05, 0x05, 0xa2, 0xee, 0xbc, 0x09, + 0x38, 0x34, 0x3a} + pubkeyY = []byte{0xb7, 0xc6, 0x7d, 0xb2, 0xe1, 0xff, 0xc8, 0x43, 0x1f, + 0x63, 0x32, 0x62, 0xaa, 0x60, 0xc6, 0x83, 0x30, 0xbd, 0x24, + 0x7e, 0xef, 0xdb, 0x6f, 0x2e, 0x8d, 0x56, 0xf0, 0x3c, 0x9f, + 0x6d, 0xb6, 0xf8} + uncompressedPkScript = []byte{0x76, 0xa9, 0x14, 0xd1, 0x7c, 0xb5, + 0xeb, 0xa4, 0x02, 0xcb, 0x68, 0xe0, 0x69, 0x56, 0xbf, 0x32, + 0x53, 0x90, 0x0e, 0x0a, 0x86, 0xc9, 0xfa, 0x88, 0xac} + compressedPkScript = []byte{0x76, 0xa9, 0x14, 0x27, 0x4d, 0x9f, 0x7f, + 0x61, 0x7e, 0x7c, 0x7a, 0x1c, 0x1f, 0xb2, 0x75, 0x79, 0x10, + 0x43, 0x65, 0x68, 0x27, 0x9d, 0x86, 0x88, 0xac} + shortPkScript = []byte{0x76, 0xa9, 0x14, 0xd1, 0x7c, 0xb5, + 0xeb, 0xa4, 0x02, 0xcb, 0x68, 0xe0, 0x69, 0x56, 0xbf, 0x32, + 0x53, 0x90, 0x0e, 0x0a, 0x88, 0xac} + uncompressedAddrStr = "1L6fd93zGmtzkK6CsZFVVoCwzZV3MUtJ4F" + compressedAddrStr = "14apLppt9zTq6cNw8SDfiJhk9PhkZrQtYZ" +) + +// 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: txscript.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: txscript.SigHashAll, + compress: false, + scriptAtWrongIndex: false, + }, + { + name: "one input compressed", + inputs: []tstInput{ + { + txout: wire.NewTxOut(coinbaseVal, compressedPkScript), + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: txscript.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: txscript.SigHashAll, + compress: true, + scriptAtWrongIndex: false, + }, + { + name: "hashType SigHashNone", + inputs: []tstInput{ + { + txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: txscript.SigHashNone, + compress: false, + scriptAtWrongIndex: false, + }, + { + name: "hashType SigHashSingle", + inputs: []tstInput{ + { + txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: txscript.SigHashSingle, + compress: false, + scriptAtWrongIndex: false, + }, + { + name: "hashType SigHashAnyoneCanPay", + inputs: []tstInput{ + { + txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: txscript.SigHashAnyOneCanPay, + compress: false, + scriptAtWrongIndex: false, + }, + { + name: "hashType non-standard", + inputs: []tstInput{ + { + txout: wire.NewTxOut(coinbaseVal, uncompressedPkScript), + sigscriptGenerates: true, + inputValidates: true, + 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: txscript.SigHashAll, + compress: true, + scriptAtWrongIndex: false, + }, + { + name: "short PkScript", + inputs: []tstInput{ + { + txout: wire.NewTxOut(coinbaseVal, shortPkScript), + sigscriptGenerates: false, + indexOutOfRange: false, + }, + }, + hashType: txscript.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: txscript.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: txscript.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, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyD) + +nexttest: + for i := range sigScriptTests { + tx := wire.NewMsgTx() + + output := wire.NewTxOut(500, []byte{txscript.OP_RETURN}) + tx.AddTxOut(output) + + for range sigScriptTests[i].inputs { + txin := wire.NewTxIn(coinbaseOutPoint, 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 = txscript.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 + scriptFlags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures + for j := range tx.TxIn { + vm, err := txscript.NewEngine(sigScriptTests[i]. + inputs[j].txout.PkScript, tx, j, scriptFlags, 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 + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/stack.go b/vendor/github.com/btcsuite/btcd/txscript/stack.go new file mode 100644 index 0000000000000000000000000000000000000000..4e7f541f60a4d81e6cfe98c9191ae6253264354c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/stack.go @@ -0,0 +1,348 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import "encoding/hex" + +// asBool gets the boolean value of the byte array. +func asBool(t []byte) bool { + for i := range t { + if t[i] != 0 { + // Negative 0 is also considered false. + if i == len(t)-1 && t[i] == 0x80 { + return false + } + return true + } + } + return false +} + +// fromBool converts a boolean into the appropriate byte array. +func fromBool(v bool) []byte { + if v { + return []byte{1} + } + return nil +} + +// stack represents a stack of immutable objects to be used with bitcoin +// scripts. Objects may be shared, therefore in usage if a value is to be +// changed it *must* be deep-copied first to avoid changing other values on the +// stack. +type stack struct { + stk [][]byte + verifyMinimalData bool +} + +// Depth returns the number of items on the stack. +func (s *stack) Depth() int32 { + return int32(len(s.stk)) +} + +// PushByteArray adds the given back array to the top of the stack. +// +// Stack transformation: [... x1 x2] -> [... x1 x2 data] +func (s *stack) PushByteArray(so []byte) { + s.stk = append(s.stk, so) +} + +// PushInt converts the provided scriptNum to a suitable byte array then pushes +// it onto the top of the stack. +// +// Stack transformation: [... x1 x2] -> [... x1 x2 int] +func (s *stack) PushInt(val scriptNum) { + s.PushByteArray(val.Bytes()) +} + +// PushBool converts the provided boolean to a suitable byte array then pushes +// it onto the top of the stack. +// +// Stack transformation: [... x1 x2] -> [... x1 x2 bool] +func (s *stack) PushBool(val bool) { + s.PushByteArray(fromBool(val)) +} + +// PopByteArray pops the value off the top of the stack and returns it. +// +// Stack transformation: [... x1 x2 x3] -> [... x1 x2] +func (s *stack) PopByteArray() ([]byte, error) { + return s.nipN(0) +} + +// PopInt pops the value off the top of the stack, converts it into a script +// num, and returns it. The act of converting to a script num enforces the +// consensus rules imposed on data interpreted as numbers. +// +// Stack transformation: [... x1 x2 x3] -> [... x1 x2] +func (s *stack) PopInt() (scriptNum, error) { + so, err := s.PopByteArray() + if err != nil { + return 0, err + } + + return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen) +} + +// PopBool pops the value off the top of the stack, converts it into a bool, and +// returns it. +// +// Stack transformation: [... x1 x2 x3] -> [... x1 x2] +func (s *stack) PopBool() (bool, error) { + so, err := s.PopByteArray() + if err != nil { + return false, err + } + + return asBool(so), nil +} + +// PeekByteArray returns the Nth item on the stack without removing it. +func (s *stack) PeekByteArray(idx int32) ([]byte, error) { + sz := int32(len(s.stk)) + if idx < 0 || idx >= sz { + return nil, ErrStackUnderflow + } + + return s.stk[sz-idx-1], nil +} + +// PeekInt returns the Nth item on the stack as a script num without removing +// it. The act of converting to a script num enforces the consensus rules +// imposed on data interpreted as numbers. +func (s *stack) PeekInt(idx int32) (scriptNum, error) { + so, err := s.PeekByteArray(idx) + if err != nil { + return 0, err + } + + return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen) +} + +// PeekBool returns the Nth item on the stack as a bool without removing it. +func (s *stack) PeekBool(idx int32) (bool, error) { + so, err := s.PeekByteArray(idx) + if err != nil { + return false, err + } + + return asBool(so), nil +} + +// nipN is an internal function that removes the nth item on the stack and +// returns it. +// +// Stack transformation: +// nipN(0): [... x1 x2 x3] -> [... x1 x2] +// nipN(1): [... x1 x2 x3] -> [... x1 x3] +// nipN(2): [... x1 x2 x3] -> [... x2 x3] +func (s *stack) nipN(idx int32) ([]byte, error) { + sz := int32(len(s.stk)) + if idx < 0 || idx > sz-1 { + return nil, ErrStackUnderflow + } + + so := s.stk[sz-idx-1] + if idx == 0 { + s.stk = s.stk[:sz-1] + } else if idx == sz-1 { + s1 := make([][]byte, sz-1, sz-1) + copy(s1, s.stk[1:]) + s.stk = s1 + } else { + s1 := s.stk[sz-idx : sz] + s.stk = s.stk[:sz-idx-1] + s.stk = append(s.stk, s1...) + } + return so, nil +} + +// NipN removes the Nth object on the stack +// +// Stack transformation: +// NipN(0): [... x1 x2 x3] -> [... x1 x2] +// NipN(1): [... x1 x2 x3] -> [... x1 x3] +// NipN(2): [... x1 x2 x3] -> [... x2 x3] +func (s *stack) NipN(idx int32) error { + _, err := s.nipN(idx) + return err +} + +// Tuck copies the item at the top of the stack and inserts it before the 2nd +// to top item. +// +// Stack transformation: [... x1 x2] -> [... x2 x1 x2] +func (s *stack) Tuck() error { + so2, err := s.PopByteArray() + if err != nil { + return err + } + so1, err := s.PopByteArray() + if err != nil { + return err + } + s.PushByteArray(so2) // stack [... x2] + s.PushByteArray(so1) // stack [... x2 x1] + s.PushByteArray(so2) // stack [... x2 x1 x2] + + return nil +} + +// DropN removes the top N items from the stack. +// +// Stack transformation: +// DropN(1): [... x1 x2] -> [... x1] +// DropN(2): [... x1 x2] -> [...] +func (s *stack) DropN(n int32) error { + if n < 1 { + return ErrStackInvalidArgs + } + + for ; n > 0; n-- { + _, err := s.PopByteArray() + if err != nil { + return err + } + } + return nil +} + +// DupN duplicates the top N items on the stack. +// +// Stack transformation: +// DupN(1): [... x1 x2] -> [... x1 x2 x2] +// DupN(2): [... x1 x2] -> [... x1 x2 x1 x2] +func (s *stack) DupN(n int32) error { + if n < 1 { + return ErrStackInvalidArgs + } + + // Iteratively duplicate the value n-1 down the stack n times. + // This leaves an in-order duplicate of the top n items on the stack. + for i := n; i > 0; i-- { + so, err := s.PeekByteArray(n - 1) + if err != nil { + return err + } + s.PushByteArray(so) + } + return nil +} + +// RotN rotates the top 3N items on the stack to the left N times. +// +// Stack transformation: +// RotN(1): [... x1 x2 x3] -> [... x2 x3 x1] +// RotN(2): [... x1 x2 x3 x4 x5 x6] -> [... x3 x4 x5 x6 x1 x2] +func (s *stack) RotN(n int32) error { + if n < 1 { + return ErrStackInvalidArgs + } + + // Nip the 3n-1th item from the stack to the top n times to rotate + // them up to the head of the stack. + entry := 3*n - 1 + for i := n; i > 0; i-- { + so, err := s.nipN(entry) + if err != nil { + return err + } + + s.PushByteArray(so) + } + return nil +} + +// SwapN swaps the top N items on the stack with those below them. +// +// Stack transformation: +// SwapN(1): [... x1 x2] -> [... x2 x1] +// SwapN(2): [... x1 x2 x3 x4] -> [... x3 x4 x1 x2] +func (s *stack) SwapN(n int32) error { + if n < 1 { + return ErrStackInvalidArgs + } + + entry := 2*n - 1 + for i := n; i > 0; i-- { + // Swap 2n-1th entry to top. + so, err := s.nipN(entry) + if err != nil { + return err + } + + s.PushByteArray(so) + } + return nil +} + +// OverN copies N items N items back to the top of the stack. +// +// Stack transformation: +// OverN(1): [... x1 x2 x3] -> [... x1 x2 x3 x2] +// OverN(2): [... x1 x2 x3 x4] -> [... x1 x2 x3 x4 x1 x2] +func (s *stack) OverN(n int32) error { + if n < 1 { + return ErrStackInvalidArgs + } + + // Copy 2n-1th entry to top of the stack. + entry := 2*n - 1 + for ; n > 0; n-- { + so, err := s.PeekByteArray(entry) + if err != nil { + return err + } + s.PushByteArray(so) + } + + return nil +} + +// PickN copies the item N items back in the stack to the top. +// +// Stack transformation: +// PickN(0): [x1 x2 x3] -> [x1 x2 x3 x3] +// PickN(1): [x1 x2 x3] -> [x1 x2 x3 x2] +// PickN(2): [x1 x2 x3] -> [x1 x2 x3 x1] +func (s *stack) PickN(n int32) error { + so, err := s.PeekByteArray(n) + if err != nil { + return err + } + s.PushByteArray(so) + + return nil +} + +// RollN moves the item N items back in the stack to the top. +// +// Stack transformation: +// RollN(0): [x1 x2 x3] -> [x1 x2 x3] +// RollN(1): [x1 x2 x3] -> [x1 x3 x2] +// RollN(2): [x1 x2 x3] -> [x2 x3 x1] +func (s *stack) RollN(n int32) error { + so, err := s.nipN(n) + if err != nil { + return err + } + + s.PushByteArray(so) + + return nil +} + +// String returns the stack in a readable format. +func (s *stack) String() string { + var result string + for _, stack := range s.stk { + if len(stack) == 0 { + result += "00000000 <empty>\n" + } + result += hex.Dump(stack) + } + + return result +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/stack_test.go b/vendor/github.com/btcsuite/btcd/txscript/stack_test.go new file mode 100644 index 0000000000000000000000000000000000000000..03c65d02c04b0ce7ec2a0864c1698a163fb13eb1 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/stack_test.go @@ -0,0 +1,944 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "bytes" + "errors" + "fmt" + "testing" +) + +// TestStack tests that all of the stack operations work as expected. +func TestStack(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + before [][]byte + operation func(*stack) error + expectedReturn error + after [][]byte + }{ + { + "noop", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(s *stack) error { + return nil + }, + nil, + [][]byte{{1}, {2}, {3}, {4}, {5}}, + }, + { + "peek underflow (byte)", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(s *stack) error { + _, err := s.PeekByteArray(5) + return err + }, + ErrStackUnderflow, + nil, + }, + { + "peek underflow (int)", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(s *stack) error { + _, err := s.PeekInt(5) + return err + }, + ErrStackUnderflow, + nil, + }, + { + "peek underflow (bool)", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(s *stack) error { + _, err := s.PeekBool(5) + return err + }, + ErrStackUnderflow, + nil, + }, + { + "pop", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(s *stack) error { + val, err := s.PopByteArray() + if err != nil { + return err + } + if !bytes.Equal(val, []byte{5}) { + return errors.New("not equal") + } + return err + }, + nil, + [][]byte{{1}, {2}, {3}, {4}}, + }, + { + "pop everything", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(s *stack) error { + for i := 0; i < 5; i++ { + _, err := s.PopByteArray() + if err != nil { + return err + } + } + return nil + }, + nil, + nil, + }, + { + "pop underflow", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(s *stack) error { + for i := 0; i < 6; i++ { + _, err := s.PopByteArray() + if err != nil { + return err + } + } + return nil + }, + ErrStackUnderflow, + nil, + }, + { + "pop bool", + [][]byte{nil}, + func(s *stack) error { + val, err := s.PopBool() + if err != nil { + return err + } + + if val != false { + return errors.New("unexpected value") + } + return nil + }, + nil, + nil, + }, + { + "pop bool", + [][]byte{{1}}, + func(s *stack) error { + val, err := s.PopBool() + if err != nil { + return err + } + + if val != true { + return errors.New("unexpected value") + } + return nil + }, + nil, + nil, + }, + { + "pop bool", + nil, + func(s *stack) error { + _, err := s.PopBool() + if err != nil { + return err + } + + return nil + }, + ErrStackUnderflow, + nil, + }, + { + "popInt 0", + [][]byte{{0x0}}, + func(s *stack) error { + v, err := s.PopInt() + if err != nil { + return err + } + if v != 0 { + return errors.New("0 != 0 on popInt") + } + return nil + }, + nil, + nil, + }, + { + "popInt -0", + [][]byte{{0x80}}, + func(s *stack) error { + v, err := s.PopInt() + if err != nil { + return err + } + if v != 0 { + return errors.New("-0 != 0 on popInt") + } + return nil + }, + nil, + nil, + }, + { + "popInt 1", + [][]byte{{0x01}}, + func(s *stack) error { + v, err := s.PopInt() + if err != nil { + return err + } + if v != 1 { + return errors.New("1 != 1 on popInt") + } + return nil + }, + nil, + nil, + }, + { + "popInt 1 leading 0", + [][]byte{{0x01, 0x00, 0x00, 0x00}}, + func(s *stack) error { + v, err := s.PopInt() + if err != nil { + return err + } + if v != 1 { + fmt.Printf("%v != %v\n", v, 1) + return errors.New("1 != 1 on popInt") + } + return nil + }, + nil, + nil, + }, + { + "popInt -1", + [][]byte{{0x81}}, + func(s *stack) error { + v, err := s.PopInt() + if err != nil { + return err + } + if v != -1 { + return errors.New("-1 != -1 on popInt") + } + return nil + }, + nil, + nil, + }, + { + "popInt -1 leading 0", + [][]byte{{0x01, 0x00, 0x00, 0x80}}, + func(s *stack) error { + v, err := s.PopInt() + if err != nil { + return err + } + if v != -1 { + fmt.Printf("%v != %v\n", v, -1) + return errors.New("-1 != -1 on popInt") + } + return nil + }, + nil, + nil, + }, + // Triggers the multibyte case in asInt + { + "popInt -513", + [][]byte{{0x1, 0x82}}, + func(s *stack) error { + v, err := s.PopInt() + if err != nil { + return err + } + if v != -513 { + fmt.Printf("%v != %v\n", v, -513) + return errors.New("1 != 1 on popInt") + } + return nil + }, + nil, + nil, + }, + // Confirm that the asInt code doesn't modify the base data. + { + "peekint nomodify -1", + [][]byte{{0x01, 0x00, 0x00, 0x80}}, + func(s *stack) error { + v, err := s.PeekInt(0) + if err != nil { + return err + } + if v != -1 { + fmt.Printf("%v != %v\n", v, -1) + return errors.New("-1 != -1 on popInt") + } + return nil + }, + nil, + [][]byte{{0x01, 0x00, 0x00, 0x80}}, + }, + { + "PushInt 0", + nil, + func(s *stack) error { + s.PushInt(scriptNum(0)) + return nil + }, + nil, + [][]byte{{}}, + }, + { + "PushInt 1", + nil, + func(s *stack) error { + s.PushInt(scriptNum(1)) + return nil + }, + nil, + [][]byte{{0x1}}, + }, + { + "PushInt -1", + nil, + func(s *stack) error { + s.PushInt(scriptNum(-1)) + return nil + }, + nil, + [][]byte{{0x81}}, + }, + { + "PushInt two bytes", + nil, + func(s *stack) error { + s.PushInt(scriptNum(256)) + return nil + }, + nil, + // little endian.. *sigh* + [][]byte{{0x00, 0x01}}, + }, + { + "PushInt leading zeros", + nil, + func(s *stack) error { + // this will have the highbit set + s.PushInt(scriptNum(128)) + return nil + }, + nil, + [][]byte{{0x80, 0x00}}, + }, + { + "dup", + [][]byte{{1}}, + func(s *stack) error { + err := s.DupN(1) + if err != nil { + return err + } + + return nil + }, + nil, + [][]byte{{1}, {1}}, + }, + { + "dup2", + [][]byte{{1}, {2}}, + func(s *stack) error { + err := s.DupN(2) + if err != nil { + return err + } + + return nil + }, + nil, + [][]byte{{1}, {2}, {1}, {2}}, + }, + { + "dup3", + [][]byte{{1}, {2}, {3}}, + func(s *stack) error { + err := s.DupN(3) + if err != nil { + return err + } + + return nil + }, + nil, + [][]byte{{1}, {2}, {3}, {1}, {2}, {3}}, + }, + { + "dup0", + [][]byte{{1}}, + func(s *stack) error { + err := s.DupN(0) + if err != nil { + return err + } + + return nil + }, + ErrStackInvalidArgs, + nil, + }, + { + "dup-1", + [][]byte{{1}}, + func(s *stack) error { + err := s.DupN(-1) + if err != nil { + return err + } + + return nil + }, + ErrStackInvalidArgs, + nil, + }, + { + "dup too much", + [][]byte{{1}}, + func(s *stack) error { + err := s.DupN(2) + if err != nil { + return err + } + + return nil + }, + ErrStackUnderflow, + nil, + }, + { + "PushBool true", + nil, + func(s *stack) error { + s.PushBool(true) + + return nil + }, + nil, + [][]byte{{1}}, + }, + { + "PushBool false", + nil, + func(s *stack) error { + s.PushBool(false) + + return nil + }, + nil, + [][]byte{nil}, + }, + { + "PushBool PopBool", + nil, + func(s *stack) error { + s.PushBool(true) + val, err := s.PopBool() + if err != nil { + return err + } + if val != true { + return errors.New("unexpected value") + } + + return nil + }, + nil, + nil, + }, + { + "PushBool PopBool 2", + nil, + func(s *stack) error { + s.PushBool(false) + val, err := s.PopBool() + if err != nil { + return err + } + if val != false { + return errors.New("unexpected value") + } + + return nil + }, + nil, + nil, + }, + { + "PushInt PopBool", + nil, + func(s *stack) error { + s.PushInt(scriptNum(1)) + val, err := s.PopBool() + if err != nil { + return err + } + if val != true { + return errors.New("unexpected value") + } + + return nil + }, + nil, + nil, + }, + { + "PushInt PopBool 2", + nil, + func(s *stack) error { + s.PushInt(scriptNum(0)) + val, err := s.PopBool() + if err != nil { + return err + } + if val != false { + return errors.New("unexpected value") + } + + return nil + }, + nil, + nil, + }, + { + "Nip top", + [][]byte{{1}, {2}, {3}}, + func(s *stack) error { + return s.NipN(0) + }, + nil, + [][]byte{{1}, {2}}, + }, + { + "Nip middle", + [][]byte{{1}, {2}, {3}}, + func(s *stack) error { + return s.NipN(1) + }, + nil, + [][]byte{{1}, {3}}, + }, + { + "Nip low", + [][]byte{{1}, {2}, {3}}, + func(s *stack) error { + return s.NipN(2) + }, + nil, + [][]byte{{2}, {3}}, + }, + { + "Nip too much", + [][]byte{{1}, {2}, {3}}, + func(s *stack) error { + // bite off more than we can chew + return s.NipN(3) + }, + ErrStackUnderflow, + [][]byte{{2}, {3}}, + }, + { + "keep on tucking", + [][]byte{{1}, {2}, {3}}, + func(s *stack) error { + return s.Tuck() + }, + nil, + [][]byte{{1}, {3}, {2}, {3}}, + }, + { + "a little tucked up", + [][]byte{{1}}, // too few arguments for tuck + func(s *stack) error { + return s.Tuck() + }, + ErrStackUnderflow, + nil, + }, + { + "all tucked up", + nil, // too few arguments for tuck + func(s *stack) error { + return s.Tuck() + }, + ErrStackUnderflow, + nil, + }, + { + "drop 1", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.DropN(1) + }, + nil, + [][]byte{{1}, {2}, {3}}, + }, + { + "drop 2", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.DropN(2) + }, + nil, + [][]byte{{1}, {2}}, + }, + { + "drop 3", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.DropN(3) + }, + nil, + [][]byte{{1}}, + }, + { + "drop 4", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.DropN(4) + }, + nil, + nil, + }, + { + "drop 4/5", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.DropN(5) + }, + ErrStackUnderflow, + nil, + }, + { + "drop invalid", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.DropN(0) + }, + ErrStackInvalidArgs, + nil, + }, + { + "Rot1", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.RotN(1) + }, + nil, + [][]byte{{1}, {3}, {4}, {2}}, + }, + { + "Rot2", + [][]byte{{1}, {2}, {3}, {4}, {5}, {6}}, + func(s *stack) error { + return s.RotN(2) + }, + nil, + [][]byte{{3}, {4}, {5}, {6}, {1}, {2}}, + }, + { + "Rot too little", + [][]byte{{1}, {2}}, + func(s *stack) error { + return s.RotN(1) + }, + ErrStackUnderflow, + nil, + }, + { + "Rot0", + [][]byte{{1}, {2}, {3}}, + func(s *stack) error { + return s.RotN(0) + }, + ErrStackInvalidArgs, + nil, + }, + { + "Swap1", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.SwapN(1) + }, + nil, + [][]byte{{1}, {2}, {4}, {3}}, + }, + { + "Swap2", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.SwapN(2) + }, + nil, + [][]byte{{3}, {4}, {1}, {2}}, + }, + { + "Swap too little", + [][]byte{{1}}, + func(s *stack) error { + return s.SwapN(1) + }, + ErrStackUnderflow, + nil, + }, + { + "Swap0", + [][]byte{{1}, {2}, {3}}, + func(s *stack) error { + return s.SwapN(0) + }, + ErrStackInvalidArgs, + nil, + }, + { + "Over1", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.OverN(1) + }, + nil, + [][]byte{{1}, {2}, {3}, {4}, {3}}, + }, + { + "Over2", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.OverN(2) + }, + nil, + [][]byte{{1}, {2}, {3}, {4}, {1}, {2}}, + }, + { + "Over too little", + [][]byte{{1}}, + func(s *stack) error { + return s.OverN(1) + }, + ErrStackUnderflow, + nil, + }, + { + "Over0", + [][]byte{{1}, {2}, {3}}, + func(s *stack) error { + return s.OverN(0) + }, + ErrStackInvalidArgs, + nil, + }, + { + "Pick1", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.PickN(1) + }, + nil, + [][]byte{{1}, {2}, {3}, {4}, {3}}, + }, + { + "Pick2", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.PickN(2) + }, + nil, + [][]byte{{1}, {2}, {3}, {4}, {2}}, + }, + { + "Pick too little", + [][]byte{{1}}, + func(s *stack) error { + return s.PickN(1) + }, + ErrStackUnderflow, + nil, + }, + { + "Roll1", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.RollN(1) + }, + nil, + [][]byte{{1}, {2}, {4}, {3}}, + }, + { + "Roll2", + [][]byte{{1}, {2}, {3}, {4}}, + func(s *stack) error { + return s.RollN(2) + }, + nil, + [][]byte{{1}, {3}, {4}, {2}}, + }, + { + "Roll too little", + [][]byte{{1}}, + func(s *stack) error { + return s.RollN(1) + }, + ErrStackUnderflow, + nil, + }, + { + "Peek bool", + [][]byte{{1}}, + func(s *stack) error { + // Peek bool is otherwise pretty well tested, + // just check it works. + val, err := s.PeekBool(0) + if err != nil { + return err + } + if val != true { + return errors.New("invalid result") + } + return nil + }, + nil, + [][]byte{{1}}, + }, + { + "Peek bool 2", + [][]byte{nil}, + func(s *stack) error { + // Peek bool is otherwise pretty well tested, + // just check it works. + val, err := s.PeekBool(0) + if err != nil { + return err + } + if val != false { + return errors.New("invalid result") + } + return nil + }, + nil, + [][]byte{nil}, + }, + { + "Peek int", + [][]byte{{1}}, + func(s *stack) error { + // Peek int is otherwise pretty well tested, + // just check it works. + val, err := s.PeekInt(0) + if err != nil { + return err + } + if val != 1 { + return errors.New("invalid result") + } + return nil + }, + nil, + [][]byte{{1}}, + }, + { + "Peek int 2", + [][]byte{{0}}, + func(s *stack) error { + // Peek int is otherwise pretty well tested, + // just check it works. + val, err := s.PeekInt(0) + if err != nil { + return err + } + if val != 0 { + return errors.New("invalid result") + } + return nil + }, + nil, + [][]byte{{0}}, + }, + { + "pop int", + nil, + func(s *stack) error { + s.PushInt(scriptNum(1)) + // Peek int is otherwise pretty well tested, + // just check it works. + val, err := s.PopInt() + if err != nil { + return err + } + if val != 1 { + return errors.New("invalid result") + } + return nil + }, + nil, + nil, + }, + { + "pop empty", + nil, + func(s *stack) error { + // Peek int is otherwise pretty well tested, + // just check it works. + _, err := s.PopInt() + return err + }, + ErrStackUnderflow, + nil, + }, + } + + for _, test := range tests { + s := stack{} + + for i := range test.before { + s.PushByteArray(test.before[i]) + } + err := test.operation(&s) + if err != test.expectedReturn { + t.Errorf("%s: operation return not what expected: %v "+ + "vs %v", test.name, err, test.expectedReturn) + } + if err != nil { + continue + } + + if int32(len(test.after)) != s.Depth() { + t.Errorf("%s: stack depth doesn't match expected: %v "+ + "vs %v", test.name, len(test.after), + s.Depth()) + } + + for i := range test.after { + val, err := s.PeekByteArray(s.Depth() - int32(i) - 1) + if err != nil { + t.Errorf("%s: can't peek %dth stack entry: %v", + test.name, i, err) + break + } + + if !bytes.Equal(val, test.after[i]) { + t.Errorf("%s: %dth stack entry doesn't match "+ + "expected: %v vs %v", test.name, i, val, + test.after[i]) + break + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/standard.go b/vendor/github.com/btcsuite/btcd/txscript/standard.go new file mode 100644 index 0000000000000000000000000000000000000000..66079461240765a18ea29e9e51a3f1e5318e1f6b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/standard.go @@ -0,0 +1,471 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcutil" +) + +const ( + // MaxDataCarrierSize is the maximum number of bytes allowed in pushed + // data to be considered a nulldata transaction + MaxDataCarrierSize = 80 + + // StandardVerifyFlags are the script flags which are used when + // executing transaction scripts to enforce additional checks which + // are required for the script to be considered standard. These checks + // help reduce issues related to transaction malleability as well as + // allow pay-to-script hash transactions. Note these flags are + // different than what is required for the consensus rules in that they + // are more strict. + // + // TODO: This definition does not belong here. It belongs in a policy + // package. + StandardVerifyFlags = ScriptBip16 | + ScriptVerifyDERSignatures | + ScriptVerifyStrictEncoding | + ScriptVerifyMinimalData | + ScriptStrictMultiSig | + ScriptDiscourageUpgradableNops | + ScriptVerifyCleanStack | + ScriptVerifyCheckLockTimeVerify | + ScriptVerifyLowS +) + +// ScriptClass is an enumeration for the list of standard types of script. +type ScriptClass byte + +// Classes of script payment known about in the blockchain. +const ( + NonStandardTy ScriptClass = iota // None of the recognized forms. + PubKeyTy // Pay pubkey. + PubKeyHashTy // Pay pubkey hash. + ScriptHashTy // Pay to script hash. + MultiSigTy // Multi signature. + NullDataTy // Empty data-only (provably prunable). +) + +// scriptClassToName houses the human-readable strings which describe each +// script class. +var scriptClassToName = []string{ + NonStandardTy: "nonstandard", + PubKeyTy: "pubkey", + PubKeyHashTy: "pubkeyhash", + ScriptHashTy: "scripthash", + MultiSigTy: "multisig", + NullDataTy: "nulldata", +} + +// String implements the Stringer interface by returning the name of +// the enum script class. If the enum is invalid then "Invalid" will be +// returned. +func (t ScriptClass) String() string { + if int(t) > len(scriptClassToName) || int(t) < 0 { + return "Invalid" + } + return scriptClassToName[t] +} + +// isPubkey returns true if the script passed is a pay-to-pubkey transaction, +// false otherwise. +func isPubkey(pops []parsedOpcode) bool { + // Valid pubkeys are either 33 or 65 bytes. + return len(pops) == 2 && + (len(pops[0].data) == 33 || len(pops[0].data) == 65) && + pops[1].opcode.value == OP_CHECKSIG +} + +// isPubkeyHash returns true if the script passed is a pay-to-pubkey-hash +// transaction, false otherwise. +func isPubkeyHash(pops []parsedOpcode) bool { + return len(pops) == 5 && + pops[0].opcode.value == OP_DUP && + pops[1].opcode.value == OP_HASH160 && + pops[2].opcode.value == OP_DATA_20 && + pops[3].opcode.value == OP_EQUALVERIFY && + pops[4].opcode.value == OP_CHECKSIG + +} + +// isMultiSig returns true if the passed script is a multisig transaction, false +// otherwise. +func isMultiSig(pops []parsedOpcode) bool { + // The absolute minimum is 1 pubkey: + // OP_0/OP_1-16 <pubkey> OP_1 OP_CHECKMULTISIG + l := len(pops) + if l < 4 { + return false + } + if !isSmallInt(pops[0].opcode) { + return false + } + if !isSmallInt(pops[l-2].opcode) { + return false + } + if pops[l-1].opcode.value != OP_CHECKMULTISIG { + return false + } + + // Verify the number of pubkeys specified matches the actual number + // of pubkeys provided. + if l-2-1 != asSmallInt(pops[l-2].opcode) { + return false + } + + for _, pop := range pops[1 : l-2] { + // Valid pubkeys are either 33 or 65 bytes. + if len(pop.data) != 33 && len(pop.data) != 65 { + return false + } + } + return true +} + +// isNullData returns true if the passed script is a null data transaction, +// false otherwise. +func isNullData(pops []parsedOpcode) bool { + // A nulldata transaction is either a single OP_RETURN or an + // OP_RETURN SMALLDATA (where SMALLDATA is a data push up to + // MaxDataCarrierSize bytes). + l := len(pops) + if l == 1 && pops[0].opcode.value == OP_RETURN { + return true + } + + return l == 2 && + pops[0].opcode.value == OP_RETURN && + pops[1].opcode.value <= OP_PUSHDATA4 && + len(pops[1].data) <= MaxDataCarrierSize +} + +// scriptType returns the type of the script being inspected from the known +// standard types. +func typeOfScript(pops []parsedOpcode) ScriptClass { + if isPubkey(pops) { + return PubKeyTy + } else if isPubkeyHash(pops) { + return PubKeyHashTy + } else if isScriptHash(pops) { + return ScriptHashTy + } else if isMultiSig(pops) { + return MultiSigTy + } else if isNullData(pops) { + return NullDataTy + } + return NonStandardTy +} + +// GetScriptClass returns the class of the script passed. +// +// NonStandardTy will be returned when the script does not parse. +func GetScriptClass(script []byte) ScriptClass { + pops, err := parseScript(script) + if err != nil { + return NonStandardTy + } + return typeOfScript(pops) +} + +// expectedInputs returns the number of arguments required by a script. +// If the script is of unknown type such that the number can not be determined +// then -1 is returned. We are an internal function and thus assume that class +// is the real class of pops (and we can thus assume things that were determined +// while finding out the type). +func expectedInputs(pops []parsedOpcode, class ScriptClass) int { + switch class { + case PubKeyTy: + return 1 + + case PubKeyHashTy: + return 2 + + case ScriptHashTy: + // Not including script. That is handled by the caller. + return 1 + + case MultiSigTy: + // Standard multisig has a push a small number for the number + // of sigs and number of keys. Check the first push instruction + // to see how many arguments are expected. typeOfScript already + // checked this so we know it'll be a small int. Also, due to + // the original bitcoind bug where OP_CHECKMULTISIG pops an + // additional item from the stack, add an extra expected input + // for the extra push that is required to compensate. + return asSmallInt(pops[0].opcode) + 1 + + case NullDataTy: + fallthrough + default: + return -1 + } +} + +// ScriptInfo houses information about a script pair that is determined by +// CalcScriptInfo. +type ScriptInfo struct { + // PkScriptClass is the class of the public key script and is equivalent + // to calling GetScriptClass on it. + PkScriptClass ScriptClass + + // NumInputs is the number of inputs provided by the public key script. + NumInputs int + + // ExpectedInputs is the number of outputs required by the signature + // script and any pay-to-script-hash scripts. The number will be -1 if + // unknown. + ExpectedInputs int + + // SigOps is the number of signature operations in the script pair. + SigOps int +} + +// CalcScriptInfo returns a structure providing data about the provided script +// pair. It will error if the pair is in someway invalid such that they can not +// be analysed, i.e. if they do not parse or the pkScript is not a push-only +// script +func CalcScriptInfo(sigScript, pkScript []byte, bip16 bool) (*ScriptInfo, error) { + sigPops, err := parseScript(sigScript) + if err != nil { + return nil, err + } + + pkPops, err := parseScript(pkScript) + if err != nil { + return nil, err + } + + // Push only sigScript makes little sense. + si := new(ScriptInfo) + si.PkScriptClass = typeOfScript(pkPops) + + // Can't have a pkScript that doesn't just push data. + if !isPushOnly(sigPops) { + return nil, ErrStackNonPushOnly + } + + si.ExpectedInputs = expectedInputs(pkPops, si.PkScriptClass) + + // All entries pushed to stack (or are OP_RESERVED and exec will fail). + si.NumInputs = len(sigPops) + + // Count sigops taking into account pay-to-script-hash. + if si.PkScriptClass == ScriptHashTy && bip16 { + // The pay-to-hash-script is the final data push of the + // signature script. + script := sigPops[len(sigPops)-1].data + shPops, err := parseScript(script) + if err != nil { + return nil, err + } + + shInputs := expectedInputs(shPops, typeOfScript(shPops)) + if shInputs == -1 { + si.ExpectedInputs = -1 + } else { + si.ExpectedInputs += shInputs + } + si.SigOps = getSigOpCount(shPops, true) + } else { + si.SigOps = getSigOpCount(pkPops, true) + } + + return si, nil +} + +// CalcMultiSigStats returns the number of public keys and signatures from +// a multi-signature transaction script. The passed script MUST already be +// known to be a multi-signature script. +func CalcMultiSigStats(script []byte) (int, int, error) { + pops, err := parseScript(script) + if err != nil { + return 0, 0, err + } + + // A multi-signature script is of the pattern: + // NUM_SIGS PUBKEY PUBKEY PUBKEY... NUM_PUBKEYS OP_CHECKMULTISIG + // Therefore the number of signatures is the oldest item on the stack + // and the number of pubkeys is the 2nd to last. Also, the absolute + // minimum for a multi-signature script is 1 pubkey, so at least 4 + // items must be on the stack per: + // OP_1 PUBKEY OP_1 OP_CHECKMULTISIG + if len(pops) < 4 { + return 0, 0, ErrStackUnderflow + } + + numSigs := asSmallInt(pops[0].opcode) + numPubKeys := asSmallInt(pops[len(pops)-2].opcode) + return numPubKeys, numSigs, nil +} + +// payToPubKeyHashScript creates a new script to pay a transaction +// output to a 20-byte pubkey hash. It is expected that the input is a valid +// hash. +func payToPubKeyHashScript(pubKeyHash []byte) ([]byte, error) { + return NewScriptBuilder().AddOp(OP_DUP).AddOp(OP_HASH160). + AddData(pubKeyHash).AddOp(OP_EQUALVERIFY).AddOp(OP_CHECKSIG). + Script() +} + +// payToScriptHashScript creates a new script to pay a transaction output to a +// script hash. It is expected that the input is a valid hash. +func payToScriptHashScript(scriptHash []byte) ([]byte, error) { + return NewScriptBuilder().AddOp(OP_HASH160).AddData(scriptHash). + AddOp(OP_EQUAL).Script() +} + +// payToPubkeyScript creates a new script to pay a transaction output to a +// public key. It is expected that the input is a valid pubkey. +func payToPubKeyScript(serializedPubKey []byte) ([]byte, error) { + return NewScriptBuilder().AddData(serializedPubKey). + AddOp(OP_CHECKSIG).Script() +} + +// PayToAddrScript creates a new script to pay a transaction output to a the +// specified address. +func PayToAddrScript(addr btcutil.Address) ([]byte, error) { + switch addr := addr.(type) { + case *btcutil.AddressPubKeyHash: + if addr == nil { + return nil, ErrUnsupportedAddress + } + return payToPubKeyHashScript(addr.ScriptAddress()) + + case *btcutil.AddressScriptHash: + if addr == nil { + return nil, ErrUnsupportedAddress + } + return payToScriptHashScript(addr.ScriptAddress()) + + case *btcutil.AddressPubKey: + if addr == nil { + return nil, ErrUnsupportedAddress + } + return payToPubKeyScript(addr.ScriptAddress()) + } + + return nil, ErrUnsupportedAddress +} + +// MultiSigScript returns a valid script for a multisignature redemption where +// nrequired of the keys in pubkeys are required to have signed the transaction +// for success. An ErrBadNumRequired will be returned if nrequired is larger +// than the number of keys provided. +func MultiSigScript(pubkeys []*btcutil.AddressPubKey, nrequired int) ([]byte, error) { + if len(pubkeys) < nrequired { + return nil, ErrBadNumRequired + } + + builder := NewScriptBuilder().AddInt64(int64(nrequired)) + for _, key := range pubkeys { + builder.AddData(key.ScriptAddress()) + } + builder.AddInt64(int64(len(pubkeys))) + builder.AddOp(OP_CHECKMULTISIG) + + return builder.Script() +} + +// PushedData returns an array of byte slices containing any pushed data found +// in the passed script. This includes OP_0, but not OP_1 - OP_16. +func PushedData(script []byte) ([][]byte, error) { + pops, err := parseScript(script) + if err != nil { + return nil, err + } + + var data [][]byte + for _, pop := range pops { + if pop.data != nil { + data = append(data, pop.data) + } else if pop.opcode.value == OP_0 { + data = append(data, nil) + } + } + return data, nil +} + +// ExtractPkScriptAddrs returns the type of script, addresses and required +// signatures associated with the passed PkScript. Note that it only works for +// 'standard' transaction script types. Any data such as public keys which are +// invalid are omitted from the results. +func ExtractPkScriptAddrs(pkScript []byte, chainParams *chaincfg.Params) (ScriptClass, []btcutil.Address, int, error) { + var addrs []btcutil.Address + var requiredSigs int + + // No valid addresses or required signatures if the script doesn't + // parse. + pops, err := parseScript(pkScript) + if err != nil { + return NonStandardTy, nil, 0, err + } + + scriptClass := typeOfScript(pops) + switch scriptClass { + case PubKeyHashTy: + // A pay-to-pubkey-hash script is of the form: + // OP_DUP OP_HASH160 <hash> OP_EQUALVERIFY OP_CHECKSIG + // Therefore the pubkey hash is the 3rd item on the stack. + // Skip the pubkey hash if it's invalid for some reason. + requiredSigs = 1 + addr, err := btcutil.NewAddressPubKeyHash(pops[2].data, + chainParams) + if err == nil { + addrs = append(addrs, addr) + } + + case PubKeyTy: + // A pay-to-pubkey script is of the form: + // <pubkey> OP_CHECKSIG + // Therefore the pubkey is the first item on the stack. + // Skip the pubkey if it's invalid for some reason. + requiredSigs = 1 + addr, err := btcutil.NewAddressPubKey(pops[0].data, chainParams) + if err == nil { + addrs = append(addrs, addr) + } + + case ScriptHashTy: + // A pay-to-script-hash script is of the form: + // OP_HASH160 <scripthash> OP_EQUAL + // Therefore the script hash is the 2nd item on the stack. + // Skip the script hash if it's invalid for some reason. + requiredSigs = 1 + addr, err := btcutil.NewAddressScriptHashFromHash(pops[1].data, + chainParams) + if err == nil { + addrs = append(addrs, addr) + } + + case MultiSigTy: + // A multi-signature script is of the form: + // <numsigs> <pubkey> <pubkey> <pubkey>... <numpubkeys> OP_CHECKMULTISIG + // Therefore the number of required signatures is the 1st item + // on the stack and the number of public keys is the 2nd to last + // item on the stack. + requiredSigs = asSmallInt(pops[0].opcode) + numPubKeys := asSmallInt(pops[len(pops)-2].opcode) + + // Extract the public keys while skipping any that are invalid. + addrs = make([]btcutil.Address, 0, numPubKeys) + for i := 0; i < numPubKeys; i++ { + addr, err := btcutil.NewAddressPubKey(pops[i+1].data, + chainParams) + if err == nil { + addrs = append(addrs, addr) + } + } + + case NullDataTy: + // Null data transactions have no addresses or required + // signatures. + + case NonStandardTy: + // Don't attempt to extract addresses or required signatures for + // nonstandard transactions. + } + + return scriptClass, addrs, requiredSigs, nil +} diff --git a/vendor/github.com/btcsuite/btcd/txscript/standard_test.go b/vendor/github.com/btcsuite/btcd/txscript/standard_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a7f74165b577870dec3ec9f95e31c57aabb7fbc4 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/txscript/standard_test.go @@ -0,0 +1,1015 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript_test + +import ( + "bytes" + "encoding/hex" + "reflect" + "testing" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcutil" +) + +// decodeHex decodes the passed hex string and returns the resulting bytes. It +// panics if an error occurs. This is only used in the tests as a helper since +// the only way it can fail is if there is an error in the test source code. +func decodeHex(hexStr string) []byte { + b, err := hex.DecodeString(hexStr) + if err != nil { + panic("invalid hex string in test source: err " + err.Error() + + ", hex: " + hexStr) + } + + return b +} + +// mustParseShortForm parses the passed short form script and returns the +// resulting bytes. It panics if an error occurs. This is only used in the +// tests as a helper since the only way it can fail is if there is an error in +// the test source code. +func mustParseShortForm(script string) []byte { + s, err := parseShortForm(script) + if err != nil { + panic("invalid short form script in test source: err " + + err.Error() + ", script: " + script) + } + + return s +} + +// newAddressPubKey returns a new btcutil.AddressPubKey from the provided +// serialized public key. It panics if an error occurs. This is only used in +// the tests as a helper since the only way it can fail is if there is an error +// in the test source code. +func newAddressPubKey(serializedPubKey []byte) btcutil.Address { + addr, err := btcutil.NewAddressPubKey(serializedPubKey, + &chaincfg.MainNetParams) + if err != nil { + panic("invalid public key in test source") + } + + return addr +} + +// newAddressPubKeyHash returns a new btcutil.AddressPubKeyHash from the +// provided hash. It panics if an error occurs. This is only used in the tests +// as a helper since the only way it can fail is if there is an error in the +// test source code. +func newAddressPubKeyHash(pkHash []byte) btcutil.Address { + addr, err := btcutil.NewAddressPubKeyHash(pkHash, &chaincfg.MainNetParams) + if err != nil { + panic("invalid public key hash in test source") + } + + return addr +} + +// newAddressScriptHash returns a new btcutil.AddressScriptHash from the +// provided hash. It panics if an error occurs. This is only used in the tests +// as a helper since the only way it can fail is if there is an error in the +// test source code. +func newAddressScriptHash(scriptHash []byte) btcutil.Address { + addr, err := btcutil.NewAddressScriptHashFromHash(scriptHash, + &chaincfg.MainNetParams) + if err != nil { + panic("invalid script hash in test source") + } + + return addr +} + +// TestExtractPkScriptAddrs ensures that extracting the type, addresses, and +// number of required signatures from PkScripts works as intended. +func TestExtractPkScriptAddrs(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + script []byte + addrs []btcutil.Address + reqSigs int + class txscript.ScriptClass + }{ + { + name: "standard p2pk with compressed pubkey (0x02)", + script: decodeHex("2102192d74d0cb94344c9569c2e7790157" + + "3d8d7903c3ebec3a957724895dca52c6b4ac"), + addrs: []btcutil.Address{ + newAddressPubKey(decodeHex("02192d74d0cb94344" + + "c9569c2e77901573d8d7903c3ebec3a95772" + + "4895dca52c6b4")), + }, + reqSigs: 1, + class: txscript.PubKeyTy, + }, + { + name: "standard p2pk with uncompressed pubkey (0x04)", + script: decodeHex("410411db93e1dcdb8a016b49840f8c53bc" + + "1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb" + + "84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643" + + "f656b412a3ac"), + addrs: []btcutil.Address{ + newAddressPubKey(decodeHex("0411db93e1dcdb8a0" + + "16b49840f8c53bc1eb68a382e97b1482ecad" + + "7b148a6909a5cb2e0eaddfb84ccf9744464f" + + "82e160bfa9b8b64f9d4c03f999b8643f656b" + + "412a3")), + }, + reqSigs: 1, + class: txscript.PubKeyTy, + }, + { + name: "standard p2pk with hybrid pubkey (0x06)", + script: decodeHex("4106192d74d0cb94344c9569c2e7790157" + + "3d8d7903c3ebec3a957724895dca52c6b40d45264838" + + "c0bd96852662ce6a847b197376830160c6d2eb5e6a4c" + + "44d33f453eac"), + addrs: []btcutil.Address{ + newAddressPubKey(decodeHex("06192d74d0cb94344" + + "c9569c2e77901573d8d7903c3ebec3a95772" + + "4895dca52c6b40d45264838c0bd96852662c" + + "e6a847b197376830160c6d2eb5e6a4c44d33" + + "f453e")), + }, + reqSigs: 1, + class: txscript.PubKeyTy, + }, + { + name: "standard p2pk with compressed pubkey (0x03)", + script: decodeHex("2103b0bd634234abbb1ba1e986e884185c" + + "61cf43e001f9137f23c2c409273eb16e65ac"), + addrs: []btcutil.Address{ + newAddressPubKey(decodeHex("03b0bd634234abbb1" + + "ba1e986e884185c61cf43e001f9137f23c2c" + + "409273eb16e65")), + }, + reqSigs: 1, + class: txscript.PubKeyTy, + }, + { + name: "2nd standard p2pk with uncompressed pubkey (0x04)", + script: decodeHex("4104b0bd634234abbb1ba1e986e884185c" + + "61cf43e001f9137f23c2c409273eb16e6537a576782e" + + "ba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3c" + + "1e0908ef7bac"), + addrs: []btcutil.Address{ + newAddressPubKey(decodeHex("04b0bd634234abbb1" + + "ba1e986e884185c61cf43e001f9137f23c2c" + + "409273eb16e6537a576782eba668a7ef8bd3" + + "b3cfb1edb7117ab65129b8a2e681f3c1e090" + + "8ef7b")), + }, + reqSigs: 1, + class: txscript.PubKeyTy, + }, + { + name: "standard p2pk with hybrid pubkey (0x07)", + script: decodeHex("4107b0bd634234abbb1ba1e986e884185c" + + "61cf43e001f9137f23c2c409273eb16e6537a576782e" + + "ba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3c" + + "1e0908ef7bac"), + addrs: []btcutil.Address{ + newAddressPubKey(decodeHex("07b0bd634234abbb1" + + "ba1e986e884185c61cf43e001f9137f23c2c" + + "409273eb16e6537a576782eba668a7ef8bd3" + + "b3cfb1edb7117ab65129b8a2e681f3c1e090" + + "8ef7b")), + }, + reqSigs: 1, + class: txscript.PubKeyTy, + }, + { + name: "standard p2pkh", + script: decodeHex("76a914ad06dd6ddee55cbca9a9e3713bd7" + + "587509a3056488ac"), + addrs: []btcutil.Address{ + newAddressPubKeyHash(decodeHex("ad06dd6ddee55" + + "cbca9a9e3713bd7587509a30564")), + }, + reqSigs: 1, + class: txscript.PubKeyHashTy, + }, + { + name: "standard p2sh", + script: decodeHex("a91463bcc565f9e68ee0189dd5cc67f1b0" + + "e5f02f45cb87"), + addrs: []btcutil.Address{ + newAddressScriptHash(decodeHex("63bcc565f9e68" + + "ee0189dd5cc67f1b0e5f02f45cb")), + }, + reqSigs: 1, + class: txscript.ScriptHashTy, + }, + // from real tx 60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1, vout 0 + { + name: "standard 1 of 2 multisig", + script: decodeHex("514104cc71eb30d653c0c3163990c47b97" + + "6f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473" + + "e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11" + + "fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381" + + "354d80e550078cb532a34bfa2fcfdeb7d76519aecc62" + + "770f5b0e4ef8551946d8a540911abe3e7854a26f39f5" + + "8b25c15342af52ae"), + addrs: []btcutil.Address{ + newAddressPubKey(decodeHex("04cc71eb30d653c0c" + + "3163990c47b976f3fb3f37cccdcbedb169a1" + + "dfef58bbfbfaff7d8a473e7e2e6d317b87ba" + + "fe8bde97e3cf8f065dec022b51d11fcdd0d3" + + "48ac4")), + newAddressPubKey(decodeHex("0461cbdcc5409fb4b" + + "4d42b51d33381354d80e550078cb532a34bf" + + "a2fcfdeb7d76519aecc62770f5b0e4ef8551" + + "946d8a540911abe3e7854a26f39f58b25c15" + + "342af")), + }, + reqSigs: 1, + class: txscript.MultiSigTy, + }, + // from real tx d646f82bd5fbdb94a36872ce460f97662b80c3050ad3209bef9d1e398ea277ab, vin 1 + { + name: "standard 2 of 3 multisig", + script: decodeHex("524104cb9c3c222c5f7a7d3b9bd152f363" + + "a0b6d54c9eb312c4d4f9af1e8551b6c421a6a4ab0e29" + + "105f24de20ff463c1c91fcf3bf662cdde4783d4799f7" + + "87cb7c08869b4104ccc588420deeebea22a7e900cc8b" + + "68620d2212c374604e3487ca08f1ff3ae12bdc639514" + + "d0ec8612a2d3c519f084d9a00cbbe3b53d071e9b09e7" + + "1e610b036aa24104ab47ad1939edcb3db65f7fedea62" + + "bbf781c5410d3f22a7a3a56ffefb2238af8627363bdf" + + "2ed97c1f89784a1aecdb43384f11d2acc64443c7fc29" + + "9cef0400421a53ae"), + addrs: []btcutil.Address{ + newAddressPubKey(decodeHex("04cb9c3c222c5f7a7" + + "d3b9bd152f363a0b6d54c9eb312c4d4f9af1" + + "e8551b6c421a6a4ab0e29105f24de20ff463" + + "c1c91fcf3bf662cdde4783d4799f787cb7c0" + + "8869b")), + newAddressPubKey(decodeHex("04ccc588420deeebe" + + "a22a7e900cc8b68620d2212c374604e3487c" + + "a08f1ff3ae12bdc639514d0ec8612a2d3c51" + + "9f084d9a00cbbe3b53d071e9b09e71e610b0" + + "36aa2")), + newAddressPubKey(decodeHex("04ab47ad1939edcb3" + + "db65f7fedea62bbf781c5410d3f22a7a3a56" + + "ffefb2238af8627363bdf2ed97c1f89784a1" + + "aecdb43384f11d2acc64443c7fc299cef040" + + "0421a")), + }, + reqSigs: 2, + class: txscript.MultiSigTy, + }, + + // The below are nonstandard script due to things such as + // invalid pubkeys, failure to parse, and not being of a + // standard form. + + { + name: "p2pk with uncompressed pk missing OP_CHECKSIG", + script: decodeHex("410411db93e1dcdb8a016b49840f8c53bc" + + "1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb" + + "84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643" + + "f656b412a3"), + addrs: nil, + reqSigs: 0, + class: txscript.NonStandardTy, + }, + { + name: "valid signature from a sigscript - no addresses", + script: decodeHex("47304402204e45e16932b8af514961a1d3" + + "a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220" + + "181522ec8eca07de4860a4acdd12909d831cc56cbbac" + + "4622082221a8768d1d0901"), + addrs: nil, + reqSigs: 0, + class: txscript.NonStandardTy, + }, + // Note the technically the pubkey is the second item on the + // stack, but since the address extraction intentionally only + // works with standard PkScripts, this should not return any + // addresses. + { + name: "valid sigscript to reedeem p2pk - no addresses", + script: decodeHex("493046022100ddc69738bf2336318e4e04" + + "1a5a77f305da87428ab1606f023260017854350ddc02" + + "2100817af09d2eec36862d16009852b7e3a0f6dd7659" + + "8290b7834e1453660367e07a014104cd4240c198e125" + + "23b6f9cb9f5bed06de1ba37e96a1bbd13745fcf9d11c" + + "25b1dff9a519675d198804ba9962d3eca2d5937d58e5" + + "a75a71042d40388a4d307f887d"), + addrs: nil, + reqSigs: 0, + class: txscript.NonStandardTy, + }, + // from real tx 691dd277dc0e90a462a3d652a1171686de49cf19067cd33c7df0392833fb986a, vout 0 + // invalid public keys + { + name: "1 of 3 multisig with invalid pubkeys", + script: decodeHex("51411c2200007353455857696b696c6561" + + "6b73204361626c6567617465204261636b75700a0a63" + + "61626c65676174652d3230313031323034313831312e" + + "377a0a0a446f41776e6c6f61642074686520666f6c6c" + + "6f77696e67207472616e73616374696f6e7320776974" + + "68205361746f736869204e616b616d6f746f27732064" + + "6f776e6c6f61416420746f6f6c2077686963680a6361" + + "6e20626520666f756e6420696e207472616e73616374" + + "696f6e20366335336364393837313139656637393764" + + "35616463636453ae"), + addrs: []btcutil.Address{}, + reqSigs: 1, + class: txscript.MultiSigTy, + }, + // from real tx: 691dd277dc0e90a462a3d652a1171686de49cf19067cd33c7df0392833fb986a, vout 44 + // invalid public keys + { + name: "1 of 3 multisig with invalid pubkeys 2", + script: decodeHex("5141346333656332353963373464616365" + + "36666430383862343463656638630a63363662633139" + + "39366338623934613338313162333635363138666531" + + "65396231623541366361636365393933613339383861" + + "34363966636336643664616266640a32363633636661" + + "39636634633033633630396335393363336539316665" + + "64653730323921313233646434326432353633396433" + + "38613663663530616234636434340a00000053ae"), + addrs: []btcutil.Address{}, + reqSigs: 1, + class: txscript.MultiSigTy, + }, + { + name: "empty script", + script: []byte{}, + addrs: nil, + reqSigs: 0, + class: txscript.NonStandardTy, + }, + { + name: "script that does not parse", + script: []byte{txscript.OP_DATA_45}, + addrs: nil, + reqSigs: 0, + class: txscript.NonStandardTy, + }, + } + + t.Logf("Running %d tests.", len(tests)) + for i, test := range tests { + class, addrs, reqSigs, err := txscript.ExtractPkScriptAddrs( + test.script, &chaincfg.MainNetParams) + if err != nil { + } + + if !reflect.DeepEqual(addrs, test.addrs) { + t.Errorf("ExtractPkScriptAddrs #%d (%s) unexpected "+ + "addresses\ngot %v\nwant %v", i, test.name, + addrs, test.addrs) + continue + } + + if reqSigs != test.reqSigs { + t.Errorf("ExtractPkScriptAddrs #%d (%s) unexpected "+ + "number of required signatures - got %d, "+ + "want %d", i, test.name, reqSigs, test.reqSigs) + continue + } + + if class != test.class { + t.Errorf("ExtractPkScriptAddrs #%d (%s) unexpected "+ + "script type - got %s, want %s", i, test.name, + class, test.class) + continue + } + } +} + +// TestCalcScriptInfo ensures the CalcScriptInfo provides the expected results +// for various valid and invalid script pairs. +func TestCalcScriptInfo(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + sigScript string + pkScript string + bip16 bool + scriptInfo txscript.ScriptInfo + scriptInfoErr error + }{ + { + // Invented scripts, the hashes do not match + // Truncated version of test below: + name: "pkscript doesn't parse", + sigScript: "1 81 DATA_8 2DUP EQUAL NOT VERIFY ABS " + + "SWAP ABS EQUAL", + pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" + + "3152205ec4f59c", + bip16: true, + scriptInfoErr: txscript.ErrStackShortScript, + }, + { + name: "sigScript doesn't parse", + // Truncated version of p2sh script below. + sigScript: "1 81 DATA_8 2DUP EQUAL NOT VERIFY ABS " + + "SWAP ABS", + pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" + + "3152205ec4f59c74 EQUAL", + bip16: true, + scriptInfoErr: txscript.ErrStackShortScript, + }, + { + // Invented scripts, the hashes do not match + name: "p2sh standard script", + sigScript: "1 81 DATA_25 DUP HASH160 DATA_20 0x010203" + + "0405060708090a0b0c0d0e0f1011121314 EQUALVERIFY " + + "CHECKSIG", + pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" + + "3152205ec4f59c74 EQUAL", + bip16: true, + scriptInfo: txscript.ScriptInfo{ + PkScriptClass: txscript.ScriptHashTy, + NumInputs: 3, + ExpectedInputs: 3, // nonstandard p2sh. + SigOps: 1, + }, + }, + { + // from 567a53d1ce19ce3d07711885168484439965501536d0d0294c5d46d46c10e53b + // from the blockchain. + name: "p2sh nonstandard script", + sigScript: "1 81 DATA_8 2DUP EQUAL NOT VERIFY ABS " + + "SWAP ABS EQUAL", + pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" + + "3152205ec4f59c74 EQUAL", + bip16: true, + scriptInfo: txscript.ScriptInfo{ + PkScriptClass: txscript.ScriptHashTy, + NumInputs: 3, + ExpectedInputs: -1, // nonstandard p2sh. + SigOps: 0, + }, + }, + { + // Script is invented, numbers all fake. + name: "multisig script", + // Extra 0 arg on the end for OP_CHECKMULTISIG bug. + sigScript: "1 1 1 0", + pkScript: "3 " + + "DATA_33 0x0102030405060708090a0b0c0d0e0f1011" + + "12131415161718191a1b1c1d1e1f2021 DATA_33 " + + "0x0102030405060708090a0b0c0d0e0f101112131415" + + "161718191a1b1c1d1e1f2021 DATA_33 0x010203040" + + "5060708090a0b0c0d0e0f101112131415161718191a1" + + "b1c1d1e1f2021 3 CHECKMULTISIG", + bip16: true, + scriptInfo: txscript.ScriptInfo{ + PkScriptClass: txscript.MultiSigTy, + NumInputs: 4, + ExpectedInputs: 4, + SigOps: 3, + }, + }, + } + + for _, test := range tests { + sigScript := mustParseShortForm(test.sigScript) + pkScript := mustParseShortForm(test.pkScript) + si, err := txscript.CalcScriptInfo(sigScript, pkScript, + test.bip16) + if err != nil { + if err != test.scriptInfoErr { + t.Errorf("scriptinfo test \"%s\": got \"%v\""+ + "expected \"%v\"", test.name, err, + test.scriptInfoErr) + } + continue + } + if test.scriptInfoErr != nil { + t.Errorf("%s: succeeded when expecting \"%v\"", + test.name, test.scriptInfoErr) + continue + } + if *si != test.scriptInfo { + t.Errorf("%s: scriptinfo doesn't match expected. "+ + "got: \"%v\" expected \"%v\"", test.name, + *si, test.scriptInfo) + continue + } + } +} + +// bogusAddress implements the btcutil.Address interface so the tests can ensure +// unsupported address types are handled properly. +type bogusAddress struct{} + +// EncodeAddress simply returns an empty string. It exists to satsify the +// btcutil.Address interface. +func (b *bogusAddress) EncodeAddress() string { + return "" +} + +// ScriptAddress simply returns an empty byte slice. It exists to satsify the +// btcutil.Address interface. +func (b *bogusAddress) ScriptAddress() []byte { + return nil +} + +// IsForNet lies blatantly to satisfy the btcutil.Address interface. +func (b *bogusAddress) IsForNet(chainParams *chaincfg.Params) bool { + return true // why not? +} + +// String simply returns an empty string. It exists to satsify the +// btcutil.Address interface. +func (b *bogusAddress) String() string { + return "" +} + +// TestPayToAddrScript ensures the PayToAddrScript function generates the +// correct scripts for the various types of addresses. +func TestPayToAddrScript(t *testing.T) { + t.Parallel() + + // 1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX + p2pkhMain, err := btcutil.NewAddressPubKeyHash(decodeHex("e34cce70c863"+ + "73273efcc54ce7d2a491bb4a0e84"), &chaincfg.MainNetParams) + if err != nil { + t.Errorf("Unable to create public key hash address: %v", err) + return + } + + // Taken from transaction: + // b0539a45de13b3e0403909b8bd1a555b8cbe45fd4e3f3fda76f3a5f52835c29d + p2shMain, _ := btcutil.NewAddressScriptHashFromHash(decodeHex("e8c300"+ + "c87986efa84c37c0519929019ef86eb5b4"), &chaincfg.MainNetParams) + if err != nil { + t.Errorf("Unable to create script hash address: %v", err) + return + } + + // mainnet p2pk 13CG6SJ3yHUXo4Cr2RY4THLLJrNFuG3gUg + p2pkCompressedMain, err := btcutil.NewAddressPubKey(decodeHex("02192d74"+ + "d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"), + &chaincfg.MainNetParams) + if err != nil { + t.Errorf("Unable to create pubkey address (compressed): %v", + err) + return + } + p2pkCompressed2Main, err := btcutil.NewAddressPubKey(decodeHex("03b0bd"+ + "634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65"), + &chaincfg.MainNetParams) + if err != nil { + t.Errorf("Unable to create pubkey address (compressed 2): %v", + err) + return + } + + p2pkUncompressedMain, err := btcutil.NewAddressPubKey(decodeHex("0411db"+ + "93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2"+ + "e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3"), + &chaincfg.MainNetParams) + if err != nil { + t.Errorf("Unable to create pubkey address (uncompressed): %v", + err) + return + } + + tests := []struct { + in btcutil.Address + expected string + err error + }{ + // pay-to-pubkey-hash address on mainnet + { + p2pkhMain, + "DUP HASH160 DATA_20 0xe34cce70c86373273efcc54ce7d2a4" + + "91bb4a0e8488 CHECKSIG", + nil, + }, + // pay-to-script-hash address on mainnet + { + p2shMain, + "HASH160 DATA_20 0xe8c300c87986efa84c37c0519929019ef8" + + "6eb5b4 EQUAL", + nil, + }, + // pay-to-pubkey address on mainnet. compressed key. + { + p2pkCompressedMain, + "DATA_33 0x02192d74d0cb94344c9569c2e77901573d8d7903c3" + + "ebec3a957724895dca52c6b4 CHECKSIG", + nil, + }, + // pay-to-pubkey address on mainnet. compressed key (other way). + { + p2pkCompressed2Main, + "DATA_33 0x03b0bd634234abbb1ba1e986e884185c61cf43e001" + + "f9137f23c2c409273eb16e65 CHECKSIG", + nil, + }, + // pay-to-pubkey address on mainnet. uncompressed key. + { + p2pkUncompressedMain, + "DATA_65 0x0411db93e1dcdb8a016b49840f8c53bc1eb68a382e" + + "97b1482ecad7b148a6909a5cb2e0eaddfb84ccf97444" + + "64f82e160bfa9b8b64f9d4c03f999b8643f656b412a3 " + + "CHECKSIG", + nil, + }, + + // Supported address types with nil pointers. + {(*btcutil.AddressPubKeyHash)(nil), "", txscript.ErrUnsupportedAddress}, + {(*btcutil.AddressScriptHash)(nil), "", txscript.ErrUnsupportedAddress}, + {(*btcutil.AddressPubKey)(nil), "", txscript.ErrUnsupportedAddress}, + + // Unsupported address type. + {&bogusAddress{}, "", txscript.ErrUnsupportedAddress}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + pkScript, err := txscript.PayToAddrScript(test.in) + if err != test.err { + t.Errorf("PayToAddrScript #%d unexpected error - "+ + "got %v, want %v", i, err, test.err) + continue + } + + expected := mustParseShortForm(test.expected) + if !bytes.Equal(pkScript, expected) { + t.Errorf("PayToAddrScript #%d got: %x\nwant: %x", + i, pkScript, expected) + continue + } + } +} + +// TestMultiSigScript ensures the MultiSigScript function returns the expected +// scripts and errors. +func TestMultiSigScript(t *testing.T) { + t.Parallel() + + // mainnet p2pk 13CG6SJ3yHUXo4Cr2RY4THLLJrNFuG3gUg + p2pkCompressedMain, err := btcutil.NewAddressPubKey(decodeHex("02192d7"+ + "4d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"), + &chaincfg.MainNetParams) + if err != nil { + t.Errorf("Unable to create pubkey address (compressed): %v", + err) + return + } + p2pkCompressed2Main, err := btcutil.NewAddressPubKey(decodeHex("03b0bd"+ + "634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65"), + &chaincfg.MainNetParams) + if err != nil { + t.Errorf("Unable to create pubkey address (compressed 2): %v", + err) + return + } + + p2pkUncompressedMain, err := btcutil.NewAddressPubKey(decodeHex("0411d"+ + "b93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"+ + "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b41"+ + "2a3"), &chaincfg.MainNetParams) + if err != nil { + t.Errorf("Unable to create pubkey address (uncompressed): %v", + err) + return + } + + tests := []struct { + keys []*btcutil.AddressPubKey + nrequired int + expected string + err error + }{ + { + []*btcutil.AddressPubKey{ + p2pkCompressedMain, + p2pkCompressed2Main, + }, + 1, + "1 DATA_33 0x02192d74d0cb94344c9569c2e77901573d8d7903c" + + "3ebec3a957724895dca52c6b4 DATA_33 0x03b0bd634" + + "234abbb1ba1e986e884185c61cf43e001f9137f23c2c4" + + "09273eb16e65 2 CHECKMULTISIG", + nil, + }, + { + []*btcutil.AddressPubKey{ + p2pkCompressedMain, + p2pkCompressed2Main, + }, + 2, + "2 DATA_33 0x02192d74d0cb94344c9569c2e77901573d8d7903c" + + "3ebec3a957724895dca52c6b4 DATA_33 0x03b0bd634" + + "234abbb1ba1e986e884185c61cf43e001f9137f23c2c4" + + "09273eb16e65 2 CHECKMULTISIG", + nil, + }, + { + []*btcutil.AddressPubKey{ + p2pkCompressedMain, + p2pkCompressed2Main, + }, + 3, + "", + txscript.ErrBadNumRequired, + }, + { + []*btcutil.AddressPubKey{ + p2pkUncompressedMain, + }, + 1, + "1 DATA_65 0x0411db93e1dcdb8a016b49840f8c53bc1eb68a382" + + "e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf97444" + + "64f82e160bfa9b8b64f9d4c03f999b8643f656b412a3 " + + "1 CHECKMULTISIG", + nil, + }, + { + []*btcutil.AddressPubKey{ + p2pkUncompressedMain, + }, + 2, + "", + txscript.ErrBadNumRequired, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + script, err := txscript.MultiSigScript(test.keys, + test.nrequired) + if err != test.err { + t.Errorf("MultiSigScript #%d unexpected error - "+ + "got %v, want %v", i, err, test.err) + continue + } + + expected := mustParseShortForm(test.expected) + if !bytes.Equal(script, expected) { + t.Errorf("MultiSigScript #%d got: %x\nwant: %x", + i, script, expected) + continue + } + } +} + +// TestCalcMultiSigStats ensures the CalcMutliSigStats function returns the +// expected errors. +func TestCalcMultiSigStats(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + script string + err error + }{ + { + name: "short script", + script: "0x046708afdb0fe5548271967f1a67130b7105cd6a828" + + "e03909a67962e0ea1f61d", + err: txscript.ErrStackShortScript, + }, + { + name: "stack underflow", + script: "RETURN DATA_41 0x046708afdb0fe5548271967f1a" + + "67130b7105cd6a828e03909a67962e0ea1f61deb649f6" + + "bc3f4cef308", + err: txscript.ErrStackUnderflow, + }, + { + name: "multisig script", + script: "0 DATA_72 0x30450220106a3e4ef0b51b764a2887226" + + "2ffef55846514dacbdcbbdd652c849d395b4384022100" + + "e03ae554c3cbb40600d31dd46fc33f25e47bf8525b1fe" + + "07282e3b6ecb5f3bb2801 CODESEPARATOR 1 DATA_33 " + + "0x0232abdc893e7f0631364d7fd01cb33d24da45329a0" + + "0357b3a7886211ab414d55a 1 CHECKMULTISIG", + err: nil, + }, + } + + for i, test := range tests { + script := mustParseShortForm(test.script) + if _, _, err := txscript.CalcMultiSigStats(script); err != test.err { + t.Errorf("CalcMultiSigStats #%d (%s) unexpected "+ + "error\ngot: %v\nwant: %v", i, test.name, err, + test.err) + } + } +} + +// scriptClassTest houses a test used to ensure various scripts have the +// expected class. +type scriptClassTest struct { + name string + script string + class txscript.ScriptClass +} + +// scriptClassTests houses several test scripts used to ensure various class +// determination is working as expected. It's defined as a test global versus +// inside a function scope since this spans both the standard tests and the +// consensus tests (pay-to-script-hash is part of consensus). +var scriptClassTests = []scriptClassTest{ + { + name: "Pay Pubkey", + script: "DATA_65 0x0411db93e1dcdb8a016b49840f8c53bc1eb68a382e" + + "97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e16" + + "0bfa9b8b64f9d4c03f999b8643f656b412a3 CHECKSIG", + class: txscript.PubKeyTy, + }, + // tx 599e47a8114fe098103663029548811d2651991b62397e057f0c863c2bc9f9ea + { + name: "Pay PubkeyHash", + script: "DUP HASH160 DATA_20 0x660d4ef3a743e3e696ad990364e555" + + "c271ad504b EQUALVERIFY CHECKSIG", + class: txscript.PubKeyHashTy, + }, + // part of tx 6d36bc17e947ce00bb6f12f8e7a56a1585c5a36188ffa2b05e10b4743273a74b + // codeseparator parts have been elided. (bitcoin core's checks for + // multisig type doesn't have codesep either). + { + name: "multisig", + script: "1 DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da4" + + "5329a00357b3a7886211ab414d55a 1 CHECKMULTISIG", + class: txscript.MultiSigTy, + }, + // tx e5779b9e78f9650debc2893fd9636d827b26b4ddfa6a8172fe8708c924f5c39d + { + name: "P2SH", + script: "HASH160 DATA_20 0x433ec2ac1ffa1b7b7d027f564529c57197f" + + "9ae88 EQUAL", + class: txscript.ScriptHashTy, + }, + { + // Nulldata with no data at all. + name: "nulldata", + script: "RETURN", + class: txscript.NullDataTy, + }, + { + // Nulldata with small data. + name: "nulldata2", + script: "RETURN DATA_8 0x046708afdb0fe554", + class: txscript.NullDataTy, + }, + { + // Nulldata with max allowed data. + name: "nulldata3", + script: "RETURN PUSHDATA1 0x50 0x046708afdb0fe5548271967f1a67" + + "130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3" + + "046708afdb0fe5548271967f1a67130b7105cd6a828e03909a67" + + "962e0ea1f61deb649f6bc3f4cef3", + class: txscript.NullDataTy, + }, + { + // Nulldata with more than max allowed data (so therefore + // nonstandard) + name: "nulldata4", + script: "RETURN PUSHDATA1 0x51 0x046708afdb0fe5548271967f1a67" + + "130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3" + + "046708afdb0fe5548271967f1a67130b7105cd6a828e03909a67" + + "962e0ea1f61deb649f6bc3f4cef308", + class: txscript.NonStandardTy, + }, + { + // Almost nulldata, but add an additional opcode after the data + // to make it nonstandard. + name: "nulldata5", + script: "RETURN 4 TRUE", + class: txscript.NonStandardTy, + }, + + // The next few are almost multisig (it is the more complex script type) + // but with various changes to make it fail. + { + // Multisig but invalid nsigs. + name: "strange 1", + script: "DUP DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da45" + + "329a00357b3a7886211ab414d55a 1 CHECKMULTISIG", + class: txscript.NonStandardTy, + }, + { + // Multisig but invalid pubkey. + name: "strange 2", + script: "1 1 1 CHECKMULTISIG", + class: txscript.NonStandardTy, + }, + { + // Multisig but no matching npubkeys opcode. + name: "strange 3", + script: "1 DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da4532" + + "9a00357b3a7886211ab414d55a DATA_33 0x0232abdc893e7f0" + + "631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a " + + "CHECKMULTISIG", + class: txscript.NonStandardTy, + }, + { + // Multisig but with multisigverify. + name: "strange 4", + script: "1 DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da4532" + + "9a00357b3a7886211ab414d55a 1 CHECKMULTISIGVERIFY", + class: txscript.NonStandardTy, + }, + { + // Multisig but wrong length. + name: "strange 5", + script: "1 CHECKMULTISIG", + class: txscript.NonStandardTy, + }, + { + name: "doesn't parse", + script: "DATA_5 0x01020304", + class: txscript.NonStandardTy, + }, + { + name: "multisig script with wrong number of pubkeys", + script: "2 " + + "DATA_33 " + + "0x027adf5df7c965a2d46203c781bd4dd8" + + "21f11844136f6673af7cc5a4a05cd29380 " + + "DATA_33 " + + "0x02c08f3de8ee2de9be7bd770f4c10eb0" + + "d6ff1dd81ee96eedd3a9d4aeaf86695e80 " + + "3 CHECKMULTISIG", + class: txscript.NonStandardTy, + }, +} + +// TestScriptClass ensures all the scripts in scriptClassTests have the expected +// class. +func TestScriptClass(t *testing.T) { + t.Parallel() + + for _, test := range scriptClassTests { + script := mustParseShortForm(test.script) + class := txscript.GetScriptClass(script) + if class != test.class { + t.Errorf("%s: expected %s got %s", test.name, + test.class, class) + return + } + } +} + +// TestStringifyClass ensures the script class string returns the expected +// string for each script class. +func TestStringifyClass(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + class txscript.ScriptClass + stringed string + }{ + { + name: "nonstandardty", + class: txscript.NonStandardTy, + stringed: "nonstandard", + }, + { + name: "pubkey", + class: txscript.PubKeyTy, + stringed: "pubkey", + }, + { + name: "pubkeyhash", + class: txscript.PubKeyHashTy, + stringed: "pubkeyhash", + }, + { + name: "scripthash", + class: txscript.ScriptHashTy, + stringed: "scripthash", + }, + { + name: "multisigty", + class: txscript.MultiSigTy, + stringed: "multisig", + }, + { + name: "nulldataty", + class: txscript.NullDataTy, + stringed: "nulldata", + }, + { + name: "broken", + class: txscript.ScriptClass(255), + stringed: "Invalid", + }, + } + + for _, test := range tests { + typeString := test.class.String() + if typeString != test.stringed { + t.Errorf("%s: got %#q, want %#q", test.name, + typeString, test.stringed) + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/upgrade.go b/vendor/github.com/btcsuite/btcd/upgrade.go new file mode 100644 index 0000000000000000000000000000000000000000..4140f41c24c284279b5479077fd0ec974aceb583 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/upgrade.go @@ -0,0 +1,177 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "io" + "os" + "path/filepath" +) + +// dirEmpty returns whether or not the specified directory path is empty. +func dirEmpty(dirPath string) (bool, error) { + f, err := os.Open(dirPath) + defer f.Close() + + // Read the names of a max of one entry from the directory. When the + // directory is empty, an io.EOF error will be returned, so allow it. + names, err := f.Readdirnames(1) + if err != nil && err != io.EOF { + return false, err + } + + return len(names) == 0, nil +} + +// oldBtcdHomeDir returns the OS specific home directory btcd used prior to +// version 0.3.3. This has since been replaced with btcutil.AppDataDir, but +// this function is still provided for the automatic upgrade path. +func oldBtcdHomeDir() string { + // Search for Windows APPDATA first. This won't exist on POSIX OSes. + appData := os.Getenv("APPDATA") + if appData != "" { + return filepath.Join(appData, "btcd") + } + + // Fall back to standard HOME directory that works for most POSIX OSes. + home := os.Getenv("HOME") + if home != "" { + return filepath.Join(home, ".btcd") + } + + // In the worst case, use the current directory. + return "." +} + +// upgradeDBPathNet moves the database for a specific network from its +// location prior to btcd version 0.2.0 and uses heuristics to ascertain the old +// database type to rename to the new format. +func upgradeDBPathNet(oldDbPath, netName string) error { + // Prior to version 0.2.0, the database was named the same thing for + // both sqlite and leveldb. Use heuristics to figure out the type + // of the database and move it to the new path and name introduced with + // version 0.2.0 accordingly. + fi, err := os.Stat(oldDbPath) + if err == nil { + oldDbType := "sqlite" + if fi.IsDir() { + oldDbType = "leveldb" + } + + // The new database name is based on the database type and + // resides in a directory named after the network type. + newDbRoot := filepath.Join(filepath.Dir(cfg.DataDir), netName) + newDbName := blockDbNamePrefix + "_" + oldDbType + if oldDbType == "sqlite" { + newDbName = newDbName + ".db" + } + newDbPath := filepath.Join(newDbRoot, newDbName) + + // Create the new path if needed. + err = os.MkdirAll(newDbRoot, 0700) + if err != nil { + return err + } + + // Move and rename the old database. + err := os.Rename(oldDbPath, newDbPath) + if err != nil { + return err + } + } + + return nil +} + +// upgradeDBPaths moves the databases from their locations prior to btcd +// version 0.2.0 to their new locations. +func upgradeDBPaths() error { + // Prior to version 0.2.0, the databases were in the "db" directory and + // their names were suffixed by "testnet" and "regtest" for their + // respective networks. Check for the old database and update it to the + // new path introduced with version 0.2.0 accordingly. + oldDbRoot := filepath.Join(oldBtcdHomeDir(), "db") + upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd.db"), "mainnet") + upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd_testnet.db"), "testnet") + upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd_regtest.db"), "regtest") + + // Remove the old db directory. + err := os.RemoveAll(oldDbRoot) + if err != nil { + return err + } + + return nil +} + +// upgradeDataPaths moves the application data from its location prior to btcd +// version 0.3.3 to its new location. +func upgradeDataPaths() error { + // No need to migrate if the old and new home paths are the same. + oldHomePath := oldBtcdHomeDir() + newHomePath := btcdHomeDir + if oldHomePath == newHomePath { + return nil + } + + // Only migrate if the old path exists and the new one doesn't. + if fileExists(oldHomePath) && !fileExists(newHomePath) { + // Create the new path. + btcdLog.Infof("Migrating application home path from '%s' to '%s'", + oldHomePath, newHomePath) + err := os.MkdirAll(newHomePath, 0700) + if err != nil { + return err + } + + // Move old btcd.conf into new location if needed. + oldConfPath := filepath.Join(oldHomePath, defaultConfigFilename) + newConfPath := filepath.Join(newHomePath, defaultConfigFilename) + if fileExists(oldConfPath) && !fileExists(newConfPath) { + err := os.Rename(oldConfPath, newConfPath) + if err != nil { + return err + } + } + + // Move old data directory into new location if needed. + oldDataPath := filepath.Join(oldHomePath, defaultDataDirname) + newDataPath := filepath.Join(newHomePath, defaultDataDirname) + if fileExists(oldDataPath) && !fileExists(newDataPath) { + err := os.Rename(oldDataPath, newDataPath) + if err != nil { + return err + } + } + + // Remove the old home if it is empty or show a warning if not. + ohpEmpty, err := dirEmpty(oldHomePath) + if err != nil { + return err + } + if ohpEmpty { + err := os.Remove(oldHomePath) + if err != nil { + return err + } + } else { + btcdLog.Warnf("Not removing '%s' since it contains files "+ + "not created by this application. You may "+ + "want to manually move them or delete them.", + oldHomePath) + } + } + + return nil +} + +// doUpgrades performs upgrades to btcd as new versions require it. +func doUpgrades() error { + err := upgradeDBPaths() + if err != nil { + return err + } + return upgradeDataPaths() +} diff --git a/vendor/github.com/btcsuite/btcd/upnp.go b/vendor/github.com/btcsuite/btcd/upnp.go new file mode 100644 index 0000000000000000000000000000000000000000..172d9eba9025bdc4b654b59f6efdcec55a1a8081 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/upnp.go @@ -0,0 +1,404 @@ +package main + +// Upnp code taken from Taipei Torrent license is below: +// Copyright (c) 2010 Jack Palevich. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Just enough UPnP to be able to forward ports +// + +import ( + "bytes" + "encoding/xml" + "errors" + "net" + "net/http" + "os" + "strconv" + "strings" + "time" +) + +// NAT is an interface representing a NAT traversal options for example UPNP or +// NAT-PMP. It provides methods to query and manipulate this traversal to allow +// access to services. +type NAT interface { + // Get the external address from outside the NAT. + GetExternalAddress() (addr net.IP, err error) + // Add a port mapping for protocol ("udp" or "tcp") from externalport to + // internal port with description lasting for timeout. + AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) + // Remove a previously added port mapping from externalport to + // internal port. + DeletePortMapping(protocol string, externalPort, internalPort int) (err error) +} + +type upnpNAT struct { + serviceURL string + ourIP string +} + +// Discover searches the local network for a UPnP router returning a NAT +// for the network if so, nil if not. +func Discover() (nat NAT, err error) { + ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900") + if err != nil { + return + } + conn, err := net.ListenPacket("udp4", ":0") + if err != nil { + return + } + socket := conn.(*net.UDPConn) + defer socket.Close() + + err = socket.SetDeadline(time.Now().Add(3 * time.Second)) + if err != nil { + return + } + + st := "ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n" + buf := bytes.NewBufferString( + "M-SEARCH * HTTP/1.1\r\n" + + "HOST: 239.255.255.250:1900\r\n" + + st + + "MAN: \"ssdp:discover\"\r\n" + + "MX: 2\r\n\r\n") + message := buf.Bytes() + answerBytes := make([]byte, 1024) + for i := 0; i < 3; i++ { + _, err = socket.WriteToUDP(message, ssdp) + if err != nil { + return + } + var n int + n, _, err = socket.ReadFromUDP(answerBytes) + if err != nil { + continue + // socket.Close() + // return + } + answer := string(answerBytes[0:n]) + if strings.Index(answer, "\r\n"+st) < 0 { + continue + } + // HTTP header field names are case-insensitive. + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 + locString := "\r\nlocation: " + locIndex := strings.Index(strings.ToLower(answer), locString) + if locIndex < 0 { + continue + } + loc := answer[locIndex+len(locString):] + endIndex := strings.Index(loc, "\r\n") + if endIndex < 0 { + continue + } + locURL := loc[0:endIndex] + var serviceURL string + serviceURL, err = getServiceURL(locURL) + if err != nil { + return + } + var ourIP string + ourIP, err = getOurIP() + if err != nil { + return + } + nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP} + return + } + err = errors.New("UPnP port discovery failed") + return +} + +// service represents the Service type in an UPnP xml description. +// Only the parts we care about are present and thus the xml may have more +// fields than present in the structure. +type service struct { + ServiceType string `xml:"serviceType"` + ControlURL string `xml:"controlURL"` +} + +// deviceList represents the deviceList type in an UPnP xml description. +// Only the parts we care about are present and thus the xml may have more +// fields than present in the structure. +type deviceList struct { + XMLName xml.Name `xml:"deviceList"` + Device []device `xml:"device"` +} + +// serviceList represents the serviceList type in an UPnP xml description. +// Only the parts we care about are present and thus the xml may have more +// fields than present in the structure. +type serviceList struct { + XMLName xml.Name `xml:"serviceList"` + Service []service `xml:"service"` +} + +// device represents the device type in an UPnP xml description. +// Only the parts we care about are present and thus the xml may have more +// fields than present in the structure. +type device struct { + XMLName xml.Name `xml:"device"` + DeviceType string `xml:"deviceType"` + DeviceList deviceList `xml:"deviceList"` + ServiceList serviceList `xml:"serviceList"` +} + +// specVersion represents the specVersion in a UPnP xml description. +// Only the parts we care about are present and thus the xml may have more +// fields than present in the structure. +type specVersion struct { + XMLName xml.Name `xml:"specVersion"` + Major int `xml:"major"` + Minor int `xml:"minor"` +} + +// root represents the Root document for a UPnP xml description. +// Only the parts we care about are present and thus the xml may have more +// fields than present in the structure. +type root struct { + XMLName xml.Name `xml:"root"` + SpecVersion specVersion + Device device +} + +// getChildDevice searches the children of device for a device with the given +// type. +func getChildDevice(d *device, deviceType string) *device { + for i := range d.DeviceList.Device { + if d.DeviceList.Device[i].DeviceType == deviceType { + return &d.DeviceList.Device[i] + } + } + return nil +} + +// getChildDevice searches the service list of device for a service with the +// given type. +func getChildService(d *device, serviceType string) *service { + for i := range d.ServiceList.Service { + if d.ServiceList.Service[i].ServiceType == serviceType { + return &d.ServiceList.Service[i] + } + } + return nil +} + +// getOurIP returns a best guess at what the local IP is. +func getOurIP() (ip string, err error) { + hostname, err := os.Hostname() + if err != nil { + return + } + return net.LookupCNAME(hostname) +} + +// getServiceURL parses the xml description at the given root url to find the +// url for the WANIPConnection service to be used for port forwarding. +func getServiceURL(rootURL string) (url string, err error) { + r, err := http.Get(rootURL) + if err != nil { + return + } + defer r.Body.Close() + if r.StatusCode >= 400 { + err = errors.New(string(r.StatusCode)) + return + } + var root root + err = xml.NewDecoder(r.Body).Decode(&root) + if err != nil { + return + } + a := &root.Device + if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" { + err = errors.New("no InternetGatewayDevice") + return + } + b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1") + if b == nil { + err = errors.New("no WANDevice") + return + } + c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1") + if c == nil { + err = errors.New("no WANConnectionDevice") + return + } + d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1") + if d == nil { + err = errors.New("no WANIPConnection") + return + } + url = combineURL(rootURL, d.ControlURL) + return +} + +// combineURL appends subURL onto rootURL. +func combineURL(rootURL, subURL string) string { + protocolEnd := "://" + protoEndIndex := strings.Index(rootURL, protocolEnd) + a := rootURL[protoEndIndex+len(protocolEnd):] + rootIndex := strings.Index(a, "/") + return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL +} + +// soapBody represents the <s:Body> element in a SOAP reply. +// fields we don't care about are elided. +type soapBody struct { + XMLName xml.Name `xml:"Body"` + Data []byte `xml:",innerxml"` +} + +// soapEnvelope represents the <s:Envelope> element in a SOAP reply. +// fields we don't care about are elided. +type soapEnvelope struct { + XMLName xml.Name `xml:"Envelope"` + Body soapBody `xml:"Body"` +} + +// soapRequests performs a soap request with the given parameters and returns +// the xml replied stripped of the soap headers. in the case that the request is +// unsuccessful the an error is returned. +func soapRequest(url, function, message string) (replyXML []byte, err error) { + fullMessage := "<?xml version=\"1.0\" ?>" + + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" + + "<s:Body>" + message + "</s:Body></s:Envelope>" + + req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"") + req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3") + //req.Header.Set("Transfer-Encoding", "chunked") + req.Header.Set("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#"+function+"\"") + req.Header.Set("Connection", "Close") + req.Header.Set("Cache-Control", "no-cache") + req.Header.Set("Pragma", "no-cache") + + r, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + if r.Body != nil { + defer r.Body.Close() + } + + if r.StatusCode >= 400 { + // log.Stderr(function, r.StatusCode) + err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function) + r = nil + return + } + var reply soapEnvelope + err = xml.NewDecoder(r.Body).Decode(&reply) + if err != nil { + return nil, err + } + return reply.Body.Data, nil +} + +// getExternalIPAddressResponse represents the XML response to a +// GetExternalIPAddress SOAP request. +type getExternalIPAddressResponse struct { + XMLName xml.Name `xml:"GetExternalIPAddressResponse"` + ExternalIPAddress string `xml:"NewExternalIPAddress"` +} + +// GetExternalAddress implements the NAT interface by fetching the external IP +// from the UPnP router. +func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) { + message := "<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\"/>\r\n" + response, err := soapRequest(n.serviceURL, "GetExternalIPAddress", message) + if err != nil { + return nil, err + } + + var reply getExternalIPAddressResponse + err = xml.Unmarshal(response, &reply) + if err != nil { + return nil, err + } + + addr = net.ParseIP(reply.ExternalIPAddress) + if addr == nil { + return nil, errors.New("unable to parse ip address") + } + return addr, nil +} + +// AddPortMapping implements the NAT interface by setting up a port forwarding +// from the UPnP router to the local machine with the given ports and protocol. +func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) { + // A single concatenation would break ARM compilation. + message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" + + "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) + message += "</NewExternalPort><NewProtocol>" + strings.ToUpper(protocol) + "</NewProtocol>" + message += "<NewInternalPort>" + strconv.Itoa(internalPort) + "</NewInternalPort>" + + "<NewInternalClient>" + n.ourIP + "</NewInternalClient>" + + "<NewEnabled>1</NewEnabled><NewPortMappingDescription>" + message += description + + "</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) + + "</NewLeaseDuration></u:AddPortMapping>" + + response, err := soapRequest(n.serviceURL, "AddPortMapping", message) + if err != nil { + return + } + + // TODO: check response to see if the port was forwarded + // If the port was not wildcard we don't get an reply with the port in + // it. Not sure about wildcard yet. miniupnpc just checks for error + // codes here. + mappedExternalPort = externalPort + _ = response + return +} + +// DeletePortMapping implements the NAT interface by removing up a port forwarding +// from the UPnP router to the local machine with the given ports and. +func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) { + + message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" + + "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) + + "</NewExternalPort><NewProtocol>" + strings.ToUpper(protocol) + "</NewProtocol>" + + "</u:DeletePortMapping>" + + response, err := soapRequest(n.serviceURL, "DeletePortMapping", message) + if err != nil { + return + } + + // TODO: check response to see if the port was deleted + // log.Println(message, response) + _ = response + return +} diff --git a/vendor/github.com/btcsuite/btcd/version.go b/vendor/github.com/btcsuite/btcd/version.go new file mode 100644 index 0000000000000000000000000000000000000000..192f4a7f585e8d02a79800ef03b3758fab9c5995 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/version.go @@ -0,0 +1,72 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "fmt" + "strings" +) + +// semanticAlphabet +const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-" + +// These constants define the application version and follow the semantic +// versioning 2.0.0 spec (http://semver.org/). +const ( + appMajor uint = 0 + appMinor uint = 12 + appPatch uint = 0 + + // appPreRelease MUST only contain characters from semanticAlphabet + // per the semantic versioning spec. + appPreRelease = "beta" +) + +// appBuild is defined as a variable so it can be overridden during the build +// process with '-ldflags "-X main.appBuild foo' if needed. It MUST only +// contain characters from semanticAlphabet per the semantic versioning spec. +var appBuild string + +// version returns the application version as a properly formed string per the +// semantic versioning 2.0.0 spec (http://semver.org/). +func version() string { + // Start with the major, minor, and patch versions. + version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch) + + // Append pre-release version if there is one. The hyphen called for + // by the semantic versioning spec is automatically appended and should + // not be contained in the pre-release string. The pre-release version + // is not appended if it contains invalid characters. + preRelease := normalizeVerString(appPreRelease) + if preRelease != "" { + version = fmt.Sprintf("%s-%s", version, preRelease) + } + + // Append build metadata if there is any. The plus called for + // by the semantic versioning spec is automatically appended and should + // not be contained in the build metadata string. The build metadata + // string is not appended if it contains invalid characters. + build := normalizeVerString(appBuild) + if build != "" { + version = fmt.Sprintf("%s+%s", version, build) + } + + return version +} + +// normalizeVerString returns the passed string stripped of all characters which +// are not valid according to the semantic versioning guidelines for pre-release +// version and build metadata strings. In particular they MUST only contain +// characters in semanticAlphabet. +func normalizeVerString(str string) string { + var result bytes.Buffer + for _, r := range str { + if strings.ContainsRune(semanticAlphabet, r) { + result.WriteRune(r) + } + } + return result.String() +} diff --git a/vendor/github.com/btcsuite/btcd/wire/README.md b/vendor/github.com/btcsuite/btcd/wire/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0e937a62ffaa5ecc5510d11e7ececa130f371aa3 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/README.md @@ -0,0 +1,114 @@ +wire +==== + +[] +(https://travis-ci.org/btcsuite/btcd) [![ISC License] +(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[] +(http://godoc.org/github.com/btcsuite/btcd/wire) + +Package wire implements the bitcoin wire protocol. A comprehensive suite of +tests with 100% test coverage is provided to ensure proper functionality. + +There is an associated blog post about the release of this package +[here](https://blog.conformal.com/btcwire-the-bitcoin-wire-protocol-package-from-btcd/). + +This package has intentionally been designed so it can be used as a standalone +package for any projects needing to interface with bitcoin peers at the wire +protocol level. + +## Installation and Updating + +```bash +$ go get -u github.com/btcsuite/btcd/wire +``` + +## Bitcoin Message Overview + +The bitcoin protocol consists of exchanging messages between peers. Each message +is preceded by a header which identifies information about it such as which +bitcoin network it is a part of, its type, how big it is, and a checksum to +verify validity. All encoding and decoding of message headers is handled by this +package. + +To accomplish this, there is a generic interface for bitcoin messages named +`Message` which allows messages of any type to be read, written, or passed +around through channels, functions, etc. In addition, concrete implementations +of most of the currently supported bitcoin messages are provided. For these +supported messages, all of the details of marshalling and unmarshalling to and +from the wire using bitcoin encoding are handled so the caller doesn't have to +concern themselves with the specifics. + +## Reading Messages Example + +In order to unmarshal bitcoin messages from the wire, use the `ReadMessage` +function. It accepts any `io.Reader`, but typically this will be a `net.Conn` +to a remote node running a bitcoin peer. Example syntax is: + +```Go + // Use the most recent protocol version supported by the package and the + // main bitcoin network. + pver := wire.ProtocolVersion + btcnet := wire.MainNet + + // Reads and validates the next bitcoin message from conn using the + // protocol version pver and the bitcoin network btcnet. The returns + // are a wire.Message, a []byte which contains the unmarshalled + // raw payload, and a possible error. + msg, rawPayload, err := wire.ReadMessage(conn, pver, btcnet) + if err != nil { + // Log and handle the error + } +``` + +See the package documentation for details on determining the message type. + +## Writing Messages Example + +In order to marshal bitcoin messages to the wire, use the `WriteMessage` +function. It accepts any `io.Writer`, but typically this will be a `net.Conn` +to a remote node running a bitcoin peer. Example syntax to request addresses +from a remote peer is: + +```Go + // Use the most recent protocol version supported by the package and the + // main bitcoin network. + pver := wire.ProtocolVersion + btcnet := wire.MainNet + + // Create a new getaddr bitcoin message. + msg := wire.NewMsgGetAddr() + + // Writes a bitcoin message msg to conn using the protocol version + // pver, and the bitcoin network btcnet. The return is a possible + // error. + err := wire.WriteMessage(conn, msg, pver, btcnet) + if err != nil { + // Log and handle the error + } +``` + +## GPG Verification Key + +All official release tags are signed by Conformal so users can ensure the code +has not been tampered with and is coming from the btcsuite developers. To +verify the signature perform the following: + +- Download the public key from the Conformal website at + https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt + +- Import the public key into your GPG keyring: + ```bash + gpg --import GIT-GPG-KEY-conformal.txt + ``` + +- Verify the release tag with the following command where `TAG_NAME` is a + placeholder for the specific tag: + ```bash + git tag -v TAG_NAME + ``` + +## License + +Package wire is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/vendor/github.com/btcsuite/btcd/wire/bench_test.go b/vendor/github.com/btcsuite/btcd/wire/bench_test.go new file mode 100644 index 0000000000000000000000000000000000000000..cef5af4fc7aa5a7533e75f9eeb24ce265c524f0d --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/bench_test.go @@ -0,0 +1,428 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "bytes" + "io/ioutil" + "testing" + "time" +) + +// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for +// the main network, regression test network, and test network (version 3). +var genesisCoinbaseTx = MsgTx{ + Version: 1, + TxIn: []*TxIn{ + { + PreviousOutPoint: OutPoint{ + Hash: ShaHash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{ + 0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, 0x45, /* |.......E| */ + 0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6d, 0x65, /* |The Time| */ + 0x73, 0x20, 0x30, 0x33, 0x2f, 0x4a, 0x61, 0x6e, /* |s 03/Jan| */ + 0x2f, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, 0x68, /* |/2009 Ch| */ + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x6f, 0x72, /* |ancellor| */ + 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x72, 0x69, 0x6e, /* | on brin| */ + 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x63, /* |k of sec|*/ + 0x6f, 0x6e, 0x64, 0x20, 0x62, 0x61, 0x69, 0x6c, /* |ond bail| */ + 0x6f, 0x75, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, /* |out for |*/ + 0x62, 0x61, 0x6e, 0x6b, 0x73, /* |banks| */ + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*TxOut{ + { + Value: 0x12a05f200, + PkScript: []byte{ + 0x41, 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, /* |A.g....U| */ + 0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, /* |H'.g..q0| */ + 0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, /* |..\..(.9| */ + 0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, /* |..yb...a| */ + 0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, /* |..I..?L.| */ + 0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, /* |8..U....| */ + 0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, /* |..\8M...| */ + 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, /* |.W.Lp+k.| */ + 0x1d, 0x5f, 0xac, /* |._.| */ + }, + }, + }, + LockTime: 0, +} + +// blockOne is the first block in the mainnet block chain. +var blockOne = MsgBlock{ + Header: BlockHeader{ + Version: 1, + PrevBlock: ShaHash([HashSize]byte{ // Make go vet happy. + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + }), + MerkleRoot: ShaHash([HashSize]byte{ // Make go vet happy. + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, + }), + + Timestamp: time.Unix(0x4966bc61, 0), // 2009-01-08 20:54:25 -0600 CST + Bits: 0x1d00ffff, // 486604799 + Nonce: 0x9962e301, // 2573394689 + }, + Transactions: []*MsgTx{ + { + Version: 1, + TxIn: []*TxIn{ + { + PreviousOutPoint: OutPoint{ + Hash: ShaHash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{ + 0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*TxOut{ + { + Value: 0x12a05f200, + PkScript: []byte{ + 0x41, // OP_DATA_65 + 0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c, + 0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16, + 0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c, + 0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c, + 0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4, + 0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6, + 0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e, + 0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58, + 0xee, // 65-byte signature + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 0, + }, + }, +} + +// BenchmarkWriteVarInt1 performs a benchmark on how long it takes to write +// a single byte variable length integer. +func BenchmarkWriteVarInt1(b *testing.B) { + for i := 0; i < b.N; i++ { + WriteVarInt(ioutil.Discard, 0, 1) + } +} + +// BenchmarkWriteVarInt3 performs a benchmark on how long it takes to write +// a three byte variable length integer. +func BenchmarkWriteVarInt3(b *testing.B) { + for i := 0; i < b.N; i++ { + WriteVarInt(ioutil.Discard, 0, 65535) + } +} + +// BenchmarkWriteVarInt5 performs a benchmark on how long it takes to write +// a five byte variable length integer. +func BenchmarkWriteVarInt5(b *testing.B) { + for i := 0; i < b.N; i++ { + WriteVarInt(ioutil.Discard, 0, 4294967295) + } +} + +// BenchmarkWriteVarInt9 performs a benchmark on how long it takes to write +// a nine byte variable length integer. +func BenchmarkWriteVarInt9(b *testing.B) { + for i := 0; i < b.N; i++ { + WriteVarInt(ioutil.Discard, 0, 18446744073709551615) + } +} + +// BenchmarkReadVarInt1 performs a benchmark on how long it takes to read +// a single byte variable length integer. +func BenchmarkReadVarInt1(b *testing.B) { + buf := []byte{0x01} + for i := 0; i < b.N; i++ { + ReadVarInt(bytes.NewReader(buf), 0) + } +} + +// BenchmarkReadVarInt3 performs a benchmark on how long it takes to read +// a three byte variable length integer. +func BenchmarkReadVarInt3(b *testing.B) { + buf := []byte{0x0fd, 0xff, 0xff} + for i := 0; i < b.N; i++ { + ReadVarInt(bytes.NewReader(buf), 0) + } +} + +// BenchmarkReadVarInt5 performs a benchmark on how long it takes to read +// a five byte variable length integer. +func BenchmarkReadVarInt5(b *testing.B) { + buf := []byte{0xfe, 0xff, 0xff, 0xff, 0xff} + for i := 0; i < b.N; i++ { + ReadVarInt(bytes.NewReader(buf), 0) + } +} + +// BenchmarkReadVarInt9 performs a benchmark on how long it takes to read +// a nine byte variable length integer. +func BenchmarkReadVarInt9(b *testing.B) { + buf := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + for i := 0; i < b.N; i++ { + ReadVarInt(bytes.NewReader(buf), 0) + } +} + +// BenchmarkReadVarStr4 performs a benchmark on how long it takes to read a +// four byte variable length string. +func BenchmarkReadVarStr4(b *testing.B) { + buf := []byte{0x04, 't', 'e', 's', 't'} + for i := 0; i < b.N; i++ { + ReadVarString(bytes.NewReader(buf), 0) + } +} + +// BenchmarkReadVarStr10 performs a benchmark on how long it takes to read a +// ten byte variable length string. +func BenchmarkReadVarStr10(b *testing.B) { + buf := []byte{0x0a, 't', 'e', 's', 't', '0', '1', '2', '3', '4', '5'} + for i := 0; i < b.N; i++ { + ReadVarString(bytes.NewReader(buf), 0) + } +} + +// BenchmarkWriteVarStr4 performs a benchmark on how long it takes to write a +// four byte variable length string. +func BenchmarkWriteVarStr4(b *testing.B) { + for i := 0; i < b.N; i++ { + WriteVarString(ioutil.Discard, 0, "test") + } +} + +// BenchmarkWriteVarStr10 performs a benchmark on how long it takes to write a +// ten byte variable length string. +func BenchmarkWriteVarStr10(b *testing.B) { + for i := 0; i < b.N; i++ { + WriteVarString(ioutil.Discard, 0, "test012345") + } +} + +// BenchmarkReadOutPoint performs a benchmark on how long it takes to read a +// transaction output point. +func BenchmarkReadOutPoint(b *testing.B) { + buf := []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash + 0xff, 0xff, 0xff, 0xff, // Previous output index + } + var op OutPoint + for i := 0; i < b.N; i++ { + readOutPoint(bytes.NewReader(buf), 0, 0, &op) + } +} + +// BenchmarkWriteOutPoint performs a benchmark on how long it takes to write a +// transaction output point. +func BenchmarkWriteOutPoint(b *testing.B) { + op := &OutPoint{ + Hash: ShaHash{}, + Index: 0, + } + for i := 0; i < b.N; i++ { + writeOutPoint(ioutil.Discard, 0, 0, op) + } +} + +// BenchmarkReadTxOut performs a benchmark on how long it takes to read a +// transaction output. +func BenchmarkReadTxOut(b *testing.B) { + buf := []byte{ + 0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount + 0x43, // Varint for length of pk script + 0x41, // OP_DATA_65 + 0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c, + 0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16, + 0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c, + 0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c, + 0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4, + 0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6, + 0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e, + 0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58, + 0xee, // 65-byte signature + 0xac, // OP_CHECKSIG + } + var txOut TxOut + for i := 0; i < b.N; i++ { + readTxOut(bytes.NewReader(buf), 0, 0, &txOut) + } +} + +// BenchmarkWriteTxOut performs a benchmark on how long it takes to write +// a transaction output. +func BenchmarkWriteTxOut(b *testing.B) { + txOut := blockOne.Transactions[0].TxOut[0] + for i := 0; i < b.N; i++ { + writeTxOut(ioutil.Discard, 0, 0, txOut) + } +} + +// BenchmarkReadTxIn performs a benchmark on how long it takes to read a +// transaction input. +func BenchmarkReadTxIn(b *testing.B) { + buf := []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash + 0xff, 0xff, 0xff, 0xff, // Previous output index + 0x07, // Varint for length of signature script + 0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, // Signature script + 0xff, 0xff, 0xff, 0xff, // Sequence + } + var txIn TxIn + for i := 0; i < b.N; i++ { + readTxIn(bytes.NewReader(buf), 0, 0, &txIn) + } +} + +// BenchmarkWriteTxIn performs a benchmark on how long it takes to write +// a transaction input. +func BenchmarkWriteTxIn(b *testing.B) { + txIn := blockOne.Transactions[0].TxIn[0] + for i := 0; i < b.N; i++ { + writeTxIn(ioutil.Discard, 0, 0, txIn) + } +} + +// BenchmarkDeserializeTx performs a benchmark on how long it takes to +// deserialize a transaction. +func BenchmarkDeserializeTx(b *testing.B) { + buf := []byte{ + 0x01, 0x00, 0x00, 0x00, // Version + 0x01, // Varint for number of input transactions + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // // Previous output hash + 0xff, 0xff, 0xff, 0xff, // Prevous output index + 0x07, // Varint for length of signature script + 0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, // Signature script + 0xff, 0xff, 0xff, 0xff, // Sequence + 0x01, // Varint for number of output transactions + 0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount + 0x43, // Varint for length of pk script + 0x41, // OP_DATA_65 + 0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c, + 0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16, + 0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c, + 0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c, + 0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4, + 0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6, + 0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e, + 0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58, + 0xee, // 65-byte signature + 0xac, // OP_CHECKSIG + 0x00, 0x00, 0x00, 0x00, // Lock time + } + var tx MsgTx + for i := 0; i < b.N; i++ { + tx.Deserialize(bytes.NewReader(buf)) + + } +} + +// BenchmarkSerializeTx performs a benchmark on how long it takes to serialize +// a transaction. +func BenchmarkSerializeTx(b *testing.B) { + tx := blockOne.Transactions[0] + for i := 0; i < b.N; i++ { + tx.Serialize(ioutil.Discard) + + } +} + +// BenchmarkReadBlockHeader performs a benchmark on how long it takes to +// deserialize a block header. +func BenchmarkReadBlockHeader(b *testing.B) { + buf := []byte{ + 0x01, 0x00, 0x00, 0x00, // Version 1 + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock + 0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, + 0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61, + 0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32, + 0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, // MerkleRoot + 0x29, 0xab, 0x5f, 0x49, // Timestamp + 0xff, 0xff, 0x00, 0x1d, // Bits + 0xf3, 0xe0, 0x01, 0x00, // Nonce + 0x00, // TxnCount Varint + } + var header BlockHeader + for i := 0; i < b.N; i++ { + readBlockHeader(bytes.NewReader(buf), 0, &header) + } +} + +// BenchmarkWriteBlockHeader performs a benchmark on how long it takes to +// serialize a block header. +func BenchmarkWriteBlockHeader(b *testing.B) { + header := blockOne.Header + for i := 0; i < b.N; i++ { + writeBlockHeader(ioutil.Discard, 0, &header) + } +} + +// BenchmarkTxSha performs a benchmark on how long it takes to hash a +// transaction. +func BenchmarkTxSha(b *testing.B) { + for i := 0; i < b.N; i++ { + genesisCoinbaseTx.TxSha() + } +} + +// BenchmarkDoubleSha256 performs a benchmark on how long it takes to perform a +// double sha 256 returning a byte slice. +func BenchmarkDoubleSha256(b *testing.B) { + b.StopTimer() + var buf bytes.Buffer + if err := genesisCoinbaseTx.Serialize(&buf); err != nil { + b.Errorf("Serialize: unexpected error: %v", err) + return + } + txBytes := buf.Bytes() + b.StartTimer() + + for i := 0; i < b.N; i++ { + _ = DoubleSha256(txBytes) + } +} + +// BenchmarkDoubleSha256SH performs a benchmark on how long it takes to perform +// a double sha 256 returning a ShaHash. +func BenchmarkDoubleSha256SH(b *testing.B) { + b.StopTimer() + var buf bytes.Buffer + if err := genesisCoinbaseTx.Serialize(&buf); err != nil { + b.Errorf("Serialize: unexpected error: %v", err) + return + } + txBytes := buf.Bytes() + b.StartTimer() + + for i := 0; i < b.N; i++ { + _ = DoubleSha256SH(txBytes) + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/blockheader.go b/vendor/github.com/btcsuite/btcd/wire/blockheader.go new file mode 100644 index 0000000000000000000000000000000000000000..941d40a541183b87303e3f86c93d692472e523f3 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/blockheader.go @@ -0,0 +1,141 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "bytes" + "io" + "time" +) + +// BlockVersion is the current latest supported block version. +const BlockVersion = 4 + +// MaxBlockHeaderPayload is the maximum number of bytes a block header can be. +// Version 4 bytes + Timestamp 4 bytes + Bits 4 bytes + Nonce 4 bytes + +// PrevBlock and MerkleRoot hashes. +const MaxBlockHeaderPayload = 16 + (HashSize * 2) + +// BlockHeader defines information about a block and is used in the bitcoin +// block (MsgBlock) and headers (MsgHeaders) messages. +type BlockHeader struct { + // Version of the block. This is not the same as the protocol version. + Version int32 + + // Hash of the previous block in the block chain. + PrevBlock ShaHash + + // Merkle tree reference to hash of all transactions for the block. + MerkleRoot ShaHash + + // Time the block was created. This is, unfortunately, encoded as a + // uint32 on the wire and therefore is limited to 2106. + Timestamp time.Time + + // Difficulty target for the block. + Bits uint32 + + // Nonce used to generate the block. + Nonce uint32 +} + +// blockHeaderLen is a constant that represents the number of bytes for a block +// header. +const blockHeaderLen = 80 + +// BlockSha computes the block identifier hash for the given block header. +func (h *BlockHeader) BlockSha() ShaHash { + // Encode the header and double sha256 everything prior to the number of + // transactions. Ignore the error returns since there is no way the + // encode could fail except being out of memory which would cause a + // run-time panic. + var buf bytes.Buffer + _ = writeBlockHeader(&buf, 0, h) + + return DoubleSha256SH(buf.Bytes()) +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +// See Deserialize for decoding block headers stored to disk, such as in a +// database, as opposed to decoding block headers from the wire. +func (h *BlockHeader) BtcDecode(r io.Reader, pver uint32) error { + return readBlockHeader(r, pver, h) +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +// See Serialize for encoding block headers to be stored to disk, such as in a +// database, as opposed to encoding block headers for the wire. +func (h *BlockHeader) BtcEncode(w io.Writer, pver uint32) error { + return writeBlockHeader(w, pver, h) +} + +// Deserialize decodes a block header from r into the receiver using a format +// that is suitable for long-term storage such as a database while respecting +// the Version field. +func (h *BlockHeader) Deserialize(r io.Reader) error { + // At the current time, there is no difference between the wire encoding + // at protocol version 0 and the stable long-term storage format. As + // a result, make use of readBlockHeader. + return readBlockHeader(r, 0, h) +} + +// Serialize encodes a block header from r into the receiver using a format +// that is suitable for long-term storage such as a database while respecting +// the Version field. +func (h *BlockHeader) Serialize(w io.Writer) error { + // At the current time, there is no difference between the wire encoding + // at protocol version 0 and the stable long-term storage format. As + // a result, make use of writeBlockHeader. + return writeBlockHeader(w, 0, h) +} + +// NewBlockHeader returns a new BlockHeader using the provided previous block +// hash, merkle root hash, difficulty bits, and nonce used to generate the +// block with defaults for the remaining fields. +func NewBlockHeader(prevHash *ShaHash, merkleRootHash *ShaHash, bits uint32, + nonce uint32) *BlockHeader { + + // Limit the timestamp to one second precision since the protocol + // doesn't support better. + return &BlockHeader{ + Version: BlockVersion, + PrevBlock: *prevHash, + MerkleRoot: *merkleRootHash, + Timestamp: time.Unix(time.Now().Unix(), 0), + Bits: bits, + Nonce: nonce, + } +} + +// readBlockHeader reads a bitcoin block header from r. See Deserialize for +// decoding block headers stored to disk, such as in a database, as opposed to +// decoding from the wire. +func readBlockHeader(r io.Reader, pver uint32, bh *BlockHeader) error { + var sec uint32 + err := readElements(r, &bh.Version, &bh.PrevBlock, &bh.MerkleRoot, &sec, + &bh.Bits, &bh.Nonce) + if err != nil { + return err + } + bh.Timestamp = time.Unix(int64(sec), 0) + + return nil +} + +// writeBlockHeader writes a bitcoin block header to w. See Serialize for +// encoding block headers to be stored to disk, such as in a database, as +// opposed to encoding for the wire. +func writeBlockHeader(w io.Writer, pver uint32, bh *BlockHeader) error { + sec := uint32(bh.Timestamp.Unix()) + err := writeElements(w, bh.Version, &bh.PrevBlock, &bh.MerkleRoot, + sec, bh.Bits, bh.Nonce) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/wire/blockheader_test.go b/vendor/github.com/btcsuite/btcd/wire/blockheader_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c6d76bfd4be1f3833edde0b5d3701de81cd765c3 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/blockheader_test.go @@ -0,0 +1,256 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "reflect" + "testing" + "time" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestBlockHeader tests the BlockHeader API. +func TestBlockHeader(t *testing.T) { + nonce64, err := wire.RandomUint64() + if err != nil { + t.Errorf("RandomUint64: Error generating nonce: %v", err) + } + nonce := uint32(nonce64) + + hash := mainNetGenesisHash + merkleHash := mainNetGenesisMerkleRoot + bits := uint32(0x1d00ffff) + bh := wire.NewBlockHeader(&hash, &merkleHash, bits, nonce) + + // Ensure we get the same data back out. + if !bh.PrevBlock.IsEqual(&hash) { + t.Errorf("NewBlockHeader: wrong prev hash - got %v, want %v", + spew.Sprint(bh.PrevBlock), spew.Sprint(hash)) + } + if !bh.MerkleRoot.IsEqual(&merkleHash) { + t.Errorf("NewBlockHeader: wrong merkle root - got %v, want %v", + spew.Sprint(bh.MerkleRoot), spew.Sprint(merkleHash)) + } + if bh.Bits != bits { + t.Errorf("NewBlockHeader: wrong bits - got %v, want %v", + bh.Bits, bits) + } + if bh.Nonce != nonce { + t.Errorf("NewBlockHeader: wrong nonce - got %v, want %v", + bh.Nonce, nonce) + } +} + +// TestBlockHeaderWire tests the BlockHeader wire encode and decode for various +// protocol versions. +func TestBlockHeaderWire(t *testing.T) { + nonce := uint32(123123) // 0x1e0f3 + pver := uint32(70001) + + // baseBlockHdr is used in the various tests as a baseline BlockHeader. + bits := uint32(0x1d00ffff) + baseBlockHdr := &wire.BlockHeader{ + Version: 1, + PrevBlock: mainNetGenesisHash, + MerkleRoot: mainNetGenesisMerkleRoot, + Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST + Bits: bits, + Nonce: nonce, + } + + // baseBlockHdrEncoded is the wire encoded bytes of baseBlockHdr. + baseBlockHdrEncoded := []byte{ + 0x01, 0x00, 0x00, 0x00, // Version 1 + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock + 0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, + 0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61, + 0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32, + 0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, // MerkleRoot + 0x29, 0xab, 0x5f, 0x49, // Timestamp + 0xff, 0xff, 0x00, 0x1d, // Bits + 0xf3, 0xe0, 0x01, 0x00, // Nonce + } + + tests := []struct { + in *wire.BlockHeader // Data to encode + out *wire.BlockHeader // Expected decoded data + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + baseBlockHdr, + baseBlockHdr, + baseBlockHdrEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version. + { + baseBlockHdr, + baseBlockHdr, + baseBlockHdrEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version. + { + baseBlockHdr, + baseBlockHdr, + baseBlockHdrEncoded, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion. + { + baseBlockHdr, + baseBlockHdr, + baseBlockHdrEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion. + { + baseBlockHdr, + baseBlockHdr, + baseBlockHdrEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + var buf bytes.Buffer + err := wire.TstWriteBlockHeader(&buf, test.pver, test.in) + if err != nil { + t.Errorf("writeBlockHeader #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("writeBlockHeader #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + buf.Reset() + err = test.in.BtcEncode(&buf, pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the block header from wire format. + var bh wire.BlockHeader + rbuf := bytes.NewReader(test.buf) + err = wire.TstReadBlockHeader(rbuf, test.pver, &bh) + if err != nil { + t.Errorf("readBlockHeader #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&bh, test.out) { + t.Errorf("readBlockHeader #%d\n got: %s want: %s", i, + spew.Sdump(&bh), spew.Sdump(test.out)) + continue + } + + rbuf = bytes.NewReader(test.buf) + err = bh.BtcDecode(rbuf, pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&bh, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(&bh), spew.Sdump(test.out)) + continue + } + } +} + +// TestBlockHeaderSerialize tests BlockHeader serialize and deserialize. +func TestBlockHeaderSerialize(t *testing.T) { + nonce := uint32(123123) // 0x1e0f3 + + // baseBlockHdr is used in the various tests as a baseline BlockHeader. + bits := uint32(0x1d00ffff) + baseBlockHdr := &wire.BlockHeader{ + Version: 1, + PrevBlock: mainNetGenesisHash, + MerkleRoot: mainNetGenesisMerkleRoot, + Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST + Bits: bits, + Nonce: nonce, + } + + // baseBlockHdrEncoded is the wire encoded bytes of baseBlockHdr. + baseBlockHdrEncoded := []byte{ + 0x01, 0x00, 0x00, 0x00, // Version 1 + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock + 0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, + 0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61, + 0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32, + 0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, // MerkleRoot + 0x29, 0xab, 0x5f, 0x49, // Timestamp + 0xff, 0xff, 0x00, 0x1d, // Bits + 0xf3, 0xe0, 0x01, 0x00, // Nonce + } + + tests := []struct { + in *wire.BlockHeader // Data to encode + out *wire.BlockHeader // Expected decoded data + buf []byte // Serialized data + }{ + { + baseBlockHdr, + baseBlockHdr, + baseBlockHdrEncoded, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Serialize the block header. + var buf bytes.Buffer + err := test.in.Serialize(&buf) + if err != nil { + t.Errorf("Serialize #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("Serialize #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Deserialize the block header. + var bh wire.BlockHeader + rbuf := bytes.NewReader(test.buf) + err = bh.Deserialize(rbuf) + if err != nil { + t.Errorf("Deserialize #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&bh, test.out) { + t.Errorf("Deserialize #%d\n got: %s want: %s", i, + spew.Sdump(&bh), spew.Sdump(test.out)) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/common.go b/vendor/github.com/btcsuite/btcd/wire/common.go new file mode 100644 index 0000000000000000000000000000000000000000..c908fa3ade9e82585d396e3148e9f75609e2ea2c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/common.go @@ -0,0 +1,564 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "crypto/rand" + "encoding/binary" + "fmt" + "io" + "math" + + "github.com/btcsuite/fastsha256" +) + +// MaxVarIntPayload is the maximum payload size for a variable length integer. +const MaxVarIntPayload = 9 + +// errNonCanonicalVarInt is the common format string used for non-canonically +// encoded variable length integer errors. +var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " + + "encode a value greater than %x" + +// readElement reads the next sequence of bytes from r using little endian +// depending on the concrete type of element pointed to. +func readElement(r io.Reader, element interface{}) error { + var scratch [8]byte + + // Attempt to read the element based on the concrete type via fast + // type assertions first. + switch e := element.(type) { + case *int32: + b := scratch[0:4] + _, err := io.ReadFull(r, b) + if err != nil { + return err + } + *e = int32(binary.LittleEndian.Uint32(b)) + return nil + + case *uint32: + b := scratch[0:4] + _, err := io.ReadFull(r, b) + if err != nil { + return err + } + *e = binary.LittleEndian.Uint32(b) + return nil + + case *int64: + b := scratch[0:8] + _, err := io.ReadFull(r, b) + if err != nil { + return err + } + *e = int64(binary.LittleEndian.Uint64(b)) + return nil + + case *uint64: + b := scratch[0:8] + _, err := io.ReadFull(r, b) + if err != nil { + return err + } + *e = binary.LittleEndian.Uint64(b) + return nil + + case *bool: + b := scratch[0:1] + _, err := io.ReadFull(r, b) + if err != nil { + return err + } + if b[0] == 0x00 { + *e = false + } else { + *e = true + } + return nil + + // Message header checksum. + case *[4]byte: + _, err := io.ReadFull(r, e[:]) + if err != nil { + return err + } + return nil + + // Message header command. + case *[CommandSize]uint8: + _, err := io.ReadFull(r, e[:]) + if err != nil { + return err + } + return nil + + // IP address. + case *[16]byte: + _, err := io.ReadFull(r, e[:]) + if err != nil { + return err + } + return nil + + case *ShaHash: + _, err := io.ReadFull(r, e[:]) + if err != nil { + return err + } + return nil + + case *ServiceFlag: + b := scratch[0:8] + _, err := io.ReadFull(r, b) + if err != nil { + return err + } + *e = ServiceFlag(binary.LittleEndian.Uint64(b)) + return nil + + case *InvType: + b := scratch[0:4] + _, err := io.ReadFull(r, b) + if err != nil { + return err + } + *e = InvType(binary.LittleEndian.Uint32(b)) + return nil + + case *BitcoinNet: + b := scratch[0:4] + _, err := io.ReadFull(r, b) + if err != nil { + return err + } + *e = BitcoinNet(binary.LittleEndian.Uint32(b)) + return nil + + case *BloomUpdateType: + b := scratch[0:1] + _, err := io.ReadFull(r, b) + if err != nil { + return err + } + *e = BloomUpdateType(b[0]) + return nil + + case *RejectCode: + b := scratch[0:1] + _, err := io.ReadFull(r, b) + if err != nil { + return err + } + *e = RejectCode(b[0]) + return nil + } + + // Fall back to the slower binary.Read if a fast path was not available + // above. + return binary.Read(r, binary.LittleEndian, element) +} + +// readElements reads multiple items from r. It is equivalent to multiple +// calls to readElement. +func readElements(r io.Reader, elements ...interface{}) error { + for _, element := range elements { + err := readElement(r, element) + if err != nil { + return err + } + } + return nil +} + +// writeElement writes the little endian representation of element to w. +func writeElement(w io.Writer, element interface{}) error { + var scratch [8]byte + + // Attempt to write the element based on the concrete type via fast + // type assertions first. + switch e := element.(type) { + case int32: + b := scratch[0:4] + binary.LittleEndian.PutUint32(b, uint32(e)) + _, err := w.Write(b) + if err != nil { + return err + } + return nil + + case uint32: + b := scratch[0:4] + binary.LittleEndian.PutUint32(b, e) + _, err := w.Write(b) + if err != nil { + return err + } + return nil + + case int64: + b := scratch[0:8] + binary.LittleEndian.PutUint64(b, uint64(e)) + _, err := w.Write(b) + if err != nil { + return err + } + return nil + + case uint64: + b := scratch[0:8] + binary.LittleEndian.PutUint64(b, e) + _, err := w.Write(b) + if err != nil { + return err + } + return nil + + case bool: + b := scratch[0:1] + if e == true { + b[0] = 0x01 + } else { + b[0] = 0x00 + } + _, err := w.Write(b) + if err != nil { + return err + } + return nil + + // Message header checksum. + case [4]byte: + _, err := w.Write(e[:]) + if err != nil { + return err + } + return nil + + // Message header command. + case [CommandSize]uint8: + _, err := w.Write(e[:]) + if err != nil { + return err + } + return nil + + // IP address. + case [16]byte: + _, err := w.Write(e[:]) + if err != nil { + return err + } + return nil + + case *ShaHash: + _, err := w.Write(e[:]) + if err != nil { + return err + } + return nil + + case ServiceFlag: + b := scratch[0:8] + binary.LittleEndian.PutUint64(b, uint64(e)) + _, err := w.Write(b) + if err != nil { + return err + } + return nil + + case InvType: + b := scratch[0:4] + binary.LittleEndian.PutUint32(b, uint32(e)) + _, err := w.Write(b) + if err != nil { + return err + } + return nil + + case BitcoinNet: + b := scratch[0:4] + binary.LittleEndian.PutUint32(b, uint32(e)) + _, err := w.Write(b) + if err != nil { + return err + } + return nil + + case BloomUpdateType: + b := scratch[0:1] + b[0] = uint8(e) + _, err := w.Write(b) + if err != nil { + return err + } + return nil + + case RejectCode: + b := scratch[0:1] + b[0] = uint8(e) + _, err := w.Write(b) + if err != nil { + return err + } + return nil + } + + // Fall back to the slower binary.Write if a fast path was not available + // above. + return binary.Write(w, binary.LittleEndian, element) +} + +// writeElements writes multiple items to w. It is equivalent to multiple +// calls to writeElement. +func writeElements(w io.Writer, elements ...interface{}) error { + for _, element := range elements { + err := writeElement(w, element) + if err != nil { + return err + } + } + return nil +} + +// ReadVarInt reads a variable length integer from r and returns it as a uint64. +func ReadVarInt(r io.Reader, pver uint32) (uint64, error) { + var b [8]byte + _, err := io.ReadFull(r, b[0:1]) + if err != nil { + return 0, err + } + + var rv uint64 + discriminant := uint8(b[0]) + switch discriminant { + case 0xff: + _, err := io.ReadFull(r, b[:]) + if err != nil { + return 0, err + } + rv = binary.LittleEndian.Uint64(b[:]) + + // The encoding is not canonical if the value could have been + // encoded using fewer bytes. + min := uint64(0x100000000) + if rv < min { + return 0, messageError("ReadVarInt", fmt.Sprintf( + errNonCanonicalVarInt, rv, discriminant, min)) + } + + case 0xfe: + _, err := io.ReadFull(r, b[0:4]) + if err != nil { + return 0, err + } + rv = uint64(binary.LittleEndian.Uint32(b[:])) + + // The encoding is not canonical if the value could have been + // encoded using fewer bytes. + min := uint64(0x10000) + if rv < min { + return 0, messageError("ReadVarInt", fmt.Sprintf( + errNonCanonicalVarInt, rv, discriminant, min)) + } + + case 0xfd: + _, err := io.ReadFull(r, b[0:2]) + if err != nil { + return 0, err + } + rv = uint64(binary.LittleEndian.Uint16(b[:])) + + // The encoding is not canonical if the value could have been + // encoded using fewer bytes. + min := uint64(0xfd) + if rv < min { + return 0, messageError("ReadVarInt", fmt.Sprintf( + errNonCanonicalVarInt, rv, discriminant, min)) + } + + default: + rv = uint64(discriminant) + } + + return rv, nil +} + +// WriteVarInt serializes val to w using a variable number of bytes depending +// on its value. +func WriteVarInt(w io.Writer, pver uint32, val uint64) error { + if val < 0xfd { + _, err := w.Write([]byte{uint8(val)}) + return err + } + + if val <= math.MaxUint16 { + var buf [3]byte + buf[0] = 0xfd + binary.LittleEndian.PutUint16(buf[1:], uint16(val)) + _, err := w.Write(buf[:]) + return err + } + + if val <= math.MaxUint32 { + var buf [5]byte + buf[0] = 0xfe + binary.LittleEndian.PutUint32(buf[1:], uint32(val)) + _, err := w.Write(buf[:]) + return err + } + + var buf [9]byte + buf[0] = 0xff + binary.LittleEndian.PutUint64(buf[1:], val) + _, err := w.Write(buf[:]) + return err +} + +// VarIntSerializeSize returns the number of bytes it would take to serialize +// val as a variable length integer. +func VarIntSerializeSize(val uint64) int { + // The value is small enough to be represented by itself, so it's + // just 1 byte. + if val < 0xfd { + return 1 + } + + // Discriminant 1 byte plus 2 bytes for the uint16. + if val <= math.MaxUint16 { + return 3 + } + + // Discriminant 1 byte plus 4 bytes for the uint32. + if val <= math.MaxUint32 { + return 5 + } + + // Discriminant 1 byte plus 8 bytes for the uint64. + return 9 +} + +// ReadVarString reads a variable length string from r and returns it as a Go +// string. A variable length string is encoded as a variable length integer +// containing the length of the string followed by the bytes that represent the +// string itself. An error is returned if the length is greater than the +// maximum block payload size since it helps protect against memory exhaustion +// attacks and forced panics through malformed messages. +func ReadVarString(r io.Reader, pver uint32) (string, error) { + count, err := ReadVarInt(r, pver) + if err != nil { + return "", err + } + + // Prevent variable length strings that are larger than the maximum + // message size. It would be possible to cause memory exhaustion and + // panics without a sane upper bound on this count. + if count > MaxMessagePayload { + str := fmt.Sprintf("variable length string is too long "+ + "[count %d, max %d]", count, MaxMessagePayload) + return "", messageError("ReadVarString", str) + } + + buf := make([]byte, count) + _, err = io.ReadFull(r, buf) + if err != nil { + return "", err + } + return string(buf), nil +} + +// WriteVarString serializes str to w as a variable length integer containing +// the length of the string followed by the bytes that represent the string +// itself. +func WriteVarString(w io.Writer, pver uint32, str string) error { + err := WriteVarInt(w, pver, uint64(len(str))) + if err != nil { + return err + } + _, err = w.Write([]byte(str)) + if err != nil { + return err + } + return nil +} + +// ReadVarBytes reads a variable length byte array. A byte array is encoded +// as a varInt containing the length of the array followed by the bytes +// themselves. An error is returned if the length is greater than the +// passed maxAllowed parameter which helps protect against memory exhuastion +// attacks and forced panics thorugh malformed messages. The fieldName +// parameter is only used for the error message so it provides more context in +// the error. +func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32, + fieldName string) ([]byte, error) { + + count, err := ReadVarInt(r, pver) + if err != nil { + return nil, err + } + + // Prevent byte array larger than the max message size. It would + // be possible to cause memory exhaustion and panics without a sane + // upper bound on this count. + if count > uint64(maxAllowed) { + str := fmt.Sprintf("%s is larger than the max allowed size "+ + "[count %d, max %d]", fieldName, count, maxAllowed) + return nil, messageError("ReadVarBytes", str) + } + + b := make([]byte, count) + _, err = io.ReadFull(r, b) + if err != nil { + return nil, err + } + return b, nil +} + +// WriteVarBytes serializes a variable length byte array to w as a varInt +// containing the number of bytes, followed by the bytes themselves. +func WriteVarBytes(w io.Writer, pver uint32, bytes []byte) error { + slen := uint64(len(bytes)) + err := WriteVarInt(w, pver, slen) + if err != nil { + return err + } + + _, err = w.Write(bytes) + if err != nil { + return err + } + return nil +} + +// randomUint64 returns a cryptographically random uint64 value. This +// unexported version takes a reader primarily to ensure the error paths +// can be properly tested by passing a fake reader in the tests. +func randomUint64(r io.Reader) (uint64, error) { + var b [8]byte + _, err := io.ReadFull(r, b[:]) + if err != nil { + return 0, err + } + return binary.BigEndian.Uint64(b[:]), nil +} + +// RandomUint64 returns a cryptographically random uint64 value. +func RandomUint64() (uint64, error) { + return randomUint64(rand.Reader) +} + +// DoubleSha256 calculates sha256(sha256(b)) and returns the resulting bytes. +func DoubleSha256(b []byte) []byte { + first := fastsha256.Sum256(b) + second := fastsha256.Sum256(first[:]) + return second[:] +} + +// DoubleSha256SH calculates sha256(sha256(b)) and returns the resulting bytes +// as a ShaHash. +func DoubleSha256SH(b []byte) ShaHash { + first := fastsha256.Sum256(b) + return ShaHash(fastsha256.Sum256(first[:])) +} diff --git a/vendor/github.com/btcsuite/btcd/wire/common_test.go b/vendor/github.com/btcsuite/btcd/wire/common_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f7e8fe9e886227d70b64a2ab9c6f5f1faf2b76ee --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/common_test.go @@ -0,0 +1,759 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "fmt" + "io" + "reflect" + "strings" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// mainNetGenesisHash is the hash of the first block in the block chain for the +// main network (genesis block). +var mainNetGenesisHash = wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, +}) + +// mainNetGenesisMerkleRoot is the hash of the first transaction in the genesis +// block for the main network. +var mainNetGenesisMerkleRoot = wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, + 0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61, + 0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32, + 0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, +}) + +// fakeRandReader implements the io.Reader interface and is used to force +// errors in the RandomUint64 function. +type fakeRandReader struct { + n int + err error +} + +// Read returns the fake reader error and the lesser of the fake reader value +// and the length of p. +func (r *fakeRandReader) Read(p []byte) (int, error) { + n := r.n + if n > len(p) { + n = len(p) + } + return n, r.err +} + +// TestElementWire tests wire encode and decode for various element types. This +// is mainly to test the "fast" paths in readElement and writeElement which use +// type assertions to avoid reflection when possible. +func TestElementWire(t *testing.T) { + type writeElementReflect int32 + + tests := []struct { + in interface{} // Value to encode + buf []byte // Wire encoding + }{ + {int32(1), []byte{0x01, 0x00, 0x00, 0x00}}, + {uint32(256), []byte{0x00, 0x01, 0x00, 0x00}}, + { + int64(65536), + []byte{0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}, + }, + { + uint64(4294967296), + []byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, + }, + { + true, + []byte{0x01}, + }, + { + false, + []byte{0x00}, + }, + { + [4]byte{0x01, 0x02, 0x03, 0x04}, + []byte{0x01, 0x02, 0x03, 0x04}, + }, + { + [wire.CommandSize]byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + }, + []byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + }, + }, + { + [16]byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + }, + []byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + }, + }, + { + (*wire.ShaHash)(&[wire.HashSize]byte{ // Make go vet happy. + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + }), + []byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + }, + }, + { + wire.ServiceFlag(wire.SFNodeNetwork), + []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + }, + { + wire.InvType(wire.InvTypeTx), + []byte{0x01, 0x00, 0x00, 0x00}, + }, + { + wire.BitcoinNet(wire.MainNet), + []byte{0xf9, 0xbe, 0xb4, 0xd9}, + }, + // Type not supported by the "fast" path and requires reflection. + { + writeElementReflect(1), + []byte{0x01, 0x00, 0x00, 0x00}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Write to wire format. + var buf bytes.Buffer + err := wire.TstWriteElement(&buf, test.in) + if err != nil { + t.Errorf("writeElement #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("writeElement #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Read from wire format. + rbuf := bytes.NewReader(test.buf) + val := test.in + if reflect.ValueOf(test.in).Kind() != reflect.Ptr { + val = reflect.New(reflect.TypeOf(test.in)).Interface() + } + err = wire.TstReadElement(rbuf, val) + if err != nil { + t.Errorf("readElement #%d error %v", i, err) + continue + } + ival := val + if reflect.ValueOf(test.in).Kind() != reflect.Ptr { + ival = reflect.Indirect(reflect.ValueOf(val)).Interface() + } + if !reflect.DeepEqual(ival, test.in) { + t.Errorf("readElement #%d\n got: %s want: %s", i, + spew.Sdump(ival), spew.Sdump(test.in)) + continue + } + } +} + +// TestElementWireErrors performs negative tests against wire encode and decode +// of various element types to confirm error paths work correctly. +func TestElementWireErrors(t *testing.T) { + tests := []struct { + in interface{} // Value to encode + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + {int32(1), 0, io.ErrShortWrite, io.EOF}, + {uint32(256), 0, io.ErrShortWrite, io.EOF}, + {int64(65536), 0, io.ErrShortWrite, io.EOF}, + {true, 0, io.ErrShortWrite, io.EOF}, + {[4]byte{0x01, 0x02, 0x03, 0x04}, 0, io.ErrShortWrite, io.EOF}, + { + [wire.CommandSize]byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + }, + 0, io.ErrShortWrite, io.EOF, + }, + { + [16]byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + }, + 0, io.ErrShortWrite, io.EOF, + }, + { + (*wire.ShaHash)(&[wire.HashSize]byte{ // Make go vet happy. + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + }), + 0, io.ErrShortWrite, io.EOF, + }, + {wire.ServiceFlag(wire.SFNodeNetwork), 0, io.ErrShortWrite, io.EOF}, + {wire.InvType(wire.InvTypeTx), 0, io.ErrShortWrite, io.EOF}, + {wire.BitcoinNet(wire.MainNet), 0, io.ErrShortWrite, io.EOF}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := wire.TstWriteElement(w, test.in) + if err != test.writeErr { + t.Errorf("writeElement #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // Decode from wire format. + r := newFixedReader(test.max, nil) + val := test.in + if reflect.ValueOf(test.in).Kind() != reflect.Ptr { + val = reflect.New(reflect.TypeOf(test.in)).Interface() + } + err = wire.TstReadElement(r, val) + if err != test.readErr { + t.Errorf("readElement #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + } +} + +// TestVarIntWire tests wire encode and decode for variable length integers. +func TestVarIntWire(t *testing.T) { + pver := wire.ProtocolVersion + + tests := []struct { + in uint64 // Value to encode + out uint64 // Expected decoded value + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + // Single byte + {0, 0, []byte{0x00}, pver}, + // Max single byte + {0xfc, 0xfc, []byte{0xfc}, pver}, + // Min 2-byte + {0xfd, 0xfd, []byte{0xfd, 0x0fd, 0x00}, pver}, + // Max 2-byte + {0xffff, 0xffff, []byte{0xfd, 0xff, 0xff}, pver}, + // Min 4-byte + {0x10000, 0x10000, []byte{0xfe, 0x00, 0x00, 0x01, 0x00}, pver}, + // Max 4-byte + {0xffffffff, 0xffffffff, []byte{0xfe, 0xff, 0xff, 0xff, 0xff}, pver}, + // Min 8-byte + { + 0x100000000, 0x100000000, + []byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, + pver, + }, + // Max 8-byte + { + 0xffffffffffffffff, 0xffffffffffffffff, + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + pver, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + var buf bytes.Buffer + err := wire.TstWriteVarInt(&buf, test.pver, test.in) + if err != nil { + t.Errorf("WriteVarInt #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("WriteVarInt #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode from wire format. + rbuf := bytes.NewReader(test.buf) + val, err := wire.TstReadVarInt(rbuf, test.pver) + if err != nil { + t.Errorf("ReadVarInt #%d error %v", i, err) + continue + } + if val != test.out { + t.Errorf("ReadVarInt #%d\n got: %d want: %d", i, + val, test.out) + continue + } + } +} + +// TestVarIntWireErrors performs negative tests against wire encode and decode +// of variable length integers to confirm error paths work correctly. +func TestVarIntWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + + tests := []struct { + in uint64 // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force errors on discriminant. + {0, []byte{0x00}, pver, 0, io.ErrShortWrite, io.EOF}, + // Force errors on 2-byte read/write. + {0xfd, []byte{0xfd}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, + // Force errors on 4-byte read/write. + {0x10000, []byte{0xfe}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, + // Force errors on 8-byte read/write. + {0x100000000, []byte{0xff}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := wire.TstWriteVarInt(w, test.pver, test.in) + if err != test.writeErr { + t.Errorf("WriteVarInt #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // Decode from wire format. + r := newFixedReader(test.max, test.buf) + _, err = wire.TstReadVarInt(r, test.pver) + if err != test.readErr { + t.Errorf("ReadVarInt #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + } +} + +// TestVarIntNonCanonical ensures variable length integers that are not encoded +// canonically return the expected error. +func TestVarIntNonCanonical(t *testing.T) { + pver := wire.ProtocolVersion + + tests := []struct { + name string // Test name for easier identification + in []byte // Value to decode + pver uint32 // Protocol version for wire encoding + }{ + { + "0 encoded with 3 bytes", []byte{0xfd, 0x00, 0x00}, + pver, + }, + { + "max single-byte value encoded with 3 bytes", + []byte{0xfd, 0xfc, 0x00}, pver, + }, + { + "0 encoded with 5 bytes", + []byte{0xfe, 0x00, 0x00, 0x00, 0x00}, pver, + }, + { + "max three-byte value encoded with 5 bytes", + []byte{0xfe, 0xff, 0xff, 0x00, 0x00}, pver, + }, + { + "0 encoded with 9 bytes", + []byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + pver, + }, + { + "max five-byte value encoded with 9 bytes", + []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}, + pver, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Decode from wire format. + rbuf := bytes.NewReader(test.in) + val, err := wire.TstReadVarInt(rbuf, test.pver) + if _, ok := err.(*wire.MessageError); !ok { + t.Errorf("ReadVarInt #%d (%s) unexpected error %v", i, + test.name, err) + continue + } + if val != 0 { + t.Errorf("ReadVarInt #%d (%s)\n got: %d want: 0", i, + test.name, val) + continue + } + } +} + +// TestVarIntWire tests the serialize size for variable length integers. +func TestVarIntSerializeSize(t *testing.T) { + tests := []struct { + val uint64 // Value to get the serialized size for + size int // Expected serialized size + }{ + // Single byte + {0, 1}, + // Max single byte + {0xfc, 1}, + // Min 2-byte + {0xfd, 3}, + // Max 2-byte + {0xffff, 3}, + // Min 4-byte + {0x10000, 5}, + // Max 4-byte + {0xffffffff, 5}, + // Min 8-byte + {0x100000000, 9}, + // Max 8-byte + {0xffffffffffffffff, 9}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + serializedSize := wire.VarIntSerializeSize(test.val) + if serializedSize != test.size { + t.Errorf("VarIntSerializeSize #%d got: %d, want: %d", i, + serializedSize, test.size) + continue + } + } +} + +// TestVarStringWire tests wire encode and decode for variable length strings. +func TestVarStringWire(t *testing.T) { + pver := wire.ProtocolVersion + + // str256 is a string that takes a 2-byte varint to encode. + str256 := strings.Repeat("test", 64) + + tests := []struct { + in string // String to encode + out string // String to decoded value + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + // Empty string + {"", "", []byte{0x00}, pver}, + // Single byte varint + string + {"Test", "Test", append([]byte{0x04}, []byte("Test")...), pver}, + // 2-byte varint + string + {str256, str256, append([]byte{0xfd, 0x00, 0x01}, []byte(str256)...), pver}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + var buf bytes.Buffer + err := wire.WriteVarString(&buf, test.pver, test.in) + if err != nil { + t.Errorf("WriteVarString #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("WriteVarString #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode from wire format. + rbuf := bytes.NewReader(test.buf) + val, err := wire.ReadVarString(rbuf, test.pver) + if err != nil { + t.Errorf("ReadVarString #%d error %v", i, err) + continue + } + if val != test.out { + t.Errorf("ReadVarString #%d\n got: %s want: %s", i, + val, test.out) + continue + } + } +} + +// TestVarStringWireErrors performs negative tests against wire encode and +// decode of variable length strings to confirm error paths work correctly. +func TestVarStringWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + + // str256 is a string that takes a 2-byte varint to encode. + str256 := strings.Repeat("test", 64) + + tests := []struct { + in string // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Latest protocol version with intentional read/write errors. + // Force errors on empty string. + {"", []byte{0x00}, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error on single byte varint + string. + {"Test", []byte{0x04}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, + // Force errors on 2-byte varint + string. + {str256, []byte{0xfd}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := wire.WriteVarString(w, test.pver, test.in) + if err != test.writeErr { + t.Errorf("WriteVarString #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // Decode from wire format. + r := newFixedReader(test.max, test.buf) + _, err = wire.ReadVarString(r, test.pver) + if err != test.readErr { + t.Errorf("ReadVarString #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + } +} + +// TestVarStringOverflowErrors performs tests to ensure deserializing variable +// length strings intentionally crafted to use large values for the string +// length are handled properly. This could otherwise potentially be used as an +// attack vector. +func TestVarStringOverflowErrors(t *testing.T) { + pver := wire.ProtocolVersion + + tests := []struct { + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + err error // Expected error + }{ + {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + pver, &wire.MessageError{}}, + {[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + pver, &wire.MessageError{}}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Decode from wire format. + rbuf := bytes.NewReader(test.buf) + _, err := wire.ReadVarString(rbuf, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("ReadVarString #%d wrong error got: %v, "+ + "want: %v", i, err, reflect.TypeOf(test.err)) + continue + } + } + +} + +// TestVarBytesWire tests wire encode and decode for variable length byte array. +func TestVarBytesWire(t *testing.T) { + pver := wire.ProtocolVersion + + // bytes256 is a byte array that takes a 2-byte varint to encode. + bytes256 := bytes.Repeat([]byte{0x01}, 256) + + tests := []struct { + in []byte // Byte Array to write + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + // Empty byte array + {[]byte{}, []byte{0x00}, pver}, + // Single byte varint + byte array + {[]byte{0x01}, []byte{0x01, 0x01}, pver}, + // 2-byte varint + byte array + {bytes256, append([]byte{0xfd, 0x00, 0x01}, bytes256...), pver}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + var buf bytes.Buffer + err := wire.TstWriteVarBytes(&buf, test.pver, test.in) + if err != nil { + t.Errorf("WriteVarBytes #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("WriteVarBytes #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode from wire format. + rbuf := bytes.NewReader(test.buf) + val, err := wire.TstReadVarBytes(rbuf, test.pver, + wire.MaxMessagePayload, "test payload") + if err != nil { + t.Errorf("ReadVarBytes #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("ReadVarBytes #%d\n got: %s want: %s", i, + val, test.buf) + continue + } + } +} + +// TestVarBytesWireErrors performs negative tests against wire encode and +// decode of variable length byte arrays to confirm error paths work correctly. +func TestVarBytesWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + + // bytes256 is a byte array that takes a 2-byte varint to encode. + bytes256 := bytes.Repeat([]byte{0x01}, 256) + + tests := []struct { + in []byte // Byte Array to write + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Latest protocol version with intentional read/write errors. + // Force errors on empty byte array. + {[]byte{}, []byte{0x00}, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error on single byte varint + byte array. + {[]byte{0x01, 0x02, 0x03}, []byte{0x04}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, + // Force errors on 2-byte varint + byte array. + {bytes256, []byte{0xfd}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := wire.TstWriteVarBytes(w, test.pver, test.in) + if err != test.writeErr { + t.Errorf("WriteVarBytes #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // Decode from wire format. + r := newFixedReader(test.max, test.buf) + _, err = wire.TstReadVarBytes(r, test.pver, + wire.MaxMessagePayload, "test payload") + if err != test.readErr { + t.Errorf("ReadVarBytes #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + } +} + +// TestVarBytesOverflowErrors performs tests to ensure deserializing variable +// length byte arrays intentionally crafted to use large values for the array +// length are handled properly. This could otherwise potentially be used as an +// attack vector. +func TestVarBytesOverflowErrors(t *testing.T) { + pver := wire.ProtocolVersion + + tests := []struct { + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + err error // Expected error + }{ + {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + pver, &wire.MessageError{}}, + {[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + pver, &wire.MessageError{}}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Decode from wire format. + rbuf := bytes.NewReader(test.buf) + _, err := wire.TstReadVarBytes(rbuf, test.pver, + wire.MaxMessagePayload, "test payload") + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("ReadVarBytes #%d wrong error got: %v, "+ + "want: %v", i, err, reflect.TypeOf(test.err)) + continue + } + } + +} + +// TestRandomUint64 exercises the randomness of the random number generator on +// the system by ensuring the probability of the generated numbers. If the RNG +// is evenly distributed as a proper cryptographic RNG should be, there really +// should only be 1 number < 2^56 in 2^8 tries for a 64-bit number. However, +// use a higher number of 5 to really ensure the test doesn't fail unless the +// RNG is just horrendous. +func TestRandomUint64(t *testing.T) { + tries := 1 << 8 // 2^8 + watermark := uint64(1 << 56) // 2^56 + maxHits := 5 + badRNG := "The random number generator on this system is clearly " + + "terrible since we got %d values less than %d in %d runs " + + "when only %d was expected" + + numHits := 0 + for i := 0; i < tries; i++ { + nonce, err := wire.RandomUint64() + if err != nil { + t.Errorf("RandomUint64 iteration %d failed - err %v", + i, err) + return + } + if nonce < watermark { + numHits++ + } + if numHits > maxHits { + str := fmt.Sprintf(badRNG, numHits, watermark, tries, maxHits) + t.Errorf("Random Uint64 iteration %d failed - %v %v", i, + str, numHits) + return + } + } +} + +// TestRandomUint64Errors uses a fake reader to force error paths to be executed +// and checks the results accordingly. +func TestRandomUint64Errors(t *testing.T) { + // Test short reads. + fr := &fakeRandReader{n: 2, err: io.EOF} + nonce, err := wire.TstRandomUint64(fr) + if err != io.ErrUnexpectedEOF { + t.Errorf("Error not expected value of %v [%v]", + io.ErrUnexpectedEOF, err) + } + if nonce != 0 { + t.Errorf("Nonce is not 0 [%v]", nonce) + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/doc.go b/vendor/github.com/btcsuite/btcd/wire/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..c10373ee1ed7c6320763443821b4fe2780af64d8 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/doc.go @@ -0,0 +1,161 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package wire implements the bitcoin wire protocol. + +For the complete details of the bitcoin protocol, see the official wiki entry +at https://en.bitcoin.it/wiki/Protocol_specification. The following only serves +as a quick overview to provide information on how to use the package. + +At a high level, this package provides support for marshalling and unmarshalling +supported bitcoin messages to and from the wire. This package does not deal +with the specifics of message handling such as what to do when a message is +received. This provides the caller with a high level of flexibility. + +Bitcoin Message Overview + +The bitcoin protocol consists of exchanging messages between peers. Each +message is preceded by a header which identifies information about it such as +which bitcoin network it is a part of, its type, how big it is, and a checksum +to verify validity. All encoding and decoding of message headers is handled by +this package. + +To accomplish this, there is a generic interface for bitcoin messages named +Message which allows messages of any type to be read, written, or passed around +through channels, functions, etc. In addition, concrete implementations of most +of the currently supported bitcoin messages are provided. For these supported +messages, all of the details of marshalling and unmarshalling to and from the +wire using bitcoin encoding are handled so the caller doesn't have to concern +themselves with the specifics. + +Message Interaction + +The following provides a quick summary of how the bitcoin messages are intended +to interact with one another. As stated above, these interactions are not +directly handled by this package. For more in-depth details about the +appropriate interactions, see the official bitcoin protocol wiki entry at +https://en.bitcoin.it/wiki/Protocol_specification. + +The initial handshake consists of two peers sending each other a version message +(MsgVersion) followed by responding with a verack message (MsgVerAck). Both +peers use the information in the version message (MsgVersion) to negotiate +things such as protocol version and supported services with each other. Once +the initial handshake is complete, the following chart indicates message +interactions in no particular order. + + Peer A Sends Peer B Responds + ---------------------------------------------------------------------------- + getaddr message (MsgGetAddr) addr message (MsgAddr) + getblocks message (MsgGetBlocks) inv message (MsgInv) + inv message (MsgInv) getdata message (MsgGetData) + getdata message (MsgGetData) block message (MsgBlock) -or- + tx message (MsgTx) -or- + notfound message (MsgNotFound) + getheaders message (MsgGetHeaders) headers message (MsgHeaders) + ping message (MsgPing) pong message (MsgHeaders)* -or- + (none -- Ability to send message is enough) + + NOTES: + * The pong message was not added until later protocol versions as defined + in BIP0031. The BIP0031Version constant can be used to detect a recent + enough protocol version for this purpose (version > BIP0031Version). + +Common Parameters + +There are several common parameters that arise when using this package to read +and write bitcoin messages. The following sections provide a quick overview of +these parameters so the next sections can build on them. + +Protocol Version + +The protocol version should be negotiated with the remote peer at a higher +level than this package via the version (MsgVersion) message exchange, however, +this package provides the wire.ProtocolVersion constant which indicates the +latest protocol version this package supports and is typically the value to use +for all outbound connections before a potentially lower protocol version is +negotiated. + +Bitcoin Network + +The bitcoin network is a magic number which is used to identify the start of a +message and which bitcoin network the message applies to. This package provides +the following constants: + + wire.MainNet + wire.TestNet (Regression test network) + wire.TestNet3 (Test network version 3) + wire.SimNet (Simulation test network) + +Determining Message Type + +As discussed in the bitcoin message overview section, this package reads +and writes bitcoin messages using a generic interface named Message. In +order to determine the actual concrete type of the message, use a type +switch or type assertion. An example of a type switch follows: + + // Assumes msg is already a valid concrete message such as one created + // via NewMsgVersion or read via ReadMessage. + switch msg := msg.(type) { + case *wire.MsgVersion: + // The message is a pointer to a MsgVersion struct. + fmt.Printf("Protocol version: %v", msg.ProtocolVersion) + case *wire.MsgBlock: + // The message is a pointer to a MsgBlock struct. + fmt.Printf("Number of tx in block: %v", msg.Header.TxnCount) + } + +Reading Messages + +In order to unmarshall bitcoin messages from the wire, use the ReadMessage +function. It accepts any io.Reader, but typically this will be a net.Conn to +a remote node running a bitcoin peer. Example syntax is: + + // Reads and validates the next bitcoin message from conn using the + // protocol version pver and the bitcoin network btcnet. The returns + // are a wire.Message, a []byte which contains the unmarshalled + // raw payload, and a possible error. + msg, rawPayload, err := wire.ReadMessage(conn, pver, btcnet) + if err != nil { + // Log and handle the error + } + +Writing Messages + +In order to marshall bitcoin messages to the wire, use the WriteMessage +function. It accepts any io.Writer, but typically this will be a net.Conn to +a remote node running a bitcoin peer. Example syntax to request addresses +from a remote peer is: + + // Create a new getaddr bitcoin message. + msg := wire.NewMsgGetAddr() + + // Writes a bitcoin message msg to conn using the protocol version + // pver, and the bitcoin network btcnet. The return is a possible + // error. + err := wire.WriteMessage(conn, msg, pver, btcnet) + if err != nil { + // Log and handle the error + } + +Errors + +Errors returned by this package are either the raw errors provided by underlying +calls to read/write from streams such as io.EOF, io.ErrUnexpectedEOF, and +io.ErrShortWrite, or of type wire.MessageError. This allows the caller to +differentiate between general IO errors and malformed messages through type +assertions. + +Bitcoin Improvement Proposals + +This package includes spec changes outlined by the following BIPs: + + BIP0014 (https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki) + BIP0031 (https://github.com/bitcoin/bips/blob/master/bip-0031.mediawiki) + BIP0035 (https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki) + BIP0037 (https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki) + BIP0111 (https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki) + BIP0130 (https://github.com/bitcoin/bips/blob/master/bip-0130.mediawiki) +*/ +package wire diff --git a/vendor/github.com/btcsuite/btcd/wire/error.go b/vendor/github.com/btcsuite/btcd/wire/error.go new file mode 100644 index 0000000000000000000000000000000000000000..755c2db49afa932d3a814d4267f28c2f95a0d0a8 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/error.go @@ -0,0 +1,34 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" +) + +// MessageError describes an issue with a message. +// An example of some potential issues are messages from the wrong bitcoin +// network, invalid commands, mismatched checksums, and exceeding max payloads. +// +// This provides a mechanism for the caller to type assert the error to +// differentiate between general io errors such as io.EOF and issues that +// resulted from malformed messages. +type MessageError struct { + Func string // Function name + Description string // Human readable description of the issue +} + +// Error satisfies the error interface and prints human-readable errors. +func (e *MessageError) Error() string { + if e.Func != "" { + return fmt.Sprintf("%v: %v", e.Func, e.Description) + } + return e.Description +} + +// messageError creates an error for the given function and description. +func messageError(f string, desc string) *MessageError { + return &MessageError{Func: f, Description: desc} +} diff --git a/vendor/github.com/btcsuite/btcd/wire/fakeconn_test.go b/vendor/github.com/btcsuite/btcd/wire/fakeconn_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b85367183de76ce85aca5c5e961666e788f7fc2e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/fakeconn_test.go @@ -0,0 +1,62 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "net" + "time" +) + +// fakeConn implements the net.Conn interface and is used to test functions +// which work with a net.Conn without having to actually make any real +// connections. +type fakeConn struct { + localAddr net.Addr + remoteAddr net.Addr +} + +// Read doesn't do anything. It just satisfies the net.Conn interface. +func (c *fakeConn) Read(b []byte) (n int, err error) { + return 0, nil +} + +// Write doesn't do anything. It just satisfies the net.Conn interface. +func (c *fakeConn) Write(b []byte) (n int, err error) { + return 0, nil +} + +// Close doesn't do anything. It just satisfies the net.Conn interface. +func (c *fakeConn) Close() error { + return nil +} + +// LocalAddr returns the localAddr field of the fake connection and satisfies +// the net.Conn interface. +func (c *fakeConn) LocalAddr() net.Addr { + return c.localAddr +} + +// RemoteAddr returns the remoteAddr field of the fake connection and satisfies +// the net.Conn interface. +func (c *fakeConn) RemoteAddr() net.Addr { + return c.remoteAddr +} + +// SetDeadline doesn't do anything. It just satisfies the net.Conn interface. +func (c *fakeConn) SetDeadline(t time.Time) error { + return nil +} + +// SetReadDeadline doesn't do anything. It just satisfies the net.Conn +// interface. +func (c *fakeConn) SetReadDeadline(t time.Time) error { + return nil +} + +// SetWriteDeadline doesn't do anything. It just satisfies the net.Conn +// interface. +func (c *fakeConn) SetWriteDeadline(t time.Time) error { + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/wire/fakemessage_test.go b/vendor/github.com/btcsuite/btcd/wire/fakemessage_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a3ed4d6dd25a3f44578366fdc3a358fa285c9255 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/fakemessage_test.go @@ -0,0 +1,60 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "io" + + "github.com/btcsuite/btcd/wire" +) + +// fakeMessage implements the wire.Message interface and is used to force +// encode errors in messages. +type fakeMessage struct { + command string + payload []byte + forceEncodeErr bool + forceLenErr bool +} + +// BtcDecode doesn't do anything. It just satisfies the wire.Message +// interface. +func (msg *fakeMessage) BtcDecode(r io.Reader, pver uint32) error { + return nil +} + +// BtcEncode writes the payload field of the fake message or forces an error +// if the forceEncodeErr flag of the fake message is set. It also satisfies the +// wire.Message interface. +func (msg *fakeMessage) BtcEncode(w io.Writer, pver uint32) error { + if msg.forceEncodeErr { + err := &wire.MessageError{ + Func: "fakeMessage.BtcEncode", + Description: "intentional error", + } + return err + } + + _, err := w.Write(msg.payload) + return err +} + +// Command returns the command field of the fake message and satisfies the +// wire.Message interface. +func (msg *fakeMessage) Command() string { + return msg.command +} + +// MaxPayloadLength returns the length of the payload field of fake message +// or a smaller value if the forceLenErr flag of the fake message is set. It +// satisfies the wire.Message interface. +func (msg *fakeMessage) MaxPayloadLength(pver uint32) uint32 { + lenp := uint32(len(msg.payload)) + if msg.forceLenErr { + return lenp - 1 + } + + return lenp +} diff --git a/vendor/github.com/btcsuite/btcd/wire/fixedIO_test.go b/vendor/github.com/btcsuite/btcd/wire/fixedIO_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f9377f0a9a689f0d9ed523afdbefb49e04adcd8f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/fixedIO_test.go @@ -0,0 +1,77 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" +) + +// fixedWriter implements the io.Writer interface and intentially allows +// testing of error paths by forcing short writes. +type fixedWriter struct { + b []byte + pos int +} + +// Write writes the contents of p to w. When the contents of p would cause +// the writer to exceed the maximum allowed size of the fixed writer, +// io.ErrShortWrite is returned and the writer is left unchanged. +// +// This satisfies the io.Writer interface. +func (w *fixedWriter) Write(p []byte) (n int, err error) { + lenp := len(p) + if w.pos+lenp > cap(w.b) { + return 0, io.ErrShortWrite + } + n = lenp + w.pos += copy(w.b[w.pos:], p) + return +} + +// Bytes returns the bytes already written to the fixed writer. +func (w *fixedWriter) Bytes() []byte { + return w.b +} + +// newFixedWriter returns a new io.Writer that will error once more bytes than +// the specified max have been written. +func newFixedWriter(max int) io.Writer { + b := make([]byte, max, max) + fw := fixedWriter{b, 0} + return &fw +} + +// fixedReader implements the io.Reader interface and intentially allows +// testing of error paths by forcing short reads. +type fixedReader struct { + buf []byte + pos int + iobuf *bytes.Buffer +} + +// Read reads the next len(p) bytes from the fixed reader. When the number of +// bytes read would exceed the maximum number of allowed bytes to be read from +// the fixed writer, an error is returned. +// +// This satisfies the io.Reader interface. +func (fr *fixedReader) Read(p []byte) (n int, err error) { + n, err = fr.iobuf.Read(p) + fr.pos += n + return +} + +// newFixedReader returns a new io.Reader that will error once more bytes than +// the specified max have been read. +func newFixedReader(max int, buf []byte) io.Reader { + b := make([]byte, max, max) + if buf != nil { + copy(b[:], buf) + } + + iobuf := bytes.NewBuffer(b) + fr := fixedReader{b, 0, iobuf} + return &fr +} diff --git a/vendor/github.com/btcsuite/btcd/wire/internal_test.go b/vendor/github.com/btcsuite/btcd/wire/internal_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d23e7a92b9eef6894cc00e03d87f324b26d7600d --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/internal_test.go @@ -0,0 +1,118 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +This test file is part of the wire package rather than than the wire_test +package so it can bridge access to the internals to properly test cases which +are either not possible or can't reliably be tested via the public interface. +The functions are only exported while the tests are being run. +*/ + +package wire + +import ( + "io" +) + +const ( + // MaxTxPerBlock makes the internal maxTxPerBlock constant available to + // the test package. + MaxTxPerBlock = maxTxPerBlock + + // MaxFlagsPerMerkleBlock makes the internal maxFlagsPerMerkleBlock + // constant available to the test package. + MaxFlagsPerMerkleBlock = maxFlagsPerMerkleBlock + + // MaxCountSetCancel makes the internal maxCountSetCancel constant + // available to the test package. + MaxCountSetCancel = maxCountSetCancel + + // MaxCountSetSubVer makes the internal maxCountSetSubVer constant + // available to the test package. + MaxCountSetSubVer = maxCountSetSubVer +) + +// TstRandomUint64 makes the internal randomUint64 function available to the +// test package. +func TstRandomUint64(r io.Reader) (uint64, error) { + return randomUint64(r) +} + +// TstReadElement makes the internal readElement function available to the +// test package. +func TstReadElement(r io.Reader, element interface{}) error { + return readElement(r, element) +} + +// TstWriteElement makes the internal writeElement function available to the +// test package. +func TstWriteElement(w io.Writer, element interface{}) error { + return writeElement(w, element) +} + +// TstReadVarInt makes the internal ReadVarInt function available to the +// test package. +func TstReadVarInt(r io.Reader, pver uint32) (uint64, error) { + return ReadVarInt(r, pver) +} + +// TstWriteVarInt makes the internal WriteVarInt function available to the +// test package. +func TstWriteVarInt(w io.Writer, pver uint32, val uint64) error { + return WriteVarInt(w, pver, val) +} + +// TstReadVarBytes makes the internal ReadVarBytes function available to the +// test package. +func TstReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32, fieldName string) ([]byte, error) { + return ReadVarBytes(r, pver, maxAllowed, fieldName) +} + +// TstWriteVarBytes makes the internal WriteVarBytes function available to the +// test package. +func TstWriteVarBytes(w io.Writer, pver uint32, bytes []byte) error { + return WriteVarBytes(w, pver, bytes) +} + +// TstReadNetAddress makes the internal readNetAddress function available to +// the test package. +func TstReadNetAddress(r io.Reader, pver uint32, na *NetAddress, ts bool) error { + return readNetAddress(r, pver, na, ts) +} + +// TstWriteNetAddress makes the internal writeNetAddress function available to +// the test package. +func TstWriteNetAddress(w io.Writer, pver uint32, na *NetAddress, ts bool) error { + return writeNetAddress(w, pver, na, ts) +} + +// TstMaxNetAddressPayload makes the internal maxNetAddressPayload function +// available to the test package. +func TstMaxNetAddressPayload(pver uint32) uint32 { + return maxNetAddressPayload(pver) +} + +// TstReadInvVect makes the internal readInvVect function available to the test +// package. +func TstReadInvVect(r io.Reader, pver uint32, iv *InvVect) error { + return readInvVect(r, pver, iv) +} + +// TstWriteInvVect makes the internal writeInvVect function available to the +// test package. +func TstWriteInvVect(w io.Writer, pver uint32, iv *InvVect) error { + return writeInvVect(w, pver, iv) +} + +// TstReadBlockHeader makes the internal readBlockHeader function available to +// the test package. +func TstReadBlockHeader(r io.Reader, pver uint32, bh *BlockHeader) error { + return readBlockHeader(r, pver, bh) +} + +// TstWriteBlockHeader makes the internal writeBlockHeader function available to +// the test package. +func TstWriteBlockHeader(w io.Writer, pver uint32, bh *BlockHeader) error { + return writeBlockHeader(w, pver, bh) +} diff --git a/vendor/github.com/btcsuite/btcd/wire/invvect.go b/vendor/github.com/btcsuite/btcd/wire/invvect.go new file mode 100644 index 0000000000000000000000000000000000000000..cf7def24ffeb0eea85a1127f43e64b2eeb58b1fe --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/invvect.go @@ -0,0 +1,82 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +const ( + // MaxInvPerMsg is the maximum number of inventory vectors that can be in a + // single bitcoin inv message. + MaxInvPerMsg = 50000 + + // Maximum payload size for an inventory vector. + maxInvVectPayload = 4 + HashSize +) + +// InvType represents the allowed types of inventory vectors. See InvVect. +type InvType uint32 + +// These constants define the various supported inventory vector types. +const ( + InvTypeError InvType = 0 + InvTypeTx InvType = 1 + InvTypeBlock InvType = 2 + InvTypeFilteredBlock InvType = 3 +) + +// Map of service flags back to their constant names for pretty printing. +var ivStrings = map[InvType]string{ + InvTypeError: "ERROR", + InvTypeTx: "MSG_TX", + InvTypeBlock: "MSG_BLOCK", + InvTypeFilteredBlock: "MSG_FILTERED_BLOCK", +} + +// String returns the InvType in human-readable form. +func (invtype InvType) String() string { + if s, ok := ivStrings[invtype]; ok { + return s + } + + return fmt.Sprintf("Unknown InvType (%d)", uint32(invtype)) +} + +// InvVect defines a bitcoin inventory vector which is used to describe data, +// as specified by the Type field, that a peer wants, has, or does not have to +// another peer. +type InvVect struct { + Type InvType // Type of data + Hash ShaHash // Hash of the data +} + +// NewInvVect returns a new InvVect using the provided type and hash. +func NewInvVect(typ InvType, hash *ShaHash) *InvVect { + return &InvVect{ + Type: typ, + Hash: *hash, + } +} + +// readInvVect reads an encoded InvVect from r depending on the protocol +// version. +func readInvVect(r io.Reader, pver uint32, iv *InvVect) error { + err := readElements(r, &iv.Type, &iv.Hash) + if err != nil { + return err + } + return nil +} + +// writeInvVect serializes an InvVect to w depending on the protocol version. +func writeInvVect(w io.Writer, pver uint32, iv *InvVect) error { + err := writeElements(w, iv.Type, &iv.Hash) + if err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/wire/invvect_test.go b/vendor/github.com/btcsuite/btcd/wire/invvect_test.go new file mode 100644 index 0000000000000000000000000000000000000000..da3fddd9148acefad27cc794b3eefad5f08e26a6 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/invvect_test.go @@ -0,0 +1,269 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestInvVectStringer tests the stringized output for inventory vector types. +func TestInvTypeStringer(t *testing.T) { + tests := []struct { + in wire.InvType + want string + }{ + {wire.InvTypeError, "ERROR"}, + {wire.InvTypeTx, "MSG_TX"}, + {wire.InvTypeBlock, "MSG_BLOCK"}, + {0xffffffff, "Unknown InvType (4294967295)"}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.in.String() + if result != test.want { + t.Errorf("String #%d\n got: %s want: %s", i, result, + test.want) + continue + } + } + +} + +// TestInvVect tests the InvVect API. +func TestInvVect(t *testing.T) { + ivType := wire.InvTypeBlock + hash := wire.ShaHash{} + + // Ensure we get the same payload and signature back out. + iv := wire.NewInvVect(ivType, &hash) + if iv.Type != ivType { + t.Errorf("NewInvVect: wrong type - got %v, want %v", + iv.Type, ivType) + } + if !iv.Hash.IsEqual(&hash) { + t.Errorf("NewInvVect: wrong hash - got %v, want %v", + spew.Sdump(iv.Hash), spew.Sdump(hash)) + } + +} + +// TestInvVectWire tests the InvVect wire encode and decode for various +// protocol versions and supported inventory vector types. +func TestInvVectWire(t *testing.T) { + // Block 203707 hash. + hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc" + baseHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // errInvVect is an inventory vector with an error. + errInvVect := wire.InvVect{ + Type: wire.InvTypeError, + Hash: wire.ShaHash{}, + } + + // errInvVectEncoded is the wire encoded bytes of errInvVect. + errInvVectEncoded := []byte{ + 0x00, 0x00, 0x00, 0x00, // InvTypeError + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // No hash + } + + // txInvVect is an inventory vector representing a transaction. + txInvVect := wire.InvVect{ + Type: wire.InvTypeTx, + Hash: *baseHash, + } + + // txInvVectEncoded is the wire encoded bytes of txInvVect. + txInvVectEncoded := []byte{ + 0x01, 0x00, 0x00, 0x00, // InvTypeTx + 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, + 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, + 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, + 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash + } + + // blockInvVect is an inventory vector representing a block. + blockInvVect := wire.InvVect{ + Type: wire.InvTypeBlock, + Hash: *baseHash, + } + + // blockInvVectEncoded is the wire encoded bytes of blockInvVect. + blockInvVectEncoded := []byte{ + 0x02, 0x00, 0x00, 0x00, // InvTypeBlock + 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, + 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, + 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, + 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash + } + + tests := []struct { + in wire.InvVect // NetAddress to encode + out wire.InvVect // Expected decoded NetAddress + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version error inventory vector. + { + errInvVect, + errInvVect, + errInvVectEncoded, + wire.ProtocolVersion, + }, + + // Latest protocol version tx inventory vector. + { + txInvVect, + txInvVect, + txInvVectEncoded, + wire.ProtocolVersion, + }, + + // Latest protocol version block inventory vector. + { + blockInvVect, + blockInvVect, + blockInvVectEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version error inventory vector. + { + errInvVect, + errInvVect, + errInvVectEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0035Version tx inventory vector. + { + txInvVect, + txInvVect, + txInvVectEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0035Version block inventory vector. + { + blockInvVect, + blockInvVect, + blockInvVectEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version error inventory vector. + { + errInvVect, + errInvVect, + errInvVectEncoded, + wire.BIP0031Version, + }, + + // Protocol version BIP0031Version tx inventory vector. + { + txInvVect, + txInvVect, + txInvVectEncoded, + wire.BIP0031Version, + }, + + // Protocol version BIP0031Version block inventory vector. + { + blockInvVect, + blockInvVect, + blockInvVectEncoded, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion error inventory vector. + { + errInvVect, + errInvVect, + errInvVectEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version NetAddressTimeVersion tx inventory vector. + { + txInvVect, + txInvVect, + txInvVectEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version NetAddressTimeVersion block inventory vector. + { + blockInvVect, + blockInvVect, + blockInvVectEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion error inventory vector. + { + errInvVect, + errInvVect, + errInvVectEncoded, + wire.MultipleAddressVersion, + }, + + // Protocol version MultipleAddressVersion tx inventory vector. + { + txInvVect, + txInvVect, + txInvVectEncoded, + wire.MultipleAddressVersion, + }, + + // Protocol version MultipleAddressVersion block inventory vector. + { + blockInvVect, + blockInvVect, + blockInvVectEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + var buf bytes.Buffer + err := wire.TstWriteInvVect(&buf, test.pver, &test.in) + if err != nil { + t.Errorf("writeInvVect #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("writeInvVect #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var iv wire.InvVect + rbuf := bytes.NewReader(test.buf) + err = wire.TstReadInvVect(rbuf, test.pver, &iv) + if err != nil { + t.Errorf("readInvVect #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(iv, test.out) { + t.Errorf("readInvVect #%d\n got: %s want: %s", i, + spew.Sdump(iv), spew.Sdump(test.out)) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/message.go b/vendor/github.com/btcsuite/btcd/wire/message.go new file mode 100644 index 0000000000000000000000000000000000000000..b5a114bf721585e801e886f5817857c24f38a5d3 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/message.go @@ -0,0 +1,369 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "bytes" + "fmt" + "io" + "unicode/utf8" +) + +// MessageHeaderSize is the number of bytes in a bitcoin message header. +// Bitcoin network (magic) 4 bytes + command 12 bytes + payload length 4 bytes + +// checksum 4 bytes. +const MessageHeaderSize = 24 + +// CommandSize is the fixed size of all commands in the common bitcoin message +// header. Shorter commands must be zero padded. +const CommandSize = 12 + +// MaxMessagePayload is the maximum bytes a message can be regardless of other +// individual limits imposed by messages themselves. +const MaxMessagePayload = (1024 * 1024 * 32) // 32MB + +// Commands used in bitcoin message headers which describe the type of message. +const ( + CmdVersion = "version" + CmdVerAck = "verack" + CmdGetAddr = "getaddr" + CmdAddr = "addr" + CmdGetBlocks = "getblocks" + CmdInv = "inv" + CmdGetData = "getdata" + CmdNotFound = "notfound" + CmdBlock = "block" + CmdTx = "tx" + CmdGetHeaders = "getheaders" + CmdHeaders = "headers" + CmdPing = "ping" + CmdPong = "pong" + CmdAlert = "alert" + CmdMemPool = "mempool" + CmdFilterAdd = "filteradd" + CmdFilterClear = "filterclear" + CmdFilterLoad = "filterload" + CmdMerkleBlock = "merkleblock" + CmdReject = "reject" + CmdSendHeaders = "sendheaders" +) + +// Message is an interface that describes a bitcoin message. A type that +// implements Message has complete control over the representation of its data +// and may therefore contain additional or fewer fields than those which +// are used directly in the protocol encoded message. +type Message interface { + BtcDecode(io.Reader, uint32) error + BtcEncode(io.Writer, uint32) error + Command() string + MaxPayloadLength(uint32) uint32 +} + +// makeEmptyMessage creates a message of the appropriate concrete type based +// on the command. +func makeEmptyMessage(command string) (Message, error) { + var msg Message + switch command { + case CmdVersion: + msg = &MsgVersion{} + + case CmdVerAck: + msg = &MsgVerAck{} + + case CmdGetAddr: + msg = &MsgGetAddr{} + + case CmdAddr: + msg = &MsgAddr{} + + case CmdGetBlocks: + msg = &MsgGetBlocks{} + + case CmdBlock: + msg = &MsgBlock{} + + case CmdInv: + msg = &MsgInv{} + + case CmdGetData: + msg = &MsgGetData{} + + case CmdNotFound: + msg = &MsgNotFound{} + + case CmdTx: + msg = &MsgTx{} + + case CmdPing: + msg = &MsgPing{} + + case CmdPong: + msg = &MsgPong{} + + case CmdGetHeaders: + msg = &MsgGetHeaders{} + + case CmdHeaders: + msg = &MsgHeaders{} + + case CmdAlert: + msg = &MsgAlert{} + + case CmdMemPool: + msg = &MsgMemPool{} + + case CmdFilterAdd: + msg = &MsgFilterAdd{} + + case CmdFilterClear: + msg = &MsgFilterClear{} + + case CmdFilterLoad: + msg = &MsgFilterLoad{} + + case CmdMerkleBlock: + msg = &MsgMerkleBlock{} + + case CmdReject: + msg = &MsgReject{} + + case CmdSendHeaders: + msg = &MsgSendHeaders{} + + default: + return nil, fmt.Errorf("unhandled command [%s]", command) + } + return msg, nil +} + +// messageHeader defines the header structure for all bitcoin protocol messages. +type messageHeader struct { + magic BitcoinNet // 4 bytes + command string // 12 bytes + length uint32 // 4 bytes + checksum [4]byte // 4 bytes +} + +// readMessageHeader reads a bitcoin message header from r. +func readMessageHeader(r io.Reader) (int, *messageHeader, error) { + // Since readElements doesn't return the amount of bytes read, attempt + // to read the entire header into a buffer first in case there is a + // short read so the proper amount of read bytes are known. This works + // since the header is a fixed size. + var headerBytes [MessageHeaderSize]byte + n, err := io.ReadFull(r, headerBytes[:]) + if err != nil { + return n, nil, err + } + hr := bytes.NewReader(headerBytes[:]) + + // Create and populate a messageHeader struct from the raw header bytes. + hdr := messageHeader{} + var command [CommandSize]byte + readElements(hr, &hdr.magic, &command, &hdr.length, &hdr.checksum) + + // Strip trailing zeros from command string. + hdr.command = string(bytes.TrimRight(command[:], string(0))) + + return n, &hdr, nil +} + +// discardInput reads n bytes from reader r in chunks and discards the read +// bytes. This is used to skip payloads when various errors occur and helps +// prevent rogue nodes from causing massive memory allocation through forging +// header length. +func discardInput(r io.Reader, n uint32) { + maxSize := uint32(10 * 1024) // 10k at a time + numReads := n / maxSize + bytesRemaining := n % maxSize + if n > 0 { + buf := make([]byte, maxSize) + for i := uint32(0); i < numReads; i++ { + io.ReadFull(r, buf) + } + } + if bytesRemaining > 0 { + buf := make([]byte, bytesRemaining) + io.ReadFull(r, buf) + } +} + +// WriteMessageN writes a bitcoin Message to w including the necessary header +// information and returns the number of bytes written. This function is the +// same as WriteMessage except it also returns the number of bytes written. +func WriteMessageN(w io.Writer, msg Message, pver uint32, btcnet BitcoinNet) (int, error) { + totalBytes := 0 + + // Enforce max command size. + var command [CommandSize]byte + cmd := msg.Command() + if len(cmd) > CommandSize { + str := fmt.Sprintf("command [%s] is too long [max %v]", + cmd, CommandSize) + return totalBytes, messageError("WriteMessage", str) + } + copy(command[:], []byte(cmd)) + + // Encode the message payload. + var bw bytes.Buffer + err := msg.BtcEncode(&bw, pver) + if err != nil { + return totalBytes, err + } + payload := bw.Bytes() + lenp := len(payload) + + // Enforce maximum overall message payload. + if lenp > MaxMessagePayload { + str := fmt.Sprintf("message payload is too large - encoded "+ + "%d bytes, but maximum message payload is %d bytes", + lenp, MaxMessagePayload) + return totalBytes, messageError("WriteMessage", str) + } + + // Enforce maximum message payload based on the message type. + mpl := msg.MaxPayloadLength(pver) + if uint32(lenp) > mpl { + str := fmt.Sprintf("message payload is too large - encoded "+ + "%d bytes, but maximum message payload size for "+ + "messages of type [%s] is %d.", lenp, cmd, mpl) + return totalBytes, messageError("WriteMessage", str) + } + + // Create header for the message. + hdr := messageHeader{} + hdr.magic = btcnet + hdr.command = cmd + hdr.length = uint32(lenp) + copy(hdr.checksum[:], DoubleSha256(payload)[0:4]) + + // Encode the header for the message. This is done to a buffer + // rather than directly to the writer since writeElements doesn't + // return the number of bytes written. + hw := bytes.NewBuffer(make([]byte, 0, MessageHeaderSize)) + writeElements(hw, hdr.magic, command, hdr.length, hdr.checksum) + + // Write header. + n, err := w.Write(hw.Bytes()) + totalBytes += n + if err != nil { + return totalBytes, err + } + + // Write payload. + n, err = w.Write(payload) + totalBytes += n + if err != nil { + return totalBytes, err + } + + return totalBytes, nil +} + +// WriteMessage writes a bitcoin Message to w including the necessary header +// information. This function is the same as WriteMessageN except it doesn't +// doesn't return the number of bytes written. This function is mainly provided +// for backwards compatibility with the original API, but it's also useful for +// callers that don't care about byte counts. +func WriteMessage(w io.Writer, msg Message, pver uint32, btcnet BitcoinNet) error { + _, err := WriteMessageN(w, msg, pver, btcnet) + return err +} + +// ReadMessageN reads, validates, and parses the next bitcoin Message from r for +// the provided protocol version and bitcoin network. It returns the number of +// bytes read in addition to the parsed Message and raw bytes which comprise the +// message. This function is the same as ReadMessage except it also returns the +// number of bytes read. +func ReadMessageN(r io.Reader, pver uint32, btcnet BitcoinNet) (int, Message, []byte, error) { + totalBytes := 0 + n, hdr, err := readMessageHeader(r) + totalBytes += n + if err != nil { + return totalBytes, nil, nil, err + } + + // Enforce maximum message payload. + if hdr.length > MaxMessagePayload { + str := fmt.Sprintf("message payload is too large - header "+ + "indicates %d bytes, but max message payload is %d "+ + "bytes.", hdr.length, MaxMessagePayload) + return totalBytes, nil, nil, messageError("ReadMessage", str) + + } + + // Check for messages from the wrong bitcoin network. + if hdr.magic != btcnet { + discardInput(r, hdr.length) + str := fmt.Sprintf("message from other network [%v]", hdr.magic) + return totalBytes, nil, nil, messageError("ReadMessage", str) + } + + // Check for malformed commands. + command := hdr.command + if !utf8.ValidString(command) { + discardInput(r, hdr.length) + str := fmt.Sprintf("invalid command %v", []byte(command)) + return totalBytes, nil, nil, messageError("ReadMessage", str) + } + + // Create struct of appropriate message type based on the command. + msg, err := makeEmptyMessage(command) + if err != nil { + discardInput(r, hdr.length) + return totalBytes, nil, nil, messageError("ReadMessage", + err.Error()) + } + + // Check for maximum length based on the message type as a malicious client + // could otherwise create a well-formed header and set the length to max + // numbers in order to exhaust the machine's memory. + mpl := msg.MaxPayloadLength(pver) + if hdr.length > mpl { + discardInput(r, hdr.length) + str := fmt.Sprintf("payload exceeds max length - header "+ + "indicates %v bytes, but max payload size for "+ + "messages of type [%v] is %v.", hdr.length, command, mpl) + return totalBytes, nil, nil, messageError("ReadMessage", str) + } + + // Read payload. + payload := make([]byte, hdr.length) + n, err = io.ReadFull(r, payload) + totalBytes += n + if err != nil { + return totalBytes, nil, nil, err + } + + // Test checksum. + checksum := DoubleSha256(payload)[0:4] + if !bytes.Equal(checksum[:], hdr.checksum[:]) { + str := fmt.Sprintf("payload checksum failed - header "+ + "indicates %v, but actual checksum is %v.", + hdr.checksum, checksum) + return totalBytes, nil, nil, messageError("ReadMessage", str) + } + + // Unmarshal message. NOTE: This must be a *bytes.Buffer since the + // MsgVersion BtcDecode function requires it. + pr := bytes.NewBuffer(payload) + err = msg.BtcDecode(pr, pver) + if err != nil { + return totalBytes, nil, nil, err + } + + return totalBytes, msg, payload, nil +} + +// ReadMessage reads, validates, and parses the next bitcoin Message from r for +// the provided protocol version and bitcoin network. It returns the parsed +// Message and raw bytes which comprise the message. This function only differs +// from ReadMessageN in that it doesn't return the number of bytes read. This +// function is mainly provided for backwards compatibility with the original +// API, but it's also useful for callers that don't care about byte counts. +func ReadMessage(r io.Reader, pver uint32, btcnet BitcoinNet) (Message, []byte, error) { + _, msg, buf, err := ReadMessageN(r, pver, btcnet) + return msg, buf, err +} diff --git a/vendor/github.com/btcsuite/btcd/wire/message_test.go b/vendor/github.com/btcsuite/btcd/wire/message_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e913dd106bf60f178b30faa30ddcf975a78165aa --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/message_test.go @@ -0,0 +1,452 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "encoding/binary" + "io" + "net" + "reflect" + "testing" + "time" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// makeHeader is a convenience function to make a message header in the form of +// a byte slice. It is used to force errors when reading messages. +func makeHeader(btcnet wire.BitcoinNet, command string, + payloadLen uint32, checksum uint32) []byte { + + // The length of a bitcoin message header is 24 bytes. + // 4 byte magic number of the bitcoin network + 12 byte command + 4 byte + // payload length + 4 byte checksum. + buf := make([]byte, 24) + binary.LittleEndian.PutUint32(buf, uint32(btcnet)) + copy(buf[4:], []byte(command)) + binary.LittleEndian.PutUint32(buf[16:], payloadLen) + binary.LittleEndian.PutUint32(buf[20:], checksum) + return buf +} + +// TestMessage tests the Read/WriteMessage and Read/WriteMessageN API. +func TestMessage(t *testing.T) { + pver := wire.ProtocolVersion + + // Create the various types of messages to test. + + // MsgVersion. + addrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333} + you, err := wire.NewNetAddress(addrYou, wire.SFNodeNetwork) + if err != nil { + t.Errorf("NewNetAddress: %v", err) + } + you.Timestamp = time.Time{} // Version message has zero value timestamp. + addrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333} + me, err := wire.NewNetAddress(addrMe, wire.SFNodeNetwork) + if err != nil { + t.Errorf("NewNetAddress: %v", err) + } + me.Timestamp = time.Time{} // Version message has zero value timestamp. + msgVersion := wire.NewMsgVersion(me, you, 123123, 0) + + msgVerack := wire.NewMsgVerAck() + msgGetAddr := wire.NewMsgGetAddr() + msgAddr := wire.NewMsgAddr() + msgGetBlocks := wire.NewMsgGetBlocks(&wire.ShaHash{}) + msgBlock := &blockOne + msgInv := wire.NewMsgInv() + msgGetData := wire.NewMsgGetData() + msgNotFound := wire.NewMsgNotFound() + msgTx := wire.NewMsgTx() + msgPing := wire.NewMsgPing(123123) + msgPong := wire.NewMsgPong(123123) + msgGetHeaders := wire.NewMsgGetHeaders() + msgHeaders := wire.NewMsgHeaders() + msgAlert := wire.NewMsgAlert([]byte("payload"), []byte("signature")) + msgMemPool := wire.NewMsgMemPool() + msgFilterAdd := wire.NewMsgFilterAdd([]byte{0x01}) + msgFilterClear := wire.NewMsgFilterClear() + msgFilterLoad := wire.NewMsgFilterLoad([]byte{0x01}, 10, 0, wire.BloomUpdateNone) + bh := wire.NewBlockHeader(&wire.ShaHash{}, &wire.ShaHash{}, 0, 0) + msgMerkleBlock := wire.NewMsgMerkleBlock(bh) + msgReject := wire.NewMsgReject("block", wire.RejectDuplicate, "duplicate block") + + tests := []struct { + in wire.Message // Value to encode + out wire.Message // Expected decoded value + pver uint32 // Protocol version for wire encoding + btcnet wire.BitcoinNet // Network to use for wire encoding + bytes int // Expected num bytes read/written + }{ + {msgVersion, msgVersion, pver, wire.MainNet, 125}, + {msgVerack, msgVerack, pver, wire.MainNet, 24}, + {msgGetAddr, msgGetAddr, pver, wire.MainNet, 24}, + {msgAddr, msgAddr, pver, wire.MainNet, 25}, + {msgGetBlocks, msgGetBlocks, pver, wire.MainNet, 61}, + {msgBlock, msgBlock, pver, wire.MainNet, 239}, + {msgInv, msgInv, pver, wire.MainNet, 25}, + {msgGetData, msgGetData, pver, wire.MainNet, 25}, + {msgNotFound, msgNotFound, pver, wire.MainNet, 25}, + {msgTx, msgTx, pver, wire.MainNet, 34}, + {msgPing, msgPing, pver, wire.MainNet, 32}, + {msgPong, msgPong, pver, wire.MainNet, 32}, + {msgGetHeaders, msgGetHeaders, pver, wire.MainNet, 61}, + {msgHeaders, msgHeaders, pver, wire.MainNet, 25}, + {msgAlert, msgAlert, pver, wire.MainNet, 42}, + {msgMemPool, msgMemPool, pver, wire.MainNet, 24}, + {msgFilterAdd, msgFilterAdd, pver, wire.MainNet, 26}, + {msgFilterClear, msgFilterClear, pver, wire.MainNet, 24}, + {msgFilterLoad, msgFilterLoad, pver, wire.MainNet, 35}, + {msgMerkleBlock, msgMerkleBlock, pver, wire.MainNet, 110}, + {msgReject, msgReject, pver, wire.MainNet, 79}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + var buf bytes.Buffer + nw, err := wire.WriteMessageN(&buf, test.in, test.pver, test.btcnet) + if err != nil { + t.Errorf("WriteMessage #%d error %v", i, err) + continue + } + + // Ensure the number of bytes written match the expected value. + if nw != test.bytes { + t.Errorf("WriteMessage #%d unexpected num bytes "+ + "written - got %d, want %d", i, nw, test.bytes) + } + + // Decode from wire format. + rbuf := bytes.NewReader(buf.Bytes()) + nr, msg, _, err := wire.ReadMessageN(rbuf, test.pver, test.btcnet) + if err != nil { + t.Errorf("ReadMessage #%d error %v, msg %v", i, err, + spew.Sdump(msg)) + continue + } + if !reflect.DeepEqual(msg, test.out) { + t.Errorf("ReadMessage #%d\n got: %v want: %v", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + + // Ensure the number of bytes read match the expected value. + if nr != test.bytes { + t.Errorf("ReadMessage #%d unexpected num bytes read - "+ + "got %d, want %d", i, nr, test.bytes) + } + } + + // Do the same thing for Read/WriteMessage, but ignore the bytes since + // they don't return them. + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + var buf bytes.Buffer + err := wire.WriteMessage(&buf, test.in, test.pver, test.btcnet) + if err != nil { + t.Errorf("WriteMessage #%d error %v", i, err) + continue + } + + // Decode from wire format. + rbuf := bytes.NewReader(buf.Bytes()) + msg, _, err := wire.ReadMessage(rbuf, test.pver, test.btcnet) + if err != nil { + t.Errorf("ReadMessage #%d error %v, msg %v", i, err, + spew.Sdump(msg)) + continue + } + if !reflect.DeepEqual(msg, test.out) { + t.Errorf("ReadMessage #%d\n got: %v want: %v", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestReadMessageWireErrors performs negative tests against wire decoding into +// concrete messages to confirm error paths work correctly. +func TestReadMessageWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + btcnet := wire.MainNet + + // Ensure message errors are as expected with no function specified. + wantErr := "something bad happened" + testErr := wire.MessageError{Description: wantErr} + if testErr.Error() != wantErr { + t.Errorf("MessageError: wrong error - got %v, want %v", + testErr.Error(), wantErr) + } + + // Ensure message errors are as expected with a function specified. + wantFunc := "foo" + testErr = wire.MessageError{Func: wantFunc, Description: wantErr} + if testErr.Error() != wantFunc+": "+wantErr { + t.Errorf("MessageError: wrong error - got %v, want %v", + testErr.Error(), wantErr) + } + + // Wire encoded bytes for main and testnet3 networks magic identifiers. + testNet3Bytes := makeHeader(wire.TestNet3, "", 0, 0) + + // Wire encoded bytes for a message that exceeds max overall message + // length. + mpl := uint32(wire.MaxMessagePayload) + exceedMaxPayloadBytes := makeHeader(btcnet, "getaddr", mpl+1, 0) + + // Wire encoded bytes for a command which is invalid utf-8. + badCommandBytes := makeHeader(btcnet, "bogus", 0, 0) + badCommandBytes[4] = 0x81 + + // Wire encoded bytes for a command which is valid, but not supported. + unsupportedCommandBytes := makeHeader(btcnet, "bogus", 0, 0) + + // Wire encoded bytes for a message which exceeds the max payload for + // a specific message type. + exceedTypePayloadBytes := makeHeader(btcnet, "getaddr", 1, 0) + + // Wire encoded bytes for a message which does not deliver the full + // payload according to the header length. + shortPayloadBytes := makeHeader(btcnet, "version", 115, 0) + + // Wire encoded bytes for a message with a bad checksum. + badChecksumBytes := makeHeader(btcnet, "version", 2, 0xbeef) + badChecksumBytes = append(badChecksumBytes, []byte{0x0, 0x0}...) + + // Wire encoded bytes for a message which has a valid header, but is + // the wrong format. An addr starts with a varint of the number of + // contained in the message. Claim there is two, but don't provide + // them. At the same time, forge the header fields so the message is + // otherwise accurate. + badMessageBytes := makeHeader(btcnet, "addr", 1, 0xeaadc31c) + badMessageBytes = append(badMessageBytes, 0x2) + + // Wire encoded bytes for a message which the header claims has 15k + // bytes of data to discard. + discardBytes := makeHeader(btcnet, "bogus", 15*1024, 0) + + tests := []struct { + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + btcnet wire.BitcoinNet // Bitcoin network for wire encoding + max int // Max size of fixed buffer to induce errors + readErr error // Expected read error + bytes int // Expected num bytes read + }{ + // Latest protocol version with intentional read errors. + + // Short header. + { + []byte{}, + pver, + btcnet, + 0, + io.EOF, + 0, + }, + + // Wrong network. Want MainNet, but giving TestNet3. + { + testNet3Bytes, + pver, + btcnet, + len(testNet3Bytes), + &wire.MessageError{}, + 24, + }, + + // Exceed max overall message payload length. + { + exceedMaxPayloadBytes, + pver, + btcnet, + len(exceedMaxPayloadBytes), + &wire.MessageError{}, + 24, + }, + + // Invalid UTF-8 command. + { + badCommandBytes, + pver, + btcnet, + len(badCommandBytes), + &wire.MessageError{}, + 24, + }, + + // Valid, but unsupported command. + { + unsupportedCommandBytes, + pver, + btcnet, + len(unsupportedCommandBytes), + &wire.MessageError{}, + 24, + }, + + // Exceed max allowed payload for a message of a specific type. + { + exceedTypePayloadBytes, + pver, + btcnet, + len(exceedTypePayloadBytes), + &wire.MessageError{}, + 24, + }, + + // Message with a payload shorter than the header indicates. + { + shortPayloadBytes, + pver, + btcnet, + len(shortPayloadBytes), + io.EOF, + 24, + }, + + // Message with a bad checksum. + { + badChecksumBytes, + pver, + btcnet, + len(badChecksumBytes), + &wire.MessageError{}, + 26, + }, + + // Message with a valid header, but wrong format. + { + badMessageBytes, + pver, + btcnet, + len(badMessageBytes), + io.EOF, + 25, + }, + + // 15k bytes of data to discard. + { + discardBytes, + pver, + btcnet, + len(discardBytes), + &wire.MessageError{}, + 24, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Decode from wire format. + r := newFixedReader(test.max, test.buf) + nr, _, _, err := wire.ReadMessageN(r, test.pver, test.btcnet) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+ + "want: %T", i, err, err, test.readErr) + continue + } + + // Ensure the number of bytes written match the expected value. + if nr != test.bytes { + t.Errorf("ReadMessage #%d unexpected num bytes read - "+ + "got %d, want %d", i, nr, test.bytes) + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+ + "want: %v <%T>", i, err, err, + test.readErr, test.readErr) + continue + } + } + } +} + +// TestWriteMessageWireErrors performs negative tests against wire encoding from +// concrete messages to confirm error paths work correctly. +func TestWriteMessageWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + btcnet := wire.MainNet + wireErr := &wire.MessageError{} + + // Fake message with a command that is too long. + badCommandMsg := &fakeMessage{command: "somethingtoolong"} + + // Fake message with a problem during encoding + encodeErrMsg := &fakeMessage{forceEncodeErr: true} + + // Fake message that has payload which exceeds max overall message size. + exceedOverallPayload := make([]byte, wire.MaxMessagePayload+1) + exceedOverallPayloadErrMsg := &fakeMessage{payload: exceedOverallPayload} + + // Fake message that has payload which exceeds max allowed per message. + exceedPayload := make([]byte, 1) + exceedPayloadErrMsg := &fakeMessage{payload: exceedPayload, forceLenErr: true} + + // Fake message that is used to force errors in the header and payload + // writes. + bogusPayload := []byte{0x01, 0x02, 0x03, 0x04} + bogusMsg := &fakeMessage{command: "bogus", payload: bogusPayload} + + tests := []struct { + msg wire.Message // Message to encode + pver uint32 // Protocol version for wire encoding + btcnet wire.BitcoinNet // Bitcoin network for wire encoding + max int // Max size of fixed buffer to induce errors + err error // Expected error + bytes int // Expected num bytes written + }{ + // Command too long. + {badCommandMsg, pver, btcnet, 0, wireErr, 0}, + // Force error in payload encode. + {encodeErrMsg, pver, btcnet, 0, wireErr, 0}, + // Force error due to exceeding max overall message payload size. + {exceedOverallPayloadErrMsg, pver, btcnet, 0, wireErr, 0}, + // Force error due to exceeding max payload for message type. + {exceedPayloadErrMsg, pver, btcnet, 0, wireErr, 0}, + // Force error in header write. + {bogusMsg, pver, btcnet, 0, io.ErrShortWrite, 0}, + // Force error in payload write. + {bogusMsg, pver, btcnet, 24, io.ErrShortWrite, 24}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode wire format. + w := newFixedWriter(test.max) + nw, err := wire.WriteMessageN(w, test.msg, test.pver, test.btcnet) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("WriteMessage #%d wrong error got: %v <%T>, "+ + "want: %T", i, err, err, test.err) + continue + } + + // Ensure the number of bytes written match the expected value. + if nw != test.bytes { + t.Errorf("WriteMessage #%d unexpected num bytes "+ + "written - got %d, want %d", i, nw, test.bytes) + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.err { + t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+ + "want: %v <%T>", i, err, err, + test.err, test.err) + continue + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgaddr.go b/vendor/github.com/btcsuite/btcd/wire/msgaddr.go new file mode 100644 index 0000000000000000000000000000000000000000..2c40e4d45b0cfdbfde3b9f1a63150beaa675c3f7 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgaddr.go @@ -0,0 +1,142 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// MaxAddrPerMsg is the maximum number of addresses that can be in a single +// bitcoin addr message (MsgAddr). +const MaxAddrPerMsg = 1000 + +// MsgAddr implements the Message interface and represents a bitcoin +// addr message. It is used to provide a list of known active peers on the +// network. An active peer is considered one that has transmitted a message +// within the last 3 hours. Nodes which have not transmitted in that time +// frame should be forgotten. Each message is limited to a maximum number of +// addresses, which is currently 1000. As a result, multiple messages must +// be used to relay the full list. +// +// Use the AddAddress function to build up the list of known addresses when +// sending an addr message to another peer. +type MsgAddr struct { + AddrList []*NetAddress +} + +// AddAddress adds a known active peer to the message. +func (msg *MsgAddr) AddAddress(na *NetAddress) error { + if len(msg.AddrList)+1 > MaxAddrPerMsg { + str := fmt.Sprintf("too many addresses in message [max %v]", + MaxAddrPerMsg) + return messageError("MsgAddr.AddAddress", str) + } + + msg.AddrList = append(msg.AddrList, na) + return nil +} + +// AddAddresses adds multiple known active peers to the message. +func (msg *MsgAddr) AddAddresses(netAddrs ...*NetAddress) error { + for _, na := range netAddrs { + err := msg.AddAddress(na) + if err != nil { + return err + } + } + return nil +} + +// ClearAddresses removes all addresses from the message. +func (msg *MsgAddr) ClearAddresses() { + msg.AddrList = []*NetAddress{} +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgAddr) BtcDecode(r io.Reader, pver uint32) error { + count, err := ReadVarInt(r, pver) + if err != nil { + return err + } + + // Limit to max addresses per message. + if count > MaxAddrPerMsg { + str := fmt.Sprintf("too many addresses for message "+ + "[count %v, max %v]", count, MaxAddrPerMsg) + return messageError("MsgAddr.BtcDecode", str) + } + + msg.AddrList = make([]*NetAddress, 0, count) + for i := uint64(0); i < count; i++ { + na := NetAddress{} + err := readNetAddress(r, pver, &na, true) + if err != nil { + return err + } + msg.AddAddress(&na) + } + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgAddr) BtcEncode(w io.Writer, pver uint32) error { + // Protocol versions before MultipleAddressVersion only allowed 1 address + // per message. + count := len(msg.AddrList) + if pver < MultipleAddressVersion && count > 1 { + str := fmt.Sprintf("too many addresses for message of "+ + "protocol version %v [count %v, max 1]", pver, count) + return messageError("MsgAddr.BtcEncode", str) + + } + if count > MaxAddrPerMsg { + str := fmt.Sprintf("too many addresses for message "+ + "[count %v, max %v]", count, MaxAddrPerMsg) + return messageError("MsgAddr.BtcEncode", str) + } + + err := WriteVarInt(w, pver, uint64(count)) + if err != nil { + return err + } + + for _, na := range msg.AddrList { + err = writeNetAddress(w, pver, na, true) + if err != nil { + return err + } + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgAddr) Command() string { + return CmdAddr +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgAddr) MaxPayloadLength(pver uint32) uint32 { + if pver < MultipleAddressVersion { + // Num addresses (varInt) + a single net addresses. + return MaxVarIntPayload + maxNetAddressPayload(pver) + } + + // Num addresses (varInt) + max allowed addresses. + return MaxVarIntPayload + (MaxAddrPerMsg * maxNetAddressPayload(pver)) +} + +// NewMsgAddr returns a new bitcoin addr message that conforms to the +// Message interface. See MsgAddr for details. +func NewMsgAddr() *MsgAddr { + return &MsgAddr{ + AddrList: make([]*NetAddress, 0, MaxAddrPerMsg), + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgaddr_test.go b/vendor/github.com/btcsuite/btcd/wire/msgaddr_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e92b2f1c90b7bb230026d25fa7987681e0aa4903 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgaddr_test.go @@ -0,0 +1,321 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "net" + "reflect" + "testing" + "time" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestAddr tests the MsgAddr API. +func TestAddr(t *testing.T) { + pver := wire.ProtocolVersion + + // Ensure the command is expected value. + wantCmd := "addr" + msg := wire.NewMsgAddr() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgAddr: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + // Num addresses (varInt) + max allowed addresses. + wantPayload := uint32(30009) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Ensure NetAddresses are added properly. + tcpAddr := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333} + na, err := wire.NewNetAddress(tcpAddr, wire.SFNodeNetwork) + if err != nil { + t.Errorf("NewNetAddress: %v", err) + } + err = msg.AddAddress(na) + if err != nil { + t.Errorf("AddAddress: %v", err) + } + if msg.AddrList[0] != na { + t.Errorf("AddAddress: wrong address added - got %v, want %v", + spew.Sprint(msg.AddrList[0]), spew.Sprint(na)) + } + + // Ensure the address list is cleared properly. + msg.ClearAddresses() + if len(msg.AddrList) != 0 { + t.Errorf("ClearAddresses: address list is not empty - "+ + "got %v [%v], want %v", len(msg.AddrList), + spew.Sprint(msg.AddrList[0]), 0) + } + + // Ensure adding more than the max allowed addresses per message returns + // error. + for i := 0; i < wire.MaxAddrPerMsg+1; i++ { + err = msg.AddAddress(na) + } + if err == nil { + t.Errorf("AddAddress: expected error on too many addresses " + + "not received") + } + err = msg.AddAddresses(na) + if err == nil { + t.Errorf("AddAddresses: expected error on too many addresses " + + "not received") + } + + // Ensure max payload is expected value for protocol versions before + // timestamp was added to NetAddress. + // Num addresses (varInt) + max allowed addresses. + pver = wire.NetAddressTimeVersion - 1 + wantPayload = uint32(26009) + maxPayload = msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Ensure max payload is expected value for protocol versions before + // multiple addresses were allowed. + // Num addresses (varInt) + a single net addresses. + pver = wire.MultipleAddressVersion - 1 + wantPayload = uint32(35) + maxPayload = msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + return +} + +// TestAddrWire tests the MsgAddr wire encode and decode for various numbers +// of addreses and protocol versions. +func TestAddrWire(t *testing.T) { + // A couple of NetAddresses to use for testing. + na := &wire.NetAddress{ + Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST + Services: wire.SFNodeNetwork, + IP: net.ParseIP("127.0.0.1"), + Port: 8333, + } + na2 := &wire.NetAddress{ + Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST + Services: wire.SFNodeNetwork, + IP: net.ParseIP("192.168.0.1"), + Port: 8334, + } + + // Empty address message. + noAddr := wire.NewMsgAddr() + noAddrEncoded := []byte{ + 0x00, // Varint for number of addresses + } + + // Address message with multiple addresses. + multiAddr := wire.NewMsgAddr() + multiAddr.AddAddresses(na, na2) + multiAddrEncoded := []byte{ + 0x02, // Varint for number of addresses + 0x29, 0xab, 0x5f, 0x49, // Timestamp + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1 + 0x20, 0x8d, // Port 8333 in big-endian + 0x29, 0xab, 0x5f, 0x49, // Timestamp + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x01, // IP 192.168.0.1 + 0x20, 0x8e, // Port 8334 in big-endian + + } + + tests := []struct { + in *wire.MsgAddr // Message to encode + out *wire.MsgAddr // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version with no addresses. + { + noAddr, + noAddr, + noAddrEncoded, + wire.ProtocolVersion, + }, + + // Latest protocol version with multiple addresses. + { + multiAddr, + multiAddr, + multiAddrEncoded, + wire.ProtocolVersion, + }, + + // Protocol version MultipleAddressVersion-1 with no addresses. + { + noAddr, + noAddr, + noAddrEncoded, + wire.MultipleAddressVersion - 1, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgAddr + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestAddrWireErrors performs negative tests against wire encode and decode +// of MsgAddr to confirm error paths work correctly. +func TestAddrWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + pverMA := wire.MultipleAddressVersion + wireErr := &wire.MessageError{} + + // A couple of NetAddresses to use for testing. + na := &wire.NetAddress{ + Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST + Services: wire.SFNodeNetwork, + IP: net.ParseIP("127.0.0.1"), + Port: 8333, + } + na2 := &wire.NetAddress{ + Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST + Services: wire.SFNodeNetwork, + IP: net.ParseIP("192.168.0.1"), + Port: 8334, + } + + // Address message with multiple addresses. + baseAddr := wire.NewMsgAddr() + baseAddr.AddAddresses(na, na2) + baseAddrEncoded := []byte{ + 0x02, // Varint for number of addresses + 0x29, 0xab, 0x5f, 0x49, // Timestamp + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1 + 0x20, 0x8d, // Port 8333 in big-endian + 0x29, 0xab, 0x5f, 0x49, // Timestamp + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x01, // IP 192.168.0.1 + 0x20, 0x8e, // Port 8334 in big-endian + + } + + // Message that forces an error by having more than the max allowed + // addresses. + maxAddr := wire.NewMsgAddr() + for i := 0; i < wire.MaxAddrPerMsg; i++ { + maxAddr.AddAddress(na) + } + maxAddr.AddrList = append(maxAddr.AddrList, na) + maxAddrEncoded := []byte{ + 0xfd, 0x03, 0xe9, // Varint for number of addresses (1001) + } + + tests := []struct { + in *wire.MsgAddr // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Latest protocol version with intentional read/write errors. + // Force error in addresses count + {baseAddr, baseAddrEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in address list. + {baseAddr, baseAddrEncoded, pver, 1, io.ErrShortWrite, io.EOF}, + // Force error with greater than max inventory vectors. + {maxAddr, maxAddrEncoded, pver, 3, wireErr, wireErr}, + // Force error with greater than max inventory vectors for + // protocol versions before multiple addresses were allowed. + {maxAddr, maxAddrEncoded, pverMA - 1, 3, wireErr, wireErr}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgAddr + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgalert.go b/vendor/github.com/btcsuite/btcd/wire/msgalert.go new file mode 100644 index 0000000000000000000000000000000000000000..23be105f6f68bf15af228cea3724047d8c6e22a0 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgalert.go @@ -0,0 +1,422 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "bytes" + "fmt" + "io" +) + +// MsgAlert contains a payload and a signature: +// +// =============================================== +// | Field | Data Type | Size | +// =============================================== +// | payload | []uchar | ? | +// ----------------------------------------------- +// | signature | []uchar | ? | +// ----------------------------------------------- +// +// Here payload is an Alert serialized into a byte array to ensure that +// versions using incompatible alert formats can still relay +// alerts among one another. +// +// An Alert is the payload deserialized as follows: +// +// =============================================== +// | Field | Data Type | Size | +// =============================================== +// | Version | int32 | 4 | +// ----------------------------------------------- +// | RelayUntil | int64 | 8 | +// ----------------------------------------------- +// | Expiration | int64 | 8 | +// ----------------------------------------------- +// | ID | int32 | 4 | +// ----------------------------------------------- +// | Cancel | int32 | 4 | +// ----------------------------------------------- +// | SetCancel | set<int32> | ? | +// ----------------------------------------------- +// | MinVer | int32 | 4 | +// ----------------------------------------------- +// | MaxVer | int32 | 4 | +// ----------------------------------------------- +// | SetSubVer | set<string> | ? | +// ----------------------------------------------- +// | Priority | int32 | 4 | +// ----------------------------------------------- +// | Comment | string | ? | +// ----------------------------------------------- +// | StatusBar | string | ? | +// ----------------------------------------------- +// | Reserved | string | ? | +// ----------------------------------------------- +// | Total (Fixed) | 45 | +// ----------------------------------------------- +// +// NOTE: +// * string is a VarString i.e VarInt length followed by the string itself +// * set<string> is a VarInt followed by as many number of strings +// * set<int32> is a VarInt followed by as many number of ints +// * fixedAlertSize = 40 + 5*min(VarInt) = 40 + 5*1 = 45 +// +// Now we can define bounds on Alert size, SetCancel and SetSubVer + +// Fixed size of the alert payload +const fixedAlertSize = 45 + +// maxSignatureSize is the max size of an ECDSA signature. +// NOTE: Since this size is fixed and < 255, the size of VarInt required = 1. +const maxSignatureSize = 72 + +// maxAlertSize is the maximum size an alert. +// +// MessagePayload = VarInt(Alert) + Alert + VarInt(Signature) + Signature +// MaxMessagePayload = maxAlertSize + max(VarInt) + maxSignatureSize + 1 +const maxAlertSize = MaxMessagePayload - maxSignatureSize - MaxVarIntPayload - 1 + +// maxCountSetCancel is the maximum number of cancel IDs that could possibly +// fit into a maximum size alert. +// +// maxAlertSize = fixedAlertSize + max(SetCancel) + max(SetSubVer) + 3*(string) +// for caculating maximum number of cancel IDs, set all other var sizes to 0 +// maxAlertSize = fixedAlertSize + (MaxVarIntPayload-1) + x*sizeOf(int32) +// x = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 4 +const maxCountSetCancel = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 4 + +// maxCountSetSubVer is the maximum number of subversions that could possibly +// fit into a maximum size alert. +// +// maxAlertSize = fixedAlertSize + max(SetCancel) + max(SetSubVer) + 3*(string) +// for caculating maximum number of subversions, set all other var sizes to 0 +// maxAlertSize = fixedAlertSize + (MaxVarIntPayload-1) + x*sizeOf(string) +// x = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / sizeOf(string) +// subversion would typically be something like "/Satoshi:0.7.2/" (15 bytes) +// so assuming < 255 bytes, sizeOf(string) = sizeOf(uint8) + 255 = 256 +const maxCountSetSubVer = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 256 + +// Alert contains the data deserialized from the MsgAlert payload. +type Alert struct { + // Alert format version + Version int32 + + // Timestamp beyond which nodes should stop relaying this alert + RelayUntil int64 + + // Timestamp beyond which this alert is no longer in effect and + // should be ignored + Expiration int64 + + // A unique ID number for this alert + ID int32 + + // All alerts with an ID less than or equal to this number should + // cancelled, deleted and not accepted in the future + Cancel int32 + + // All alert IDs contained in this set should be cancelled as above + SetCancel []int32 + + // This alert only applies to versions greater than or equal to this + // version. Other versions should still relay it. + MinVer int32 + + // This alert only applies to versions less than or equal to this version. + // Other versions should still relay it. + MaxVer int32 + + // If this set contains any elements, then only nodes that have their + // subVer contained in this set are affected by the alert. Other versions + // should still relay it. + SetSubVer []string + + // Relative priority compared to other alerts + Priority int32 + + // A comment on the alert that is not displayed + Comment string + + // The alert message that is displayed to the user + StatusBar string + + // Reserved + Reserved string +} + +// Serialize encodes the alert to w using the alert protocol encoding format. +func (alert *Alert) Serialize(w io.Writer, pver uint32) error { + err := writeElements(w, alert.Version, alert.RelayUntil, + alert.Expiration, alert.ID, alert.Cancel) + if err != nil { + return err + } + + count := len(alert.SetCancel) + if count > maxCountSetCancel { + str := fmt.Sprintf("too many cancel alert IDs for alert "+ + "[count %v, max %v]", count, maxCountSetCancel) + return messageError("Alert.Serialize", str) + } + err = WriteVarInt(w, pver, uint64(count)) + if err != nil { + return err + } + for i := 0; i < int(count); i++ { + err = writeElement(w, alert.SetCancel[i]) + if err != nil { + return err + } + } + + err = writeElements(w, alert.MinVer, alert.MaxVer) + if err != nil { + return err + } + + count = len(alert.SetSubVer) + if count > maxCountSetSubVer { + str := fmt.Sprintf("too many sub versions for alert "+ + "[count %v, max %v]", count, maxCountSetSubVer) + return messageError("Alert.Serialize", str) + } + err = WriteVarInt(w, pver, uint64(count)) + if err != nil { + return err + } + for i := 0; i < int(count); i++ { + err = WriteVarString(w, pver, alert.SetSubVer[i]) + if err != nil { + return err + } + } + + err = writeElement(w, alert.Priority) + if err != nil { + return err + } + err = WriteVarString(w, pver, alert.Comment) + if err != nil { + return err + } + err = WriteVarString(w, pver, alert.StatusBar) + if err != nil { + return err + } + err = WriteVarString(w, pver, alert.Reserved) + if err != nil { + return err + } + return nil +} + +// Deserialize decodes from r into the receiver using the alert protocol +// encoding format. +func (alert *Alert) Deserialize(r io.Reader, pver uint32) error { + err := readElements(r, &alert.Version, &alert.RelayUntil, + &alert.Expiration, &alert.ID, &alert.Cancel) + if err != nil { + return err + } + + // SetCancel: first read a VarInt that contains + // count - the number of Cancel IDs, then + // iterate count times and read them + count, err := ReadVarInt(r, pver) + if err != nil { + return err + } + if count > maxCountSetCancel { + str := fmt.Sprintf("too many cancel alert IDs for alert "+ + "[count %v, max %v]", count, maxCountSetCancel) + return messageError("Alert.Deserialize", str) + } + alert.SetCancel = make([]int32, count) + for i := 0; i < int(count); i++ { + err := readElement(r, &alert.SetCancel[i]) + if err != nil { + return err + } + } + + err = readElements(r, &alert.MinVer, &alert.MaxVer) + if err != nil { + return err + } + + // SetSubVer: similar to SetCancel + // but read count number of sub-version strings + count, err = ReadVarInt(r, pver) + if err != nil { + return err + } + if count > maxCountSetSubVer { + str := fmt.Sprintf("too many sub versions for alert "+ + "[count %v, max %v]", count, maxCountSetSubVer) + return messageError("Alert.Deserialize", str) + } + alert.SetSubVer = make([]string, count) + for i := 0; i < int(count); i++ { + alert.SetSubVer[i], err = ReadVarString(r, pver) + if err != nil { + return err + } + } + + err = readElement(r, &alert.Priority) + if err != nil { + return err + } + alert.Comment, err = ReadVarString(r, pver) + if err != nil { + return err + } + alert.StatusBar, err = ReadVarString(r, pver) + if err != nil { + return err + } + alert.Reserved, err = ReadVarString(r, pver) + if err != nil { + return err + } + return nil +} + +// NewAlert returns an new Alert with values provided. +func NewAlert(version int32, relayUntil int64, expiration int64, + id int32, cancel int32, setCancel []int32, minVer int32, + maxVer int32, setSubVer []string, priority int32, comment string, + statusBar string) *Alert { + return &Alert{ + Version: version, + RelayUntil: relayUntil, + Expiration: expiration, + ID: id, + Cancel: cancel, + SetCancel: setCancel, + MinVer: minVer, + MaxVer: maxVer, + SetSubVer: setSubVer, + Priority: priority, + Comment: comment, + StatusBar: statusBar, + Reserved: "", + } +} + +// NewAlertFromPayload returns an Alert with values deserialized from the +// serialized payload. +func NewAlertFromPayload(serializedPayload []byte, pver uint32) (*Alert, error) { + var alert Alert + r := bytes.NewReader(serializedPayload) + err := alert.Deserialize(r, pver) + if err != nil { + return nil, err + } + return &alert, nil +} + +// MsgAlert implements the Message interface and defines a bitcoin alert +// message. +// +// This is a signed message that provides notifications that the client should +// display if the signature matches the key. bitcoind/bitcoin-qt only checks +// against a signature from the core developers. +type MsgAlert struct { + // SerializedPayload is the alert payload serialized as a string so that the + // version can change but the Alert can still be passed on by older + // clients. + SerializedPayload []byte + + // Signature is the ECDSA signature of the message. + Signature []byte + + // Deserialized Payload + Payload *Alert +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgAlert) BtcDecode(r io.Reader, pver uint32) error { + var err error + + msg.SerializedPayload, err = ReadVarBytes(r, pver, MaxMessagePayload, + "alert serialized payload") + if err != nil { + return err + } + + msg.Payload, err = NewAlertFromPayload(msg.SerializedPayload, pver) + if err != nil { + msg.Payload = nil + } + + msg.Signature, err = ReadVarBytes(r, pver, MaxMessagePayload, + "alert signature") + if err != nil { + return err + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgAlert) BtcEncode(w io.Writer, pver uint32) error { + var err error + var serializedpayload []byte + if msg.Payload != nil { + // try to Serialize Payload if possible + r := new(bytes.Buffer) + err = msg.Payload.Serialize(r, pver) + if err != nil { + // Serialize failed - ignore & fallback + // to SerializedPayload + serializedpayload = msg.SerializedPayload + } else { + serializedpayload = r.Bytes() + } + } else { + serializedpayload = msg.SerializedPayload + } + slen := uint64(len(serializedpayload)) + if slen == 0 { + return messageError("MsgAlert.BtcEncode", "empty serialized payload") + } + err = WriteVarBytes(w, pver, serializedpayload) + if err != nil { + return err + } + err = WriteVarBytes(w, pver, msg.Signature) + if err != nil { + return err + } + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgAlert) Command() string { + return CmdAlert +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgAlert) MaxPayloadLength(pver uint32) uint32 { + // Since this can vary depending on the message, make it the max + // size allowed. + return MaxMessagePayload +} + +// NewMsgAlert returns a new bitcoin alert message that conforms to the Message +// interface. See MsgAlert for details. +func NewMsgAlert(serializedPayload []byte, signature []byte) *MsgAlert { + return &MsgAlert{ + SerializedPayload: serializedPayload, + Signature: signature, + Payload: nil, + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgalert_test.go b/vendor/github.com/btcsuite/btcd/wire/msgalert_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e850d926a9a9785f9555c606ea7846bb6bfad8e9 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgalert_test.go @@ -0,0 +1,467 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestMsgAlert tests the MsgAlert API. +func TestMsgAlert(t *testing.T) { + pver := wire.ProtocolVersion + serializedpayload := []byte("some message") + signature := []byte("some sig") + + // Ensure we get the same payload and signature back out. + msg := wire.NewMsgAlert(serializedpayload, signature) + if !reflect.DeepEqual(msg.SerializedPayload, serializedpayload) { + t.Errorf("NewMsgAlert: wrong serializedpayload - got %v, want %v", + msg.SerializedPayload, serializedpayload) + } + if !reflect.DeepEqual(msg.Signature, signature) { + t.Errorf("NewMsgAlert: wrong signature - got %v, want %v", + msg.Signature, signature) + } + + // Ensure the command is expected value. + wantCmd := "alert" + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgAlert: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value. + wantPayload := uint32(1024 * 1024 * 32) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Test BtcEncode with Payload == nil + var buf bytes.Buffer + err := msg.BtcEncode(&buf, pver) + if err != nil { + t.Error(err.Error()) + } + // expected = 0x0c + serializedpayload + 0x08 + signature + expectedBuf := append([]byte{0x0c}, serializedpayload...) + expectedBuf = append(expectedBuf, []byte{0x08}...) + expectedBuf = append(expectedBuf, signature...) + if !bytes.Equal(buf.Bytes(), expectedBuf) { + t.Errorf("BtcEncode got: %s want: %s", + spew.Sdump(buf.Bytes()), spew.Sdump(expectedBuf)) + } + + // Test BtcEncode with Payload != nil + // note: Payload is an empty Alert but not nil + msg.Payload = new(wire.Alert) + buf = *new(bytes.Buffer) + err = msg.BtcEncode(&buf, pver) + if err != nil { + t.Error(err.Error()) + } + // empty Alert is 45 null bytes, see Alert comments + // for details + // expected = 0x2d + 45*0x00 + 0x08 + signature + expectedBuf = append([]byte{0x2d}, bytes.Repeat([]byte{0x00}, 45)...) + expectedBuf = append(expectedBuf, []byte{0x08}...) + expectedBuf = append(expectedBuf, signature...) + if !bytes.Equal(buf.Bytes(), expectedBuf) { + t.Errorf("BtcEncode got: %s want: %s", + spew.Sdump(buf.Bytes()), spew.Sdump(expectedBuf)) + } +} + +// TestMsgAlertWire tests the MsgAlert wire encode and decode for various protocol +// versions. +func TestMsgAlertWire(t *testing.T) { + baseMsgAlert := wire.NewMsgAlert([]byte("some payload"), []byte("somesig")) + baseMsgAlertEncoded := []byte{ + 0x0c, // Varint for payload length + 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, // "some payload" + 0x07, // Varint for signature length + 0x73, 0x6f, 0x6d, 0x65, 0x73, 0x69, 0x67, // "somesig" + } + + tests := []struct { + in *wire.MsgAlert // Message to encode + out *wire.MsgAlert // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + baseMsgAlert, + baseMsgAlert, + baseMsgAlertEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version. + { + baseMsgAlert, + baseMsgAlert, + baseMsgAlertEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version. + { + baseMsgAlert, + baseMsgAlert, + baseMsgAlertEncoded, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion. + { + baseMsgAlert, + baseMsgAlert, + baseMsgAlertEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion. + { + baseMsgAlert, + baseMsgAlert, + baseMsgAlertEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgAlert + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestMsgAlertWireErrors performs negative tests against wire encode and decode +// of MsgAlert to confirm error paths work correctly. +func TestMsgAlertWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + + baseMsgAlert := wire.NewMsgAlert([]byte("some payload"), []byte("somesig")) + baseMsgAlertEncoded := []byte{ + 0x0c, // Varint for payload length + 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, // "some payload" + 0x07, // Varint for signature length + 0x73, 0x6f, 0x6d, 0x65, 0x73, 0x69, 0x67, // "somesig" + } + + tests := []struct { + in *wire.MsgAlert // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error in payload length. + {baseMsgAlert, baseMsgAlertEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in payload. + {baseMsgAlert, baseMsgAlertEncoded, pver, 1, io.ErrShortWrite, io.EOF}, + // Force error in signature length. + {baseMsgAlert, baseMsgAlertEncoded, pver, 13, io.ErrShortWrite, io.EOF}, + // Force error in signature. + {baseMsgAlert, baseMsgAlertEncoded, pver, 14, io.ErrShortWrite, io.EOF}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgAlert + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + } + + // Test Error on empty Payload + baseMsgAlert.SerializedPayload = []byte{} + w := new(bytes.Buffer) + err := baseMsgAlert.BtcEncode(w, pver) + if _, ok := err.(*wire.MessageError); !ok { + t.Errorf("MsgAlert.BtcEncode wrong error got: %T, want: %T", + err, wire.MessageError{}) + } + + // Test Payload Serialize error + // overflow the max number of elements in SetCancel + baseMsgAlert.Payload = new(wire.Alert) + baseMsgAlert.Payload.SetCancel = make([]int32, wire.MaxCountSetCancel+1) + buf := *new(bytes.Buffer) + err = baseMsgAlert.BtcEncode(&buf, pver) + if _, ok := err.(*wire.MessageError); !ok { + t.Errorf("MsgAlert.BtcEncode wrong error got: %T, want: %T", + err, wire.MessageError{}) + } + + // overflow the max number of elements in SetSubVer + baseMsgAlert.Payload = new(wire.Alert) + baseMsgAlert.Payload.SetSubVer = make([]string, wire.MaxCountSetSubVer+1) + buf = *new(bytes.Buffer) + err = baseMsgAlert.BtcEncode(&buf, pver) + if _, ok := err.(*wire.MessageError); !ok { + t.Errorf("MsgAlert.BtcEncode wrong error got: %T, want: %T", + err, wire.MessageError{}) + } +} + +// TestAlert tests serialization and deserialization +// of the payload to Alert +func TestAlert(t *testing.T) { + pver := wire.ProtocolVersion + alert := wire.NewAlert( + 1, 1337093712, 1368628812, 1015, + 1013, []int32{1014}, 0, 40599, []string{"/Satoshi:0.7.2/"}, 5000, "", + "URGENT: upgrade required, see http://bitcoin.org/dos for details", + ) + w := new(bytes.Buffer) + err := alert.Serialize(w, pver) + if err != nil { + t.Error(err.Error()) + } + serializedpayload := w.Bytes() + newAlert, err := wire.NewAlertFromPayload(serializedpayload, pver) + if err != nil { + t.Error(err.Error()) + } + + if alert.Version != newAlert.Version { + t.Errorf("NewAlertFromPayload: wrong Version - got %v, want %v ", + alert.Version, newAlert.Version) + } + if alert.RelayUntil != newAlert.RelayUntil { + t.Errorf("NewAlertFromPayload: wrong RelayUntil - got %v, want %v ", + alert.RelayUntil, newAlert.RelayUntil) + } + if alert.Expiration != newAlert.Expiration { + t.Errorf("NewAlertFromPayload: wrong Expiration - got %v, want %v ", + alert.Expiration, newAlert.Expiration) + } + if alert.ID != newAlert.ID { + t.Errorf("NewAlertFromPayload: wrong ID - got %v, want %v ", + alert.ID, newAlert.ID) + } + if alert.Cancel != newAlert.Cancel { + t.Errorf("NewAlertFromPayload: wrong Cancel - got %v, want %v ", + alert.Cancel, newAlert.Cancel) + } + if len(alert.SetCancel) != len(newAlert.SetCancel) { + t.Errorf("NewAlertFromPayload: wrong number of SetCancel - got %v, want %v ", + len(alert.SetCancel), len(newAlert.SetCancel)) + } + for i := 0; i < len(alert.SetCancel); i++ { + if alert.SetCancel[i] != newAlert.SetCancel[i] { + t.Errorf("NewAlertFromPayload: wrong SetCancel[%v] - got %v, want %v ", + len(alert.SetCancel), alert.SetCancel[i], newAlert.SetCancel[i]) + } + } + if alert.MinVer != newAlert.MinVer { + t.Errorf("NewAlertFromPayload: wrong MinVer - got %v, want %v ", + alert.MinVer, newAlert.MinVer) + } + if alert.MaxVer != newAlert.MaxVer { + t.Errorf("NewAlertFromPayload: wrong MaxVer - got %v, want %v ", + alert.MaxVer, newAlert.MaxVer) + } + if len(alert.SetSubVer) != len(newAlert.SetSubVer) { + t.Errorf("NewAlertFromPayload: wrong number of SetSubVer - got %v, want %v ", + len(alert.SetSubVer), len(newAlert.SetSubVer)) + } + for i := 0; i < len(alert.SetSubVer); i++ { + if alert.SetSubVer[i] != newAlert.SetSubVer[i] { + t.Errorf("NewAlertFromPayload: wrong SetSubVer[%v] - got %v, want %v ", + len(alert.SetSubVer), alert.SetSubVer[i], newAlert.SetSubVer[i]) + } + } + if alert.Priority != newAlert.Priority { + t.Errorf("NewAlertFromPayload: wrong Priority - got %v, want %v ", + alert.Priority, newAlert.Priority) + } + if alert.Comment != newAlert.Comment { + t.Errorf("NewAlertFromPayload: wrong Comment - got %v, want %v ", + alert.Comment, newAlert.Comment) + } + if alert.StatusBar != newAlert.StatusBar { + t.Errorf("NewAlertFromPayload: wrong StatusBar - got %v, want %v ", + alert.StatusBar, newAlert.StatusBar) + } + if alert.Reserved != newAlert.Reserved { + t.Errorf("NewAlertFromPayload: wrong Reserved - got %v, want %v ", + alert.Reserved, newAlert.Reserved) + } +} + +// TestAlertErrors performs negative tests against payload serialization, +// deserialization of Alert to confirm error paths work correctly. +func TestAlertErrors(t *testing.T) { + pver := wire.ProtocolVersion + + baseAlert := wire.NewAlert( + 1, 1337093712, 1368628812, 1015, + 1013, []int32{1014}, 0, 40599, []string{"/Satoshi:0.7.2/"}, 5000, "", + "URGENT", + ) + baseAlertEncoded := []byte{ + 0x01, 0x00, 0x00, 0x00, 0x50, 0x6e, 0xb2, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x9e, 0x93, 0x51, //|....Pn.O....L..Q| + 0x00, 0x00, 0x00, 0x00, 0xf7, 0x03, 0x00, 0x00, 0xf5, 0x03, 0x00, 0x00, 0x01, 0xf6, 0x03, 0x00, //|................| + 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x9e, 0x00, 0x00, 0x01, 0x0f, 0x2f, 0x53, 0x61, 0x74, 0x6f, //|.........../Sato| + 0x73, 0x68, 0x69, 0x3a, 0x30, 0x2e, 0x37, 0x2e, 0x32, 0x2f, 0x88, 0x13, 0x00, 0x00, 0x00, 0x06, //|shi:0.7.2/......| + 0x55, 0x52, 0x47, 0x45, 0x4e, 0x54, 0x00, //|URGENT.| + } + tests := []struct { + in *wire.Alert // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error in Version + {baseAlert, baseAlertEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in SetCancel VarInt. + {baseAlert, baseAlertEncoded, pver, 28, io.ErrShortWrite, io.EOF}, + // Force error in SetCancel ints. + {baseAlert, baseAlertEncoded, pver, 29, io.ErrShortWrite, io.EOF}, + // Force error in MinVer + {baseAlert, baseAlertEncoded, pver, 40, io.ErrShortWrite, io.EOF}, + // Force error in SetSubVer string VarInt. + {baseAlert, baseAlertEncoded, pver, 41, io.ErrShortWrite, io.EOF}, + // Force error in SetSubVer strings. + {baseAlert, baseAlertEncoded, pver, 48, io.ErrShortWrite, io.EOF}, + // Force error in Priority + {baseAlert, baseAlertEncoded, pver, 60, io.ErrShortWrite, io.EOF}, + // Force error in Comment string. + {baseAlert, baseAlertEncoded, pver, 62, io.ErrShortWrite, io.EOF}, + // Force error in StatusBar string. + {baseAlert, baseAlertEncoded, pver, 64, io.ErrShortWrite, io.EOF}, + // Force error in Reserved string. + {baseAlert, baseAlertEncoded, pver, 70, io.ErrShortWrite, io.EOF}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + w := newFixedWriter(test.max) + err := test.in.Serialize(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("Alert.Serialize #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + var alert wire.Alert + r := newFixedReader(test.max, test.buf) + err = alert.Deserialize(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("Alert.Deserialize #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + } + + // overflow the max number of elements in SetCancel + // maxCountSetCancel + 1 == 8388575 == \xdf\xff\x7f\x00 + // replace bytes 29-33 + badAlertEncoded := []byte{ + 0x01, 0x00, 0x00, 0x00, 0x50, 0x6e, 0xb2, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x9e, 0x93, 0x51, //|....Pn.O....L..Q| + 0x00, 0x00, 0x00, 0x00, 0xf7, 0x03, 0x00, 0x00, 0xf5, 0x03, 0x00, 0x00, 0xfe, 0xdf, 0xff, 0x7f, //|................| + 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x9e, 0x00, 0x00, 0x01, 0x0f, 0x2f, 0x53, 0x61, 0x74, 0x6f, //|.........../Sato| + 0x73, 0x68, 0x69, 0x3a, 0x30, 0x2e, 0x37, 0x2e, 0x32, 0x2f, 0x88, 0x13, 0x00, 0x00, 0x00, 0x06, //|shi:0.7.2/......| + 0x55, 0x52, 0x47, 0x45, 0x4e, 0x54, 0x00, //|URGENT.| + } + var alert wire.Alert + r := bytes.NewReader(badAlertEncoded) + err := alert.Deserialize(r, pver) + if _, ok := err.(*wire.MessageError); !ok { + t.Errorf("Alert.Deserialize wrong error got: %T, want: %T", + err, wire.MessageError{}) + } + + // overflow the max number of elements in SetSubVer + // maxCountSetSubVer + 1 == 131071 + 1 == \x00\x00\x02\x00 + // replace bytes 42-46 + badAlertEncoded = []byte{ + 0x01, 0x00, 0x00, 0x00, 0x50, 0x6e, 0xb2, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x9e, 0x93, 0x51, //|....Pn.O....L..Q| + 0x00, 0x00, 0x00, 0x00, 0xf7, 0x03, 0x00, 0x00, 0xf5, 0x03, 0x00, 0x00, 0x01, 0xf6, 0x03, 0x00, //|................| + 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x9e, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x02, 0x00, 0x74, 0x6f, //|.........../Sato| + 0x73, 0x68, 0x69, 0x3a, 0x30, 0x2e, 0x37, 0x2e, 0x32, 0x2f, 0x88, 0x13, 0x00, 0x00, 0x00, 0x06, //|shi:0.7.2/......| + 0x55, 0x52, 0x47, 0x45, 0x4e, 0x54, 0x00, //|URGENT.| + } + r = bytes.NewReader(badAlertEncoded) + err = alert.Deserialize(r, pver) + if _, ok := err.(*wire.MessageError); !ok { + t.Errorf("Alert.Deserialize wrong error got: %T, want: %T", + err, wire.MessageError{}) + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgblock.go b/vendor/github.com/btcsuite/btcd/wire/msgblock.go new file mode 100644 index 0000000000000000000000000000000000000000..009d4175457a124ea5b7e17e0c1d307cf412db9f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgblock.go @@ -0,0 +1,248 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "bytes" + "fmt" + "io" +) + +// defaultTransactionAlloc is the default size used for the backing array +// for transactions. The transaction array will dynamically grow as needed, but +// this figure is intended to provide enough space for the number of +// transactions in the vast majority of blocks without needing to grow the +// backing array multiple times. +const defaultTransactionAlloc = 2048 + +// MaxBlocksPerMsg is the maximum number of blocks allowed per message. +const MaxBlocksPerMsg = 500 + +// MaxBlockPayload is the maximum bytes a block message can be in bytes. +const MaxBlockPayload = 1000000 // Not actually 1MB which would be 1024 * 1024 + +// maxTxPerBlock is the maximum number of transactions that could +// possibly fit into a block. +const maxTxPerBlock = (MaxBlockPayload / minTxPayload) + 1 + +// TxLoc holds locator data for the offset and length of where a transaction is +// located within a MsgBlock data buffer. +type TxLoc struct { + TxStart int + TxLen int +} + +// MsgBlock implements the Message interface and represents a bitcoin +// block message. It is used to deliver block and transaction information in +// response to a getdata message (MsgGetData) for a given block hash. +type MsgBlock struct { + Header BlockHeader + Transactions []*MsgTx +} + +// AddTransaction adds a transaction to the message. +func (msg *MsgBlock) AddTransaction(tx *MsgTx) error { + msg.Transactions = append(msg.Transactions, tx) + return nil + +} + +// ClearTransactions removes all transactions from the message. +func (msg *MsgBlock) ClearTransactions() { + msg.Transactions = make([]*MsgTx, 0, defaultTransactionAlloc) +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +// See Deserialize for decoding blocks stored to disk, such as in a database, as +// opposed to decoding blocks from the wire. +func (msg *MsgBlock) BtcDecode(r io.Reader, pver uint32) error { + err := readBlockHeader(r, pver, &msg.Header) + if err != nil { + return err + } + + txCount, err := ReadVarInt(r, pver) + if err != nil { + return err + } + + // Prevent more transactions than could possibly fit into a block. + // It would be possible to cause memory exhaustion and panics without + // a sane upper bound on this count. + if txCount > maxTxPerBlock { + str := fmt.Sprintf("too many transactions to fit into a block "+ + "[count %d, max %d]", txCount, maxTxPerBlock) + return messageError("MsgBlock.BtcDecode", str) + } + + msg.Transactions = make([]*MsgTx, 0, txCount) + for i := uint64(0); i < txCount; i++ { + tx := MsgTx{} + err := tx.BtcDecode(r, pver) + if err != nil { + return err + } + msg.Transactions = append(msg.Transactions, &tx) + } + + return nil +} + +// Deserialize decodes a block from r into the receiver using a format that is +// suitable for long-term storage such as a database while respecting the +// Version field in the block. This function differs from BtcDecode in that +// BtcDecode decodes from the bitcoin wire protocol as it was sent across the +// network. The wire encoding can technically differ depending on the protocol +// version and doesn't even really need to match the format of a stored block at +// all. As of the time this comment was written, the encoded block is the same +// in both instances, but there is a distinct difference and separating the two +// allows the API to be flexible enough to deal with changes. +func (msg *MsgBlock) Deserialize(r io.Reader) error { + // At the current time, there is no difference between the wire encoding + // at protocol version 0 and the stable long-term storage format. As + // a result, make use of BtcDecode. + return msg.BtcDecode(r, 0) +} + +// DeserializeTxLoc decodes r in the same manner Deserialize does, but it takes +// a byte buffer instead of a generic reader and returns a slice containing the +// start and length of each transaction within the raw data that is being +// deserialized. +func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) { + fullLen := r.Len() + + // At the current time, there is no difference between the wire encoding + // at protocol version 0 and the stable long-term storage format. As + // a result, make use of existing wire protocol functions. + err := readBlockHeader(r, 0, &msg.Header) + if err != nil { + return nil, err + } + + txCount, err := ReadVarInt(r, 0) + if err != nil { + return nil, err + } + + // Prevent more transactions than could possibly fit into a block. + // It would be possible to cause memory exhaustion and panics without + // a sane upper bound on this count. + if txCount > maxTxPerBlock { + str := fmt.Sprintf("too many transactions to fit into a block "+ + "[count %d, max %d]", txCount, maxTxPerBlock) + return nil, messageError("MsgBlock.DeserializeTxLoc", str) + } + + // Deserialize each transaction while keeping track of its location + // within the byte stream. + msg.Transactions = make([]*MsgTx, 0, txCount) + txLocs := make([]TxLoc, txCount) + for i := uint64(0); i < txCount; i++ { + txLocs[i].TxStart = fullLen - r.Len() + tx := MsgTx{} + err := tx.Deserialize(r) + if err != nil { + return nil, err + } + msg.Transactions = append(msg.Transactions, &tx) + txLocs[i].TxLen = (fullLen - r.Len()) - txLocs[i].TxStart + } + + return txLocs, nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +// See Serialize for encoding blocks to be stored to disk, such as in a +// database, as opposed to encoding blocks for the wire. +func (msg *MsgBlock) BtcEncode(w io.Writer, pver uint32) error { + err := writeBlockHeader(w, pver, &msg.Header) + if err != nil { + return err + } + + err = WriteVarInt(w, pver, uint64(len(msg.Transactions))) + if err != nil { + return err + } + + for _, tx := range msg.Transactions { + err = tx.BtcEncode(w, pver) + if err != nil { + return err + } + } + + return nil +} + +// Serialize encodes the block to w using a format that suitable for long-term +// storage such as a database while respecting the Version field in the block. +// This function differs from BtcEncode in that BtcEncode encodes the block to +// the bitcoin wire protocol in order to be sent across the network. The wire +// encoding can technically differ depending on the protocol version and doesn't +// even really need to match the format of a stored block at all. As of the +// time this comment was written, the encoded block is the same in both +// instances, but there is a distinct difference and separating the two allows +// the API to be flexible enough to deal with changes. +func (msg *MsgBlock) Serialize(w io.Writer) error { + // At the current time, there is no difference between the wire encoding + // at protocol version 0 and the stable long-term storage format. As + // a result, make use of BtcEncode. + return msg.BtcEncode(w, 0) +} + +// SerializeSize returns the number of bytes it would take to serialize the +// the block. +func (msg *MsgBlock) SerializeSize() int { + // Block header bytes + Serialized varint size for the number of + // transactions. + n := blockHeaderLen + VarIntSerializeSize(uint64(len(msg.Transactions))) + + for _, tx := range msg.Transactions { + n += tx.SerializeSize() + } + + return n +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgBlock) Command() string { + return CmdBlock +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgBlock) MaxPayloadLength(pver uint32) uint32 { + // Block header at 80 bytes + transaction count + max transactions + // which can vary up to the MaxBlockPayload (including the block header + // and transaction count). + return MaxBlockPayload +} + +// BlockSha computes the block identifier hash for this block. +func (msg *MsgBlock) BlockSha() ShaHash { + return msg.Header.BlockSha() +} + +// TxShas returns a slice of hashes of all of transactions in this block. +func (msg *MsgBlock) TxShas() ([]ShaHash, error) { + shaList := make([]ShaHash, 0, len(msg.Transactions)) + for _, tx := range msg.Transactions { + shaList = append(shaList, tx.TxSha()) + } + return shaList, nil +} + +// NewMsgBlock returns a new bitcoin block message that conforms to the +// Message interface. See MsgBlock for details. +func NewMsgBlock(blockHeader *BlockHeader) *MsgBlock { + return &MsgBlock{ + Header: *blockHeader, + Transactions: make([]*MsgTx, 0, defaultTransactionAlloc), + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgblock_test.go b/vendor/github.com/btcsuite/btcd/wire/msgblock_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7fe0cd7573e352659fa21b3b2e7f84d38a4397bf --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgblock_test.go @@ -0,0 +1,581 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + "time" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestBlock tests the MsgBlock API. +func TestBlock(t *testing.T) { + pver := wire.ProtocolVersion + + // Block 1 header. + prevHash := &blockOne.Header.PrevBlock + merkleHash := &blockOne.Header.MerkleRoot + bits := blockOne.Header.Bits + nonce := blockOne.Header.Nonce + bh := wire.NewBlockHeader(prevHash, merkleHash, bits, nonce) + + // Ensure the command is expected value. + wantCmd := "block" + msg := wire.NewMsgBlock(bh) + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgBlock: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + // Num addresses (varInt) + max allowed addresses. + wantPayload := uint32(1000000) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Ensure we get the same block header data back out. + if !reflect.DeepEqual(&msg.Header, bh) { + t.Errorf("NewMsgBlock: wrong block header - got %v, want %v", + spew.Sdump(&msg.Header), spew.Sdump(bh)) + } + + // Ensure transactions are added properly. + tx := blockOne.Transactions[0].Copy() + msg.AddTransaction(tx) + if !reflect.DeepEqual(msg.Transactions, blockOne.Transactions) { + t.Errorf("AddTransaction: wrong transactions - got %v, want %v", + spew.Sdump(msg.Transactions), + spew.Sdump(blockOne.Transactions)) + } + + // Ensure transactions are properly cleared. + msg.ClearTransactions() + if len(msg.Transactions) != 0 { + t.Errorf("ClearTransactions: wrong transactions - got %v, want %v", + len(msg.Transactions), 0) + } + + return +} + +// TestBlockTxShas tests the ability to generate a slice of all transaction +// hashes from a block accurately. +func TestBlockTxShas(t *testing.T) { + // Block 1, transaction 1 hash. + hashStr := "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098" + wantHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + return + } + + wantShas := []wire.ShaHash{*wantHash} + shas, err := blockOne.TxShas() + if err != nil { + t.Errorf("TxShas: %v", err) + } + if !reflect.DeepEqual(shas, wantShas) { + t.Errorf("TxShas: wrong transaction hashes - got %v, want %v", + spew.Sdump(shas), spew.Sdump(wantShas)) + } +} + +// TestBlockSha tests the ability to generate the hash of a block accurately. +func TestBlockSha(t *testing.T) { + // Block 1 hash. + hashStr := "839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048" + wantHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Ensure the hash produced is expected. + blockHash := blockOne.BlockSha() + if !blockHash.IsEqual(wantHash) { + t.Errorf("BlockSha: wrong hash - got %v, want %v", + spew.Sprint(blockHash), spew.Sprint(wantHash)) + } +} + +// TestBlockWire tests the MsgBlock wire encode and decode for various numbers +// of transaction inputs and outputs and protocol versions. +func TestBlockWire(t *testing.T) { + tests := []struct { + in *wire.MsgBlock // Message to encode + out *wire.MsgBlock // Expected decoded message + buf []byte // Wire encoding + txLocs []wire.TxLoc // Expected transaction locations + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + &blockOne, + &blockOne, + blockOneBytes, + blockOneTxLocs, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version. + { + &blockOne, + &blockOne, + blockOneBytes, + blockOneTxLocs, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version. + { + &blockOne, + &blockOne, + blockOneBytes, + blockOneTxLocs, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion. + { + &blockOne, + &blockOne, + blockOneBytes, + blockOneTxLocs, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion. + { + &blockOne, + &blockOne, + blockOneBytes, + blockOneTxLocs, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgBlock + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(&msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestBlockWireErrors performs negative tests against wire encode and decode +// of MsgBlock to confirm error paths work correctly. +func TestBlockWireErrors(t *testing.T) { + // Use protocol version 60002 specifically here instead of the latest + // because the test data is using bytes encoded with that protocol + // version. + pver := uint32(60002) + + tests := []struct { + in *wire.MsgBlock // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error in version. + {&blockOne, blockOneBytes, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in prev block hash. + {&blockOne, blockOneBytes, pver, 4, io.ErrShortWrite, io.EOF}, + // Force error in merkle root. + {&blockOne, blockOneBytes, pver, 36, io.ErrShortWrite, io.EOF}, + // Force error in timestamp. + {&blockOne, blockOneBytes, pver, 68, io.ErrShortWrite, io.EOF}, + // Force error in difficulty bits. + {&blockOne, blockOneBytes, pver, 72, io.ErrShortWrite, io.EOF}, + // Force error in header nonce. + {&blockOne, blockOneBytes, pver, 76, io.ErrShortWrite, io.EOF}, + // Force error in transaction count. + {&blockOne, blockOneBytes, pver, 80, io.ErrShortWrite, io.EOF}, + // Force error in transactions. + {&blockOne, blockOneBytes, pver, 81, io.ErrShortWrite, io.EOF}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // Decode from wire format. + var msg wire.MsgBlock + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + } +} + +// TestBlockSerialize tests MsgBlock serialize and deserialize. +func TestBlockSerialize(t *testing.T) { + tests := []struct { + in *wire.MsgBlock // Message to encode + out *wire.MsgBlock // Expected decoded message + buf []byte // Serialized data + txLocs []wire.TxLoc // Expected transaction locations + }{ + { + &blockOne, + &blockOne, + blockOneBytes, + blockOneTxLocs, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Serialize the block. + var buf bytes.Buffer + err := test.in.Serialize(&buf) + if err != nil { + t.Errorf("Serialize #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("Serialize #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Deserialize the block. + var block wire.MsgBlock + rbuf := bytes.NewReader(test.buf) + err = block.Deserialize(rbuf) + if err != nil { + t.Errorf("Deserialize #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&block, test.out) { + t.Errorf("Deserialize #%d\n got: %s want: %s", i, + spew.Sdump(&block), spew.Sdump(test.out)) + continue + } + + // Deserialize the block while gathering transaction location + // information. + var txLocBlock wire.MsgBlock + br := bytes.NewBuffer(test.buf) + txLocs, err := txLocBlock.DeserializeTxLoc(br) + if err != nil { + t.Errorf("DeserializeTxLoc #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&txLocBlock, test.out) { + t.Errorf("DeserializeTxLoc #%d\n got: %s want: %s", i, + spew.Sdump(&txLocBlock), spew.Sdump(test.out)) + continue + } + if !reflect.DeepEqual(txLocs, test.txLocs) { + t.Errorf("DeserializeTxLoc #%d\n got: %s want: %s", i, + spew.Sdump(txLocs), spew.Sdump(test.txLocs)) + continue + } + } +} + +// TestBlockSerializeErrors performs negative tests against wire encode and +// decode of MsgBlock to confirm error paths work correctly. +func TestBlockSerializeErrors(t *testing.T) { + tests := []struct { + in *wire.MsgBlock // Value to encode + buf []byte // Serialized data + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error in version. + {&blockOne, blockOneBytes, 0, io.ErrShortWrite, io.EOF}, + // Force error in prev block hash. + {&blockOne, blockOneBytes, 4, io.ErrShortWrite, io.EOF}, + // Force error in merkle root. + {&blockOne, blockOneBytes, 36, io.ErrShortWrite, io.EOF}, + // Force error in timestamp. + {&blockOne, blockOneBytes, 68, io.ErrShortWrite, io.EOF}, + // Force error in difficulty bits. + {&blockOne, blockOneBytes, 72, io.ErrShortWrite, io.EOF}, + // Force error in header nonce. + {&blockOne, blockOneBytes, 76, io.ErrShortWrite, io.EOF}, + // Force error in transaction count. + {&blockOne, blockOneBytes, 80, io.ErrShortWrite, io.EOF}, + // Force error in transactions. + {&blockOne, blockOneBytes, 81, io.ErrShortWrite, io.EOF}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Serialize the block. + w := newFixedWriter(test.max) + err := test.in.Serialize(w) + if err != test.writeErr { + t.Errorf("Serialize #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // Deserialize the block. + var block wire.MsgBlock + r := newFixedReader(test.max, test.buf) + err = block.Deserialize(r) + if err != test.readErr { + t.Errorf("Deserialize #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + var txLocBlock wire.MsgBlock + br := bytes.NewBuffer(test.buf[0:test.max]) + _, err = txLocBlock.DeserializeTxLoc(br) + if err != test.readErr { + t.Errorf("DeserializeTxLoc #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + } +} + +// TestBlockOverflowErrors performs tests to ensure deserializing blocks which +// are intentionally crafted to use large values for the number of transactions +// are handled properly. This could otherwise potentially be used as an attack +// vector. +func TestBlockOverflowErrors(t *testing.T) { + // Use protocol version 70001 specifically here instead of the latest + // protocol version because the test data is using bytes encoded with + // that version. + pver := uint32(70001) + + tests := []struct { + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + err error // Expected error + }{ + // Block that claims to have ~uint64(0) transactions. + { + []byte{ + 0x01, 0x00, 0x00, 0x00, // Version 1 + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot + 0x61, 0xbc, 0x66, 0x49, // Timestamp + 0xff, 0xff, 0x00, 0x1d, // Bits + 0x01, 0xe3, 0x62, 0x99, // Nonce + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, // TxnCount + }, pver, &wire.MessageError{}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Decode from wire format. + var msg wire.MsgBlock + r := bytes.NewReader(test.buf) + err := msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, reflect.TypeOf(test.err)) + continue + } + + // Deserialize from wire format. + r = bytes.NewReader(test.buf) + err = msg.Deserialize(r) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("Deserialize #%d wrong error got: %v, want: %v", + i, err, reflect.TypeOf(test.err)) + continue + } + + // Deserialize with transaction location info from wire format. + br := bytes.NewBuffer(test.buf) + _, err = msg.DeserializeTxLoc(br) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("DeserializeTxLoc #%d wrong error got: %v, "+ + "want: %v", i, err, reflect.TypeOf(test.err)) + continue + } + } +} + +// TestBlockSerializeSize performs tests to ensure the serialize size for +// various blocks is accurate. +func TestBlockSerializeSize(t *testing.T) { + // Block with no transactions. + noTxBlock := wire.NewMsgBlock(&blockOne.Header) + + tests := []struct { + in *wire.MsgBlock // Block to encode + size int // Expected serialized size + }{ + // Block with no transactions. + {noTxBlock, 81}, + + // First block in the mainnet block chain. + {&blockOne, len(blockOneBytes)}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + serializedSize := test.in.SerializeSize() + if serializedSize != test.size { + t.Errorf("MsgBlock.SerializeSize: #%d got: %d, want: "+ + "%d", i, serializedSize, test.size) + continue + } + } +} + +var blockOne = wire.MsgBlock{ + Header: wire.BlockHeader{ + Version: 1, + PrevBlock: wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + }), + MerkleRoot: wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, + }), + + Timestamp: time.Unix(0x4966bc61, 0), // 2009-01-08 20:54:25 -0600 CST + Bits: 0x1d00ffff, // 486604799 + Nonce: 0x9962e301, // 2573394689 + }, + Transactions: []*wire.MsgTx{ + { + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{ + 0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 0x12a05f200, + PkScript: []byte{ + 0x41, // OP_DATA_65 + 0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c, + 0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16, + 0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c, + 0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c, + 0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4, + 0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6, + 0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e, + 0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58, + 0xee, // 65-byte signature + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 0, + }, + }, +} + +// Block one serialized bytes. +var blockOneBytes = []byte{ + 0x01, 0x00, 0x00, 0x00, // Version 1 + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot + 0x61, 0xbc, 0x66, 0x49, // Timestamp + 0xff, 0xff, 0x00, 0x1d, // Bits + 0x01, 0xe3, 0x62, 0x99, // Nonce + 0x01, // TxnCount + 0x01, 0x00, 0x00, 0x00, // Version + 0x01, // Varint for number of transaction inputs + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash + 0xff, 0xff, 0xff, 0xff, // Prevous output index + 0x07, // Varint for length of signature script + 0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, // Signature script (coinbase) + 0xff, 0xff, 0xff, 0xff, // Sequence + 0x01, // Varint for number of transaction outputs + 0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount + 0x43, // Varint for length of pk script + 0x41, // OP_DATA_65 + 0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c, + 0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16, + 0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c, + 0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c, + 0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4, + 0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6, + 0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e, + 0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58, + 0xee, // 65-byte uncompressed public key + 0xac, // OP_CHECKSIG + 0x00, 0x00, 0x00, 0x00, // Lock time +} + +// Transaction location information for block one transactions. +var blockOneTxLocs = []wire.TxLoc{ + {TxStart: 81, TxLen: 134}, +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgfilteradd.go b/vendor/github.com/btcsuite/btcd/wire/msgfilteradd.go new file mode 100644 index 0000000000000000000000000000000000000000..244ae4d09cf4e93cdbfb35a958055a2829ac14cc --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgfilteradd.go @@ -0,0 +1,90 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +const ( + // MaxFilterAddDataSize is the maximum byte size of a data + // element to add to the Bloom filter. It is equal to the + // maximum element size of a script. + MaxFilterAddDataSize = 520 +) + +// MsgFilterAdd implements the Message interface and represents a bitcoin +// filteradd message. It is used to add a data element to an existing Bloom +// filter. +// +// This message was not added until protocol version BIP0037Version. +type MsgFilterAdd struct { + Data []byte +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgFilterAdd) BtcDecode(r io.Reader, pver uint32) error { + if pver < BIP0037Version { + str := fmt.Sprintf("filteradd message invalid for protocol "+ + "version %d", pver) + return messageError("MsgFilterAdd.BtcDecode", str) + } + + var err error + msg.Data, err = ReadVarBytes(r, pver, MaxFilterAddDataSize, + "filteradd data") + if err != nil { + return err + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgFilterAdd) BtcEncode(w io.Writer, pver uint32) error { + if pver < BIP0037Version { + str := fmt.Sprintf("filteradd message invalid for protocol "+ + "version %d", pver) + return messageError("MsgFilterAdd.BtcEncode", str) + } + + size := len(msg.Data) + if size > MaxFilterAddDataSize { + str := fmt.Sprintf("filteradd size too large for message "+ + "[size %v, max %v]", size, MaxFilterAddDataSize) + return messageError("MsgFilterAdd.BtcEncode", str) + } + + err := WriteVarBytes(w, pver, msg.Data) + if err != nil { + return err + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgFilterAdd) Command() string { + return CmdFilterAdd +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgFilterAdd) MaxPayloadLength(pver uint32) uint32 { + return uint32(VarIntSerializeSize(MaxFilterAddDataSize)) + + MaxFilterAddDataSize +} + +// NewMsgFilterAdd returns a new bitcoin filteradd message that conforms to the +// Message interface. See MsgFilterAdd for details. +func NewMsgFilterAdd(data []byte) *MsgFilterAdd { + return &MsgFilterAdd{ + Data: data, + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgfilteradd_test.go b/vendor/github.com/btcsuite/btcd/wire/msgfilteradd_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c45573060e0f849130414d2bc35e09af32f84304 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgfilteradd_test.go @@ -0,0 +1,189 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" +) + +// TestFilterAddLatest tests the MsgFilterAdd API against the latest protocol +// version. +func TestFilterAddLatest(t *testing.T) { + pver := wire.ProtocolVersion + + data := []byte{0x01, 0x02} + msg := wire.NewMsgFilterAdd(data) + + // Ensure the command is expected value. + wantCmd := "filteradd" + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgFilterAdd: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + wantPayload := uint32(523) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Test encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, pver) + if err != nil { + t.Errorf("encode of MsgFilterAdd failed %v err <%v>", msg, err) + } + + // Test decode with latest protocol version. + var readmsg wire.MsgFilterAdd + err = readmsg.BtcDecode(&buf, pver) + if err != nil { + t.Errorf("decode of MsgFilterAdd failed [%v] err <%v>", buf, err) + } + + return +} + +// TestFilterAddCrossProtocol tests the MsgFilterAdd API when encoding with the +// latest protocol version and decoding with BIP0031Version. +func TestFilterAddCrossProtocol(t *testing.T) { + data := []byte{0x01, 0x02} + msg := wire.NewMsgFilterAdd(data) + if !bytes.Equal(msg.Data, data) { + t.Errorf("should get same data back out") + } + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, wire.ProtocolVersion) + if err != nil { + t.Errorf("encode of MsgFilterAdd failed %v err <%v>", msg, err) + } + + // Decode with old protocol version. + var readmsg wire.MsgFilterAdd + err = readmsg.BtcDecode(&buf, wire.BIP0031Version) + if err == nil { + t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't "+ + "have %v", msg) + } + + // Since one of the protocol versions doesn't support the filteradd + // message, make sure the data didn't get encoded and decoded back out. + if bytes.Equal(msg.Data, readmsg.Data) { + t.Error("should not get same data for cross protocol") + } + +} + +// TestFilterAddMaxDataSize tests the MsgFilterAdd API maximum data size. +func TestFilterAddMaxDataSize(t *testing.T) { + data := bytes.Repeat([]byte{0xff}, 521) + msg := wire.NewMsgFilterAdd(data) + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, wire.ProtocolVersion) + if err == nil { + t.Errorf("encode of MsgFilterAdd succeeded when it shouldn't "+ + "have %v", msg) + } + + // Decode with latest protocol version. + readbuf := bytes.NewReader(data) + err = msg.BtcDecode(readbuf, wire.ProtocolVersion) + if err == nil { + t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't "+ + "have %v", msg) + } +} + +// TestFilterAddWireErrors performs negative tests against wire encode and decode +// of MsgFilterAdd to confirm error paths work correctly. +func TestFilterAddWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + pverNoFilterAdd := wire.BIP0037Version - 1 + wireErr := &wire.MessageError{} + + baseData := []byte{0x01, 0x02, 0x03, 0x04} + baseFilterAdd := wire.NewMsgFilterAdd(baseData) + baseFilterAddEncoded := append([]byte{0x04}, baseData...) + + tests := []struct { + in *wire.MsgFilterAdd // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Latest protocol version with intentional read/write errors. + // Force error in data size. + { + baseFilterAdd, baseFilterAddEncoded, pver, 0, + io.ErrShortWrite, io.EOF, + }, + // Force error in data. + { + baseFilterAdd, baseFilterAddEncoded, pver, 1, + io.ErrShortWrite, io.EOF, + }, + // Force error due to unsupported protocol version. + { + baseFilterAdd, baseFilterAddEncoded, pverNoFilterAdd, 5, + wireErr, wireErr, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgFilterAdd + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgfilterclear.go b/vendor/github.com/btcsuite/btcd/wire/msgfilterclear.go new file mode 100644 index 0000000000000000000000000000000000000000..b82d6b85c0bbfde9b4f4cc873c2d7966857f8603 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgfilterclear.go @@ -0,0 +1,59 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// MsgFilterClear implements the Message interface and represents a bitcoin +// filterclear message which is used to reset a Bloom filter. +// +// This message was not added until protocol version BIP0037Version and has +// no payload. +type MsgFilterClear struct{} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgFilterClear) BtcDecode(r io.Reader, pver uint32) error { + if pver < BIP0037Version { + str := fmt.Sprintf("filterclear message invalid for protocol "+ + "version %d", pver) + return messageError("MsgFilterClear.BtcDecode", str) + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgFilterClear) BtcEncode(w io.Writer, pver uint32) error { + if pver < BIP0037Version { + str := fmt.Sprintf("filterclear message invalid for protocol "+ + "version %d", pver) + return messageError("MsgFilterClear.BtcEncode", str) + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgFilterClear) Command() string { + return CmdFilterClear +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgFilterClear) MaxPayloadLength(pver uint32) uint32 { + return 0 +} + +// NewMsgFilterClear returns a new bitcoin filterclear message that conforms to the Message +// interface. See MsgFilterClear for details. +func NewMsgFilterClear() *MsgFilterClear { + return &MsgFilterClear{} +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgfilterclear_test.go b/vendor/github.com/btcsuite/btcd/wire/msgfilterclear_test.go new file mode 100644 index 0000000000000000000000000000000000000000..edd210c6797e674a90d3dd838859e6370b165c63 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgfilterclear_test.go @@ -0,0 +1,197 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestFilterCLearLatest tests the MsgFilterClear API against the latest +// protocol version. +func TestFilterClearLatest(t *testing.T) { + pver := wire.ProtocolVersion + + msg := wire.NewMsgFilterClear() + + // Ensure the command is expected value. + wantCmd := "filterclear" + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgFilterClear: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + wantPayload := uint32(0) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + return +} + +// TestFilterClearCrossProtocol tests the MsgFilterClear API when encoding with +// the latest protocol version and decoding with BIP0031Version. +func TestFilterClearCrossProtocol(t *testing.T) { + msg := wire.NewMsgFilterClear() + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, wire.ProtocolVersion) + if err != nil { + t.Errorf("encode of MsgFilterClear failed %v err <%v>", msg, err) + } + + // Decode with old protocol version. + var readmsg wire.MsgFilterClear + err = readmsg.BtcDecode(&buf, wire.BIP0031Version) + if err == nil { + t.Errorf("decode of MsgFilterClear succeeded when it "+ + "shouldn't have %v", msg) + } +} + +// TestFilterClearWire tests the MsgFilterClear wire encode and decode for +// various protocol versions. +func TestFilterClearWire(t *testing.T) { + msgFilterClear := wire.NewMsgFilterClear() + msgFilterClearEncoded := []byte{} + + tests := []struct { + in *wire.MsgFilterClear // Message to encode + out *wire.MsgFilterClear // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + msgFilterClear, + msgFilterClear, + msgFilterClearEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0037Version + 1. + { + msgFilterClear, + msgFilterClear, + msgFilterClearEncoded, + wire.BIP0037Version + 1, + }, + + // Protocol version BIP0037Version. + { + msgFilterClear, + msgFilterClear, + msgFilterClearEncoded, + wire.BIP0037Version, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgFilterClear + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestFilterClearWireErrors performs negative tests against wire encode and +// decode of MsgFilterClear to confirm error paths work correctly. +func TestFilterClearWireErrors(t *testing.T) { + pverNoFilterClear := wire.BIP0037Version - 1 + wireErr := &wire.MessageError{} + + baseFilterClear := wire.NewMsgFilterClear() + baseFilterClearEncoded := []byte{} + + tests := []struct { + in *wire.MsgFilterClear // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error due to unsupported protocol version. + { + baseFilterClear, baseFilterClearEncoded, + pverNoFilterClear, 4, wireErr, wireErr, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgFilterClear + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgfilterload.go b/vendor/github.com/btcsuite/btcd/wire/msgfilterload.go new file mode 100644 index 0000000000000000000000000000000000000000..b8bdd147c02fb8711cdb6def9e115322b22be4c7 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgfilterload.go @@ -0,0 +1,141 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// BloomUpdateType specifies how the filter is updated when a match is found +type BloomUpdateType uint8 + +const ( + // BloomUpdateNone indicates the filter is not adjusted when a match is + // found. + BloomUpdateNone BloomUpdateType = 0 + + // BloomUpdateAll indicates if the filter matches any data element in a + // public key script, the outpoint is serialized and inserted into the + // filter. + BloomUpdateAll BloomUpdateType = 1 + + // BloomUpdateP2PubkeyOnly indicates if the filter matches a data + // element in a public key script and the script is of the standard + // pay-to-pubkey or multisig, the outpoint is serialized and inserted + // into the filter. + BloomUpdateP2PubkeyOnly BloomUpdateType = 2 +) + +const ( + // MaxFilterLoadHashFuncs is the maximum number of hash functions to + // load into the Bloom filter. + MaxFilterLoadHashFuncs = 50 + + // MaxFilterLoadFilterSize is the maximum size in bytes a filter may be. + MaxFilterLoadFilterSize = 36000 +) + +// MsgFilterLoad implements the Message interface and represents a bitcoin +// filterload message which is used to reset a Bloom filter. +// +// This message was not added until protocol version BIP0037Version. +type MsgFilterLoad struct { + Filter []byte + HashFuncs uint32 + Tweak uint32 + Flags BloomUpdateType +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgFilterLoad) BtcDecode(r io.Reader, pver uint32) error { + if pver < BIP0037Version { + str := fmt.Sprintf("filterload message invalid for protocol "+ + "version %d", pver) + return messageError("MsgFilterLoad.BtcDecode", str) + } + + var err error + msg.Filter, err = ReadVarBytes(r, pver, MaxFilterLoadFilterSize, + "filterload filter size") + if err != nil { + return err + } + + err = readElements(r, &msg.HashFuncs, &msg.Tweak, &msg.Flags) + if err != nil { + return err + } + + if msg.HashFuncs > MaxFilterLoadHashFuncs { + str := fmt.Sprintf("too many filter hash functions for message "+ + "[count %v, max %v]", msg.HashFuncs, MaxFilterLoadHashFuncs) + return messageError("MsgFilterLoad.BtcDecode", str) + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgFilterLoad) BtcEncode(w io.Writer, pver uint32) error { + if pver < BIP0037Version { + str := fmt.Sprintf("filterload message invalid for protocol "+ + "version %d", pver) + return messageError("MsgFilterLoad.BtcEncode", str) + } + + size := len(msg.Filter) + if size > MaxFilterLoadFilterSize { + str := fmt.Sprintf("filterload filter size too large for message "+ + "[size %v, max %v]", size, MaxFilterLoadFilterSize) + return messageError("MsgFilterLoad.BtcEncode", str) + } + + if msg.HashFuncs > MaxFilterLoadHashFuncs { + str := fmt.Sprintf("too many filter hash functions for message "+ + "[count %v, max %v]", msg.HashFuncs, MaxFilterLoadHashFuncs) + return messageError("MsgFilterLoad.BtcEncode", str) + } + + err := WriteVarBytes(w, pver, msg.Filter) + if err != nil { + return err + } + + err = writeElements(w, msg.HashFuncs, msg.Tweak, msg.Flags) + if err != nil { + return err + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgFilterLoad) Command() string { + return CmdFilterLoad +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgFilterLoad) MaxPayloadLength(pver uint32) uint32 { + // Num filter bytes (varInt) + filter + 4 bytes hash funcs + + // 4 bytes tweak + 1 byte flags. + return uint32(VarIntSerializeSize(MaxFilterLoadFilterSize)) + + MaxFilterLoadFilterSize + 9 +} + +// NewMsgFilterLoad returns a new bitcoin filterload message that conforms to +// the Message interface. See MsgFilterLoad for details. +func NewMsgFilterLoad(filter []byte, hashFuncs uint32, tweak uint32, flags BloomUpdateType) *MsgFilterLoad { + return &MsgFilterLoad{ + Filter: filter, + HashFuncs: hashFuncs, + Tweak: tweak, + Flags: flags, + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgfilterload_test.go b/vendor/github.com/btcsuite/btcd/wire/msgfilterload_test.go new file mode 100644 index 0000000000000000000000000000000000000000..97503a44db1b48094644d9e0443c27a17977ecb4 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgfilterload_test.go @@ -0,0 +1,230 @@ +// Copyright (c) 2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" +) + +// TestFilterCLearLatest tests the MsgFilterLoad API against the latest protocol +// version. +func TestFilterLoadLatest(t *testing.T) { + pver := wire.ProtocolVersion + + data := []byte{0x01, 0x02} + msg := wire.NewMsgFilterLoad(data, 10, 0, 0) + + // Ensure the command is expected value. + wantCmd := "filterload" + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgFilterLoad: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + wantPayload := uint32(36012) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayLoadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Test encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, pver) + if err != nil { + t.Errorf("encode of MsgFilterLoad failed %v err <%v>", msg, err) + } + + // Test decode with latest protocol version. + readmsg := wire.MsgFilterLoad{} + err = readmsg.BtcDecode(&buf, pver) + if err != nil { + t.Errorf("decode of MsgFilterLoad failed [%v] err <%v>", buf, err) + } + + return +} + +// TestFilterLoadCrossProtocol tests the MsgFilterLoad API when encoding with +// the latest protocol version and decoding with BIP0031Version. +func TestFilterLoadCrossProtocol(t *testing.T) { + data := []byte{0x01, 0x02} + msg := wire.NewMsgFilterLoad(data, 10, 0, 0) + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, wire.ProtocolVersion) + if err != nil { + t.Errorf("encode of NewMsgFilterLoad failed %v err <%v>", msg, + err) + } + + // Decode with old protocol version. + var readmsg wire.MsgFilterLoad + err = readmsg.BtcDecode(&buf, wire.BIP0031Version) + if err == nil { + t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't have %v", + msg) + } +} + +// TestFilterLoadMaxFilterSize tests the MsgFilterLoad API maximum filter size. +func TestFilterLoadMaxFilterSize(t *testing.T) { + data := bytes.Repeat([]byte{0xff}, 36001) + msg := wire.NewMsgFilterLoad(data, 10, 0, 0) + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, wire.ProtocolVersion) + if err == nil { + t.Errorf("encode of MsgFilterLoad succeeded when it shouldn't "+ + "have %v", msg) + } + + // Decode with latest protocol version. + readbuf := bytes.NewReader(data) + err = msg.BtcDecode(readbuf, wire.ProtocolVersion) + if err == nil { + t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't "+ + "have %v", msg) + } +} + +// TestFilterLoadMaxHashFuncsSize tests the MsgFilterLoad API maximum hash functions. +func TestFilterLoadMaxHashFuncsSize(t *testing.T) { + data := bytes.Repeat([]byte{0xff}, 10) + msg := wire.NewMsgFilterLoad(data, 61, 0, 0) + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, wire.ProtocolVersion) + if err == nil { + t.Errorf("encode of MsgFilterLoad succeeded when it shouldn't have %v", + msg) + } + + newBuf := []byte{ + 0x0a, // filter size + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // filter + 0x3d, 0x00, 0x00, 0x00, // max hash funcs + 0x00, 0x00, 0x00, 0x00, // tweak + 0x00, // update Type + } + // Decode with latest protocol version. + readbuf := bytes.NewReader(newBuf) + err = msg.BtcDecode(readbuf, wire.ProtocolVersion) + if err == nil { + t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't have %v", + msg) + } +} + +// TestFilterLoadWireErrors performs negative tests against wire encode and decode +// of MsgFilterLoad to confirm error paths work correctly. +func TestFilterLoadWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + pverNoFilterLoad := wire.BIP0037Version - 1 + wireErr := &wire.MessageError{} + + baseFilter := []byte{0x01, 0x02, 0x03, 0x04} + baseFilterLoad := wire.NewMsgFilterLoad(baseFilter, 10, 0, + wire.BloomUpdateNone) + baseFilterLoadEncoded := append([]byte{0x04}, baseFilter...) + baseFilterLoadEncoded = append(baseFilterLoadEncoded, + 0x00, 0x00, 0x00, 0x0a, // HashFuncs + 0x00, 0x00, 0x00, 0x00, // Tweak + 0x00) // Flags + + tests := []struct { + in *wire.MsgFilterLoad // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Latest protocol version with intentional read/write errors. + // Force error in filter size. + { + baseFilterLoad, baseFilterLoadEncoded, pver, 0, + io.ErrShortWrite, io.EOF, + }, + // Force error in filter. + { + baseFilterLoad, baseFilterLoadEncoded, pver, 1, + io.ErrShortWrite, io.EOF, + }, + // Force error in hash funcs. + { + baseFilterLoad, baseFilterLoadEncoded, pver, 5, + io.ErrShortWrite, io.EOF, + }, + // Force error in tweak. + { + baseFilterLoad, baseFilterLoadEncoded, pver, 9, + io.ErrShortWrite, io.EOF, + }, + // Force error in flags. + { + baseFilterLoad, baseFilterLoadEncoded, pver, 13, + io.ErrShortWrite, io.EOF, + }, + // Force error due to unsupported protocol version. + { + baseFilterLoad, baseFilterLoadEncoded, pverNoFilterLoad, + 10, wireErr, wireErr, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgFilterLoad + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msggetaddr.go b/vendor/github.com/btcsuite/btcd/wire/msggetaddr.go new file mode 100644 index 0000000000000000000000000000000000000000..0a4bf57ad9e80e83e41716169c9aaf22b3f48b85 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msggetaddr.go @@ -0,0 +1,47 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "io" +) + +// MsgGetAddr implements the Message interface and represents a bitcoin +// getaddr message. It is used to request a list of known active peers on the +// network from a peer to help identify potential nodes. The list is returned +// via one or more addr messages (MsgAddr). +// +// This message has no payload. +type MsgGetAddr struct{} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgGetAddr) BtcDecode(r io.Reader, pver uint32) error { + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgGetAddr) BtcEncode(w io.Writer, pver uint32) error { + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgGetAddr) Command() string { + return CmdGetAddr +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgGetAddr) MaxPayloadLength(pver uint32) uint32 { + return 0 +} + +// NewMsgGetAddr returns a new bitcoin getaddr message that conforms to the +// Message interface. See MsgGetAddr for details. +func NewMsgGetAddr() *MsgGetAddr { + return &MsgGetAddr{} +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msggetaddr_test.go b/vendor/github.com/btcsuite/btcd/wire/msggetaddr_test.go new file mode 100644 index 0000000000000000000000000000000000000000..15c68bd79318a6a5cb87e82c287c8888270eca94 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msggetaddr_test.go @@ -0,0 +1,123 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestGetAddr tests the MsgGetAddr API. +func TestGetAddr(t *testing.T) { + pver := wire.ProtocolVersion + + // Ensure the command is expected value. + wantCmd := "getaddr" + msg := wire.NewMsgGetAddr() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgGetAddr: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + // Num addresses (varInt) + max allowed addresses. + wantPayload := uint32(0) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + return +} + +// TestGetAddrWire tests the MsgGetAddr wire encode and decode for various +// protocol versions. +func TestGetAddrWire(t *testing.T) { + msgGetAddr := wire.NewMsgGetAddr() + msgGetAddrEncoded := []byte{} + + tests := []struct { + in *wire.MsgGetAddr // Message to encode + out *wire.MsgGetAddr // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + msgGetAddr, + msgGetAddr, + msgGetAddrEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version. + { + msgGetAddr, + msgGetAddr, + msgGetAddrEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version. + { + msgGetAddr, + msgGetAddr, + msgGetAddrEncoded, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion. + { + msgGetAddr, + msgGetAddr, + msgGetAddrEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion. + { + msgGetAddr, + msgGetAddr, + msgGetAddrEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgGetAddr + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msggetblocks.go b/vendor/github.com/btcsuite/btcd/wire/msggetblocks.go new file mode 100644 index 0000000000000000000000000000000000000000..4d8d5b58180fc0bfb92a2d56036720cab6303d1b --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msggetblocks.go @@ -0,0 +1,144 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// MaxBlockLocatorsPerMsg is the maximum number of block locator hashes allowed +// per message. +const MaxBlockLocatorsPerMsg = 500 + +// MsgGetBlocks implements the Message interface and represents a bitcoin +// getblocks message. It is used to request a list of blocks starting after the +// last known hash in the slice of block locator hashes. The list is returned +// via an inv message (MsgInv) and is limited by a specific hash to stop at or +// the maximum number of blocks per message, which is currently 500. +// +// Set the HashStop field to the hash at which to stop and use +// AddBlockLocatorHash to build up the list of block locator hashes. +// +// The algorithm for building the block locator hashes should be to add the +// hashes in reverse order until you reach the genesis block. In order to keep +// the list of locator hashes to a reasonable number of entries, first add the +// most recent 10 block hashes, then double the step each loop iteration to +// exponentially decrease the number of hashes the further away from head and +// closer to the genesis block you get. +type MsgGetBlocks struct { + ProtocolVersion uint32 + BlockLocatorHashes []*ShaHash + HashStop ShaHash +} + +// AddBlockLocatorHash adds a new block locator hash to the message. +func (msg *MsgGetBlocks) AddBlockLocatorHash(hash *ShaHash) error { + if len(msg.BlockLocatorHashes)+1 > MaxBlockLocatorsPerMsg { + str := fmt.Sprintf("too many block locator hashes for message [max %v]", + MaxBlockLocatorsPerMsg) + return messageError("MsgGetBlocks.AddBlockLocatorHash", str) + } + + msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash) + return nil +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgGetBlocks) BtcDecode(r io.Reader, pver uint32) error { + err := readElement(r, &msg.ProtocolVersion) + if err != nil { + return err + } + + // Read num block locator hashes and limit to max. + count, err := ReadVarInt(r, pver) + if err != nil { + return err + } + if count > MaxBlockLocatorsPerMsg { + str := fmt.Sprintf("too many block locator hashes for message "+ + "[count %v, max %v]", count, MaxBlockLocatorsPerMsg) + return messageError("MsgGetBlocks.BtcDecode", str) + } + + msg.BlockLocatorHashes = make([]*ShaHash, 0, count) + for i := uint64(0); i < count; i++ { + sha := ShaHash{} + err := readElement(r, &sha) + if err != nil { + return err + } + msg.AddBlockLocatorHash(&sha) + } + + err = readElement(r, &msg.HashStop) + if err != nil { + return err + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgGetBlocks) BtcEncode(w io.Writer, pver uint32) error { + count := len(msg.BlockLocatorHashes) + if count > MaxBlockLocatorsPerMsg { + str := fmt.Sprintf("too many block locator hashes for message "+ + "[count %v, max %v]", count, MaxBlockLocatorsPerMsg) + return messageError("MsgGetBlocks.BtcEncode", str) + } + + err := writeElement(w, msg.ProtocolVersion) + if err != nil { + return err + } + + err = WriteVarInt(w, pver, uint64(count)) + if err != nil { + return err + } + + for _, hash := range msg.BlockLocatorHashes { + err = writeElement(w, hash) + if err != nil { + return err + } + } + + err = writeElement(w, &msg.HashStop) + if err != nil { + return err + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgGetBlocks) Command() string { + return CmdGetBlocks +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgGetBlocks) MaxPayloadLength(pver uint32) uint32 { + // Protocol version 4 bytes + num hashes (varInt) + max block locator + // hashes + hash stop. + return 4 + MaxVarIntPayload + (MaxBlockLocatorsPerMsg * HashSize) + HashSize +} + +// NewMsgGetBlocks returns a new bitcoin getblocks message that conforms to the +// Message interface using the passed parameters and defaults for the remaining +// fields. +func NewMsgGetBlocks(hashStop *ShaHash) *MsgGetBlocks { + return &MsgGetBlocks{ + ProtocolVersion: ProtocolVersion, + BlockLocatorHashes: make([]*ShaHash, 0, MaxBlockLocatorsPerMsg), + HashStop: *hashStop, + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msggetblocks_test.go b/vendor/github.com/btcsuite/btcd/wire/msggetblocks_test.go new file mode 100644 index 0000000000000000000000000000000000000000..69806477d425413c03e1095503f7dcf611ae386f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msggetblocks_test.go @@ -0,0 +1,390 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestGetBlocks tests the MsgGetBlocks API. +func TestGetBlocks(t *testing.T) { + pver := wire.ProtocolVersion + + // Block 99500 hash. + hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0" + locatorHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Block 100000 hash. + hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" + hashStop, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Ensure we get the same data back out. + msg := wire.NewMsgGetBlocks(hashStop) + if !msg.HashStop.IsEqual(hashStop) { + t.Errorf("NewMsgGetBlocks: wrong stop hash - got %v, want %v", + msg.HashStop, hashStop) + } + + // Ensure the command is expected value. + wantCmd := "getblocks" + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgGetBlocks: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + // Protocol version 4 bytes + num hashes (varInt) + max block locator + // hashes + hash stop. + wantPayload := uint32(16045) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Ensure block locator hashes are added properly. + err = msg.AddBlockLocatorHash(locatorHash) + if err != nil { + t.Errorf("AddBlockLocatorHash: %v", err) + } + if msg.BlockLocatorHashes[0] != locatorHash { + t.Errorf("AddBlockLocatorHash: wrong block locator added - "+ + "got %v, want %v", + spew.Sprint(msg.BlockLocatorHashes[0]), + spew.Sprint(locatorHash)) + } + + // Ensure adding more than the max allowed block locator hashes per + // message returns an error. + for i := 0; i < wire.MaxBlockLocatorsPerMsg; i++ { + err = msg.AddBlockLocatorHash(locatorHash) + } + if err == nil { + t.Errorf("AddBlockLocatorHash: expected error on too many " + + "block locator hashes not received") + } + + return +} + +// TestGetBlocksWire tests the MsgGetBlocks wire encode and decode for various +// numbers of block locator hashes and protocol versions. +func TestGetBlocksWire(t *testing.T) { + // Set protocol inside getblocks message. + pver := uint32(60002) + + // Block 99499 hash. + hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535" + hashLocator, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Block 99500 hash. + hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0" + hashLocator2, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Block 100000 hash. + hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" + hashStop, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // MsgGetBlocks message with no block locators or stop hash. + noLocators := wire.NewMsgGetBlocks(&wire.ShaHash{}) + noLocators.ProtocolVersion = pver + noLocatorsEncoded := []byte{ + 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 + 0x00, // Varint for number of block locator hashes + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop + } + + // MsgGetBlocks message with multiple block locators and a stop hash. + multiLocators := wire.NewMsgGetBlocks(hashStop) + multiLocators.AddBlockLocatorHash(hashLocator2) + multiLocators.AddBlockLocatorHash(hashLocator) + multiLocators.ProtocolVersion = pver + multiLocatorsEncoded := []byte{ + 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 + 0x02, // Varint for number of block locator hashes + 0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63, + 0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65, + 0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b, + 0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash + 0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60, + 0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9, + 0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40, + 0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash + 0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39, + 0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2, + 0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa, + 0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop + } + + tests := []struct { + in *wire.MsgGetBlocks // Message to encode + out *wire.MsgGetBlocks // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version with no block locators. + { + noLocators, + noLocators, + noLocatorsEncoded, + wire.ProtocolVersion, + }, + + // Latest protocol version with multiple block locators. + { + multiLocators, + multiLocators, + multiLocatorsEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version with no block locators. + { + noLocators, + noLocators, + noLocatorsEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0035Version with multiple block locators. + { + multiLocators, + multiLocators, + multiLocatorsEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version with no block locators. + { + noLocators, + noLocators, + noLocatorsEncoded, + wire.BIP0031Version, + }, + + // Protocol version BIP0031Versionwith multiple block locators. + { + multiLocators, + multiLocators, + multiLocatorsEncoded, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion with no block locators. + { + noLocators, + noLocators, + noLocatorsEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version NetAddressTimeVersion multiple block locators. + { + multiLocators, + multiLocators, + multiLocatorsEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion with no block locators. + { + noLocators, + noLocators, + noLocatorsEncoded, + wire.MultipleAddressVersion, + }, + + // Protocol version MultipleAddressVersion multiple block locators. + { + multiLocators, + multiLocators, + multiLocatorsEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgGetBlocks + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(&msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestGetBlocksWireErrors performs negative tests against wire encode and +// decode of MsgGetBlocks to confirm error paths work correctly. +func TestGetBlocksWireErrors(t *testing.T) { + // Set protocol inside getheaders message. Use protocol version 60002 + // specifically here instead of the latest because the test data is + // using bytes encoded with that protocol version. + pver := uint32(60002) + wireErr := &wire.MessageError{} + + // Block 99499 hash. + hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535" + hashLocator, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Block 99500 hash. + hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0" + hashLocator2, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Block 100000 hash. + hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" + hashStop, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // MsgGetBlocks message with multiple block locators and a stop hash. + baseGetBlocks := wire.NewMsgGetBlocks(hashStop) + baseGetBlocks.ProtocolVersion = pver + baseGetBlocks.AddBlockLocatorHash(hashLocator2) + baseGetBlocks.AddBlockLocatorHash(hashLocator) + baseGetBlocksEncoded := []byte{ + 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 + 0x02, // Varint for number of block locator hashes + 0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63, + 0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65, + 0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b, + 0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash + 0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60, + 0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9, + 0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40, + 0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash + 0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39, + 0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2, + 0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa, + 0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop + } + + // Message that forces an error by having more than the max allowed + // block locator hashes. + maxGetBlocks := wire.NewMsgGetBlocks(hashStop) + for i := 0; i < wire.MaxBlockLocatorsPerMsg; i++ { + maxGetBlocks.AddBlockLocatorHash(&mainNetGenesisHash) + } + maxGetBlocks.BlockLocatorHashes = append(maxGetBlocks.BlockLocatorHashes, + &mainNetGenesisHash) + maxGetBlocksEncoded := []byte{ + 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 + 0xfd, 0xf5, 0x01, // Varint for number of block loc hashes (501) + } + + tests := []struct { + in *wire.MsgGetBlocks // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error in protocol version. + {baseGetBlocks, baseGetBlocksEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in block locator hash count. + {baseGetBlocks, baseGetBlocksEncoded, pver, 4, io.ErrShortWrite, io.EOF}, + // Force error in block locator hashes. + {baseGetBlocks, baseGetBlocksEncoded, pver, 5, io.ErrShortWrite, io.EOF}, + // Force error in stop hash. + {baseGetBlocks, baseGetBlocksEncoded, pver, 69, io.ErrShortWrite, io.EOF}, + // Force error with greater than max block locator hashes. + {maxGetBlocks, maxGetBlocksEncoded, pver, 7, wireErr, wireErr}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgGetBlocks + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msggetdata.go b/vendor/github.com/btcsuite/btcd/wire/msggetdata.go new file mode 100644 index 0000000000000000000000000000000000000000..2cc58b03d844a2ccd772b4ae999aef7c6e4cb1ff --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msggetdata.go @@ -0,0 +1,130 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// MsgGetData implements the Message interface and represents a bitcoin +// getdata message. It is used to request data such as blocks and transactions +// from another peer. It should be used in response to the inv (MsgInv) message +// to request the actual data referenced by each inventory vector the receiving +// peer doesn't already have. Each message is limited to a maximum number of +// inventory vectors, which is currently 50,000. As a result, multiple messages +// must be used to request larger amounts of data. +// +// Use the AddInvVect function to build up the list of inventory vectors when +// sending a getdata message to another peer. +type MsgGetData struct { + InvList []*InvVect +} + +// AddInvVect adds an inventory vector to the message. +func (msg *MsgGetData) AddInvVect(iv *InvVect) error { + if len(msg.InvList)+1 > MaxInvPerMsg { + str := fmt.Sprintf("too many invvect in message [max %v]", + MaxInvPerMsg) + return messageError("MsgGetData.AddInvVect", str) + } + + msg.InvList = append(msg.InvList, iv) + return nil +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgGetData) BtcDecode(r io.Reader, pver uint32) error { + count, err := ReadVarInt(r, pver) + if err != nil { + return err + } + + // Limit to max inventory vectors per message. + if count > MaxInvPerMsg { + str := fmt.Sprintf("too many invvect in message [%v]", count) + return messageError("MsgGetData.BtcDecode", str) + } + + msg.InvList = make([]*InvVect, 0, count) + for i := uint64(0); i < count; i++ { + iv := InvVect{} + err := readInvVect(r, pver, &iv) + if err != nil { + return err + } + msg.AddInvVect(&iv) + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgGetData) BtcEncode(w io.Writer, pver uint32) error { + // Limit to max inventory vectors per message. + count := len(msg.InvList) + if count > MaxInvPerMsg { + str := fmt.Sprintf("too many invvect in message [%v]", count) + return messageError("MsgGetData.BtcEncode", str) + } + + err := WriteVarInt(w, pver, uint64(count)) + if err != nil { + return err + } + + for _, iv := range msg.InvList { + err := writeInvVect(w, pver, iv) + if err != nil { + return err + } + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgGetData) Command() string { + return CmdGetData +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgGetData) MaxPayloadLength(pver uint32) uint32 { + // Num inventory vectors (varInt) + max allowed inventory vectors. + return MaxVarIntPayload + (MaxInvPerMsg * maxInvVectPayload) +} + +// NewMsgGetData returns a new bitcoin getdata message that conforms to the +// Message interface. See MsgGetData for details. +func NewMsgGetData() *MsgGetData { + return &MsgGetData{ + InvList: make([]*InvVect, 0, defaultInvListAlloc), + } +} + +// NewMsgGetDataSizeHint returns a new bitcoin getdata message that conforms to +// the Message interface. See MsgGetData for details. This function differs +// from NewMsgGetData in that it allows a default allocation size for the +// backing array which houses the inventory vector list. This allows callers +// who know in advance how large the inventory list will grow to avoid the +// overhead of growing the internal backing array several times when appending +// large amounts of inventory vectors with AddInvVect. Note that the specified +// hint is just that - a hint that is used for the default allocation size. +// Adding more (or less) inventory vectors will still work properly. The size +// hint is limited to MaxInvPerMsg. +func NewMsgGetDataSizeHint(sizeHint uint) *MsgGetData { + // Limit the specified hint to the maximum allow per message. + if sizeHint > MaxInvPerMsg { + sizeHint = MaxInvPerMsg + } + + return &MsgGetData{ + InvList: make([]*InvVect, 0, sizeHint), + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msggetdata_test.go b/vendor/github.com/btcsuite/btcd/wire/msggetdata_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d7b2909729ad29220009d899443595b7b0d309dc --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msggetdata_test.go @@ -0,0 +1,331 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestGetData tests the MsgGetData API. +func TestGetData(t *testing.T) { + pver := wire.ProtocolVersion + + // Ensure the command is expected value. + wantCmd := "getdata" + msg := wire.NewMsgGetData() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgGetData: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + // Num inventory vectors (varInt) + max allowed inventory vectors. + wantPayload := uint32(1800009) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Ensure inventory vectors are added properly. + hash := wire.ShaHash{} + iv := wire.NewInvVect(wire.InvTypeBlock, &hash) + err := msg.AddInvVect(iv) + if err != nil { + t.Errorf("AddInvVect: %v", err) + } + if msg.InvList[0] != iv { + t.Errorf("AddInvVect: wrong invvect added - got %v, want %v", + spew.Sprint(msg.InvList[0]), spew.Sprint(iv)) + } + + // Ensure adding more than the max allowed inventory vectors per + // message returns an error. + for i := 0; i < wire.MaxInvPerMsg; i++ { + err = msg.AddInvVect(iv) + } + if err == nil { + t.Errorf("AddInvVect: expected error on too many inventory " + + "vectors not received") + } + + // Ensure creating the message with a size hint larger than the max + // works as expected. + msg = wire.NewMsgGetDataSizeHint(wire.MaxInvPerMsg + 1) + wantCap := wire.MaxInvPerMsg + if cap(msg.InvList) != wantCap { + t.Errorf("NewMsgGetDataSizeHint: wrong cap for size hint - "+ + "got %v, want %v", cap(msg.InvList), wantCap) + } + + return +} + +// TestGetDataWire tests the MsgGetData wire encode and decode for various +// numbers of inventory vectors and protocol versions. +func TestGetDataWire(t *testing.T) { + // Block 203707 hash. + hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc" + blockHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Transation 1 of Block 203707 hash. + hashStr = "d28a3dc7392bf00a9855ee93dd9a81eff82a2c4fe57fbd42cfe71b487accfaf0" + txHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + iv := wire.NewInvVect(wire.InvTypeBlock, blockHash) + iv2 := wire.NewInvVect(wire.InvTypeTx, txHash) + + // Empty MsgGetData message. + NoInv := wire.NewMsgGetData() + NoInvEncoded := []byte{ + 0x00, // Varint for number of inventory vectors + } + + // MsgGetData message with multiple inventory vectors. + MultiInv := wire.NewMsgGetData() + MultiInv.AddInvVect(iv) + MultiInv.AddInvVect(iv2) + MultiInvEncoded := []byte{ + 0x02, // Varint for number of inv vectors + 0x02, 0x00, 0x00, 0x00, // InvTypeBlock + 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, + 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, + 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, + 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash + 0x01, 0x00, 0x00, 0x00, // InvTypeTx + 0xf0, 0xfa, 0xcc, 0x7a, 0x48, 0x1b, 0xe7, 0xcf, + 0x42, 0xbd, 0x7f, 0xe5, 0x4f, 0x2c, 0x2a, 0xf8, + 0xef, 0x81, 0x9a, 0xdd, 0x93, 0xee, 0x55, 0x98, + 0x0a, 0xf0, 0x2b, 0x39, 0xc7, 0x3d, 0x8a, 0xd2, // Tx 1 of block 203707 hash + } + + tests := []struct { + in *wire.MsgGetData // Message to encode + out *wire.MsgGetData // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version with no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.ProtocolVersion, + }, + + // Latest protocol version with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0035Version with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.BIP0031Version, + }, + + // Protocol version BIP0031Version with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version NetAddressTimeVersion with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.MultipleAddressVersion, + }, + + // Protocol version MultipleAddressVersion with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgGetData + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestGetDataWireErrors performs negative tests against wire encode and decode +// of MsgGetData to confirm error paths work correctly. +func TestGetDataWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + wireErr := &wire.MessageError{} + + // Block 203707 hash. + hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc" + blockHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + iv := wire.NewInvVect(wire.InvTypeBlock, blockHash) + + // Base message used to induce errors. + baseGetData := wire.NewMsgGetData() + baseGetData.AddInvVect(iv) + baseGetDataEncoded := []byte{ + 0x02, // Varint for number of inv vectors + 0x02, 0x00, 0x00, 0x00, // InvTypeBlock + 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, + 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, + 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, + 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash + } + + // Message that forces an error by having more than the max allowed inv + // vectors. + maxGetData := wire.NewMsgGetData() + for i := 0; i < wire.MaxInvPerMsg; i++ { + maxGetData.AddInvVect(iv) + } + maxGetData.InvList = append(maxGetData.InvList, iv) + maxGetDataEncoded := []byte{ + 0xfd, 0x51, 0xc3, // Varint for number of inv vectors (50001) + } + + tests := []struct { + in *wire.MsgGetData // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Latest protocol version with intentional read/write errors. + // Force error in inventory vector count + {baseGetData, baseGetDataEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in inventory list. + {baseGetData, baseGetDataEncoded, pver, 1, io.ErrShortWrite, io.EOF}, + // Force error with greater than max inventory vectors. + {maxGetData, maxGetDataEncoded, pver, 3, wireErr, wireErr}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgGetData + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msggetheaders.go b/vendor/github.com/btcsuite/btcd/wire/msggetheaders.go new file mode 100644 index 0000000000000000000000000000000000000000..6d56560be198a3bcc3a5db06819adda01284dc13 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msggetheaders.go @@ -0,0 +1,139 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// MsgGetHeaders implements the Message interface and represents a bitcoin +// getheaders message. It is used to request a list of block headers for +// blocks starting after the last known hash in the slice of block locator +// hashes. The list is returned via a headers message (MsgHeaders) and is +// limited by a specific hash to stop at or the maximum number of block headers +// per message, which is currently 2000. +// +// Set the HashStop field to the hash at which to stop and use +// AddBlockLocatorHash to build up the list of block locator hashes. +// +// The algorithm for building the block locator hashes should be to add the +// hashes in reverse order until you reach the genesis block. In order to keep +// the list of locator hashes to a resonable number of entries, first add the +// most recent 10 block hashes, then double the step each loop iteration to +// exponentially decrease the number of hashes the further away from head and +// closer to the genesis block you get. +type MsgGetHeaders struct { + ProtocolVersion uint32 + BlockLocatorHashes []*ShaHash + HashStop ShaHash +} + +// AddBlockLocatorHash adds a new block locator hash to the message. +func (msg *MsgGetHeaders) AddBlockLocatorHash(hash *ShaHash) error { + if len(msg.BlockLocatorHashes)+1 > MaxBlockLocatorsPerMsg { + str := fmt.Sprintf("too many block locator hashes for message [max %v]", + MaxBlockLocatorsPerMsg) + return messageError("MsgGetHeaders.AddBlockLocatorHash", str) + } + + msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash) + return nil +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgGetHeaders) BtcDecode(r io.Reader, pver uint32) error { + err := readElement(r, &msg.ProtocolVersion) + if err != nil { + return err + } + + // Read num block locator hashes and limit to max. + count, err := ReadVarInt(r, pver) + if err != nil { + return err + } + if count > MaxBlockLocatorsPerMsg { + str := fmt.Sprintf("too many block locator hashes for message "+ + "[count %v, max %v]", count, MaxBlockLocatorsPerMsg) + return messageError("MsgGetHeaders.BtcDecode", str) + } + + msg.BlockLocatorHashes = make([]*ShaHash, 0, count) + for i := uint64(0); i < count; i++ { + sha := ShaHash{} + err := readElement(r, &sha) + if err != nil { + return err + } + msg.AddBlockLocatorHash(&sha) + } + + err = readElement(r, &msg.HashStop) + if err != nil { + return err + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgGetHeaders) BtcEncode(w io.Writer, pver uint32) error { + // Limit to max block locator hashes per message. + count := len(msg.BlockLocatorHashes) + if count > MaxBlockLocatorsPerMsg { + str := fmt.Sprintf("too many block locator hashes for message "+ + "[count %v, max %v]", count, MaxBlockLocatorsPerMsg) + return messageError("MsgGetHeaders.BtcEncode", str) + } + + err := writeElement(w, msg.ProtocolVersion) + if err != nil { + return err + } + + err = WriteVarInt(w, pver, uint64(count)) + if err != nil { + return err + } + + for _, sha := range msg.BlockLocatorHashes { + err := writeElement(w, sha) + if err != nil { + return err + } + } + + err = writeElement(w, &msg.HashStop) + if err != nil { + return err + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgGetHeaders) Command() string { + return CmdGetHeaders +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgGetHeaders) MaxPayloadLength(pver uint32) uint32 { + // Version 4 bytes + num block locator hashes (varInt) + max allowed block + // locators + hash stop. + return 4 + MaxVarIntPayload + (MaxBlockLocatorsPerMsg * HashSize) + HashSize +} + +// NewMsgGetHeaders returns a new bitcoin getheaders message that conforms to +// the Message interface. See MsgGetHeaders for details. +func NewMsgGetHeaders() *MsgGetHeaders { + return &MsgGetHeaders{ + BlockLocatorHashes: make([]*ShaHash, 0, MaxBlockLocatorsPerMsg), + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msggetheaders_test.go b/vendor/github.com/btcsuite/btcd/wire/msggetheaders_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a87fa4ed92a93cb97d644fd015b8f19092fa5c16 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msggetheaders_test.go @@ -0,0 +1,381 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestGetHeaders tests the MsgGetHeader API. +func TestGetHeaders(t *testing.T) { + pver := wire.ProtocolVersion + + // Block 99500 hash. + hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0" + locatorHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Ensure the command is expected value. + wantCmd := "getheaders" + msg := wire.NewMsgGetHeaders() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgGetHeaders: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + // Protocol version 4 bytes + num hashes (varInt) + max block locator + // hashes + hash stop. + wantPayload := uint32(16045) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Ensure block locator hashes are added properly. + err = msg.AddBlockLocatorHash(locatorHash) + if err != nil { + t.Errorf("AddBlockLocatorHash: %v", err) + } + if msg.BlockLocatorHashes[0] != locatorHash { + t.Errorf("AddBlockLocatorHash: wrong block locator added - "+ + "got %v, want %v", + spew.Sprint(msg.BlockLocatorHashes[0]), + spew.Sprint(locatorHash)) + } + + // Ensure adding more than the max allowed block locator hashes per + // message returns an error. + for i := 0; i < wire.MaxBlockLocatorsPerMsg; i++ { + err = msg.AddBlockLocatorHash(locatorHash) + } + if err == nil { + t.Errorf("AddBlockLocatorHash: expected error on too many " + + "block locator hashes not received") + } + + return +} + +// TestGetHeadersWire tests the MsgGetHeaders wire encode and decode for various +// numbers of block locator hashes and protocol versions. +func TestGetHeadersWire(t *testing.T) { + // Set protocol inside getheaders message. Use protocol version 60002 + // specifically here instead of the latest because the test data is + // using bytes encoded with that protocol version. + pver := uint32(60002) + + // Block 99499 hash. + hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535" + hashLocator, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Block 99500 hash. + hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0" + hashLocator2, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Block 100000 hash. + hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" + hashStop, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // MsgGetHeaders message with no block locators or stop hash. + noLocators := wire.NewMsgGetHeaders() + noLocators.ProtocolVersion = pver + noLocatorsEncoded := []byte{ + 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 + 0x00, // Varint for number of block locator hashes + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop + } + + // MsgGetHeaders message with multiple block locators and a stop hash. + multiLocators := wire.NewMsgGetHeaders() + multiLocators.ProtocolVersion = pver + multiLocators.HashStop = *hashStop + multiLocators.AddBlockLocatorHash(hashLocator2) + multiLocators.AddBlockLocatorHash(hashLocator) + multiLocatorsEncoded := []byte{ + 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 + 0x02, // Varint for number of block locator hashes + 0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63, + 0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65, + 0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b, + 0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash + 0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60, + 0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9, + 0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40, + 0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash + 0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39, + 0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2, + 0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa, + 0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop + } + + tests := []struct { + in *wire.MsgGetHeaders // Message to encode + out *wire.MsgGetHeaders // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version with no block locators. + { + noLocators, + noLocators, + noLocatorsEncoded, + wire.ProtocolVersion, + }, + + // Latest protocol version with multiple block locators. + { + multiLocators, + multiLocators, + multiLocatorsEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version with no block locators. + { + noLocators, + noLocators, + noLocatorsEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0035Version with multiple block locators. + { + multiLocators, + multiLocators, + multiLocatorsEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version with no block locators. + { + noLocators, + noLocators, + noLocatorsEncoded, + wire.BIP0031Version, + }, + + // Protocol version BIP0031Versionwith multiple block locators. + { + multiLocators, + multiLocators, + multiLocatorsEncoded, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion with no block locators. + { + noLocators, + noLocators, + noLocatorsEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version NetAddressTimeVersion multiple block locators. + { + multiLocators, + multiLocators, + multiLocatorsEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion with no block locators. + { + noLocators, + noLocators, + noLocatorsEncoded, + wire.MultipleAddressVersion, + }, + + // Protocol version MultipleAddressVersion multiple block locators. + { + multiLocators, + multiLocators, + multiLocatorsEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgGetHeaders + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(&msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestGetHeadersWireErrors performs negative tests against wire encode and +// decode of MsgGetHeaders to confirm error paths work correctly. +func TestGetHeadersWireErrors(t *testing.T) { + // Set protocol inside getheaders message. Use protocol version 60002 + // specifically here instead of the latest because the test data is + // using bytes encoded with that protocol version. + pver := uint32(60002) + wireErr := &wire.MessageError{} + + // Block 99499 hash. + hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535" + hashLocator, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Block 99500 hash. + hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0" + hashLocator2, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Block 100000 hash. + hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" + hashStop, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // MsgGetHeaders message with multiple block locators and a stop hash. + baseGetHeaders := wire.NewMsgGetHeaders() + baseGetHeaders.ProtocolVersion = pver + baseGetHeaders.HashStop = *hashStop + baseGetHeaders.AddBlockLocatorHash(hashLocator2) + baseGetHeaders.AddBlockLocatorHash(hashLocator) + baseGetHeadersEncoded := []byte{ + 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 + 0x02, // Varint for number of block locator hashes + 0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63, + 0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65, + 0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b, + 0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash + 0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60, + 0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9, + 0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40, + 0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash + 0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39, + 0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2, + 0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa, + 0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop + } + + // Message that forces an error by having more than the max allowed + // block locator hashes. + maxGetHeaders := wire.NewMsgGetHeaders() + for i := 0; i < wire.MaxBlockLocatorsPerMsg; i++ { + maxGetHeaders.AddBlockLocatorHash(&mainNetGenesisHash) + } + maxGetHeaders.BlockLocatorHashes = append(maxGetHeaders.BlockLocatorHashes, + &mainNetGenesisHash) + maxGetHeadersEncoded := []byte{ + 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 + 0xfd, 0xf5, 0x01, // Varint for number of block loc hashes (501) + } + + tests := []struct { + in *wire.MsgGetHeaders // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error in protocol version. + {baseGetHeaders, baseGetHeadersEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in block locator hash count. + {baseGetHeaders, baseGetHeadersEncoded, pver, 4, io.ErrShortWrite, io.EOF}, + // Force error in block locator hashes. + {baseGetHeaders, baseGetHeadersEncoded, pver, 5, io.ErrShortWrite, io.EOF}, + // Force error in stop hash. + {baseGetHeaders, baseGetHeadersEncoded, pver, 69, io.ErrShortWrite, io.EOF}, + // Force error with greater than max block locator hashes. + {maxGetHeaders, maxGetHeadersEncoded, pver, 7, wireErr, wireErr}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgGetHeaders + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgheaders.go b/vendor/github.com/btcsuite/btcd/wire/msgheaders.go new file mode 100644 index 0000000000000000000000000000000000000000..62b6076870a8f900746233d57d3c51e4051b8bde --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgheaders.go @@ -0,0 +1,133 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// MaxBlockHeadersPerMsg is the maximum number of block headers that can be in +// a single bitcoin headers message. +const MaxBlockHeadersPerMsg = 2000 + +// MsgHeaders implements the Message interface and represents a bitcoin headers +// message. It is used to deliver block header information in response +// to a getheaders message (MsgGetHeaders). The maximum number of block headers +// per message is currently 2000. See MsgGetHeaders for details on requesting +// the headers. +type MsgHeaders struct { + Headers []*BlockHeader +} + +// AddBlockHeader adds a new block header to the message. +func (msg *MsgHeaders) AddBlockHeader(bh *BlockHeader) error { + if len(msg.Headers)+1 > MaxBlockHeadersPerMsg { + str := fmt.Sprintf("too many block headers in message [max %v]", + MaxBlockHeadersPerMsg) + return messageError("MsgHeaders.AddBlockHeader", str) + } + + msg.Headers = append(msg.Headers, bh) + return nil +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgHeaders) BtcDecode(r io.Reader, pver uint32) error { + count, err := ReadVarInt(r, pver) + if err != nil { + return err + } + + // Limit to max block headers per message. + if count > MaxBlockHeadersPerMsg { + str := fmt.Sprintf("too many block headers for message "+ + "[count %v, max %v]", count, MaxBlockHeadersPerMsg) + return messageError("MsgHeaders.BtcDecode", str) + } + + msg.Headers = make([]*BlockHeader, 0, count) + for i := uint64(0); i < count; i++ { + bh := BlockHeader{} + err := readBlockHeader(r, pver, &bh) + if err != nil { + return err + } + + txCount, err := ReadVarInt(r, pver) + if err != nil { + return err + } + + // Ensure the transaction count is zero for headers. + if txCount > 0 { + str := fmt.Sprintf("block headers may not contain "+ + "transactions [count %v]", txCount) + return messageError("MsgHeaders.BtcDecode", str) + } + msg.AddBlockHeader(&bh) + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgHeaders) BtcEncode(w io.Writer, pver uint32) error { + // Limit to max block headers per message. + count := len(msg.Headers) + if count > MaxBlockHeadersPerMsg { + str := fmt.Sprintf("too many block headers for message "+ + "[count %v, max %v]", count, MaxBlockHeadersPerMsg) + return messageError("MsgHeaders.BtcEncode", str) + } + + err := WriteVarInt(w, pver, uint64(count)) + if err != nil { + return err + } + + for _, bh := range msg.Headers { + err := writeBlockHeader(w, pver, bh) + if err != nil { + return err + } + + // The wire protocol encoding always includes a 0 for the number + // of transactions on header messages. This is really just an + // artifact of the way the original implementation serializes + // block headers, but it is required. + err = WriteVarInt(w, pver, 0) + if err != nil { + return err + } + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgHeaders) Command() string { + return CmdHeaders +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgHeaders) MaxPayloadLength(pver uint32) uint32 { + // Num headers (varInt) + max allowed headers (header length + 1 byte + // for the number of transactions which is always 0). + return MaxVarIntPayload + ((MaxBlockHeaderPayload + 1) * + MaxBlockHeadersPerMsg) +} + +// NewMsgHeaders returns a new bitcoin headers message that conforms to the +// Message interface. See MsgHeaders for details. +func NewMsgHeaders() *MsgHeaders { + return &MsgHeaders{ + Headers: make([]*BlockHeader, 0, MaxBlockHeadersPerMsg), + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgheaders_test.go b/vendor/github.com/btcsuite/btcd/wire/msgheaders_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ee80ac42a9bf2a7e46a1cff0ffc86ce1156fdcc7 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgheaders_test.go @@ -0,0 +1,350 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestHeaders tests the MsgHeaders API. +func TestHeaders(t *testing.T) { + pver := uint32(60002) + + // Ensure the command is expected value. + wantCmd := "headers" + msg := wire.NewMsgHeaders() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgHeaders: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + // Num headers (varInt) + max allowed headers (header length + 1 byte + // for the number of transactions which is always 0). + wantPayload := uint32(162009) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Ensure headers are added properly. + bh := &blockOne.Header + msg.AddBlockHeader(bh) + if !reflect.DeepEqual(msg.Headers[0], bh) { + t.Errorf("AddHeader: wrong header - got %v, want %v", + spew.Sdump(msg.Headers), + spew.Sdump(bh)) + } + + // Ensure adding more than the max allowed headers per message returns + // error. + var err error + for i := 0; i < wire.MaxBlockHeadersPerMsg+1; i++ { + err = msg.AddBlockHeader(bh) + } + if reflect.TypeOf(err) != reflect.TypeOf(&wire.MessageError{}) { + t.Errorf("AddBlockHeader: expected error on too many headers " + + "not received") + } + + return +} + +// TestHeadersWire tests the MsgHeaders wire encode and decode for various +// numbers of headers and protocol versions. +func TestHeadersWire(t *testing.T) { + hash := mainNetGenesisHash + merkleHash := blockOne.Header.MerkleRoot + bits := uint32(0x1d00ffff) + nonce := uint32(0x9962e301) + bh := wire.NewBlockHeader(&hash, &merkleHash, bits, nonce) + bh.Version = blockOne.Header.Version + bh.Timestamp = blockOne.Header.Timestamp + + // Empty headers message. + noHeaders := wire.NewMsgHeaders() + noHeadersEncoded := []byte{ + 0x00, // Varint for number of headers + } + + // Headers message with one header. + oneHeader := wire.NewMsgHeaders() + oneHeader.AddBlockHeader(bh) + oneHeaderEncoded := []byte{ + 0x01, // VarInt for number of headers. + 0x01, 0x00, 0x00, 0x00, // Version 1 + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot + 0x61, 0xbc, 0x66, 0x49, // Timestamp + 0xff, 0xff, 0x00, 0x1d, // Bits + 0x01, 0xe3, 0x62, 0x99, // Nonce + 0x00, // TxnCount (0 for headers message) + } + + tests := []struct { + in *wire.MsgHeaders // Message to encode + out *wire.MsgHeaders // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version with no headers. + { + noHeaders, + noHeaders, + noHeadersEncoded, + wire.ProtocolVersion, + }, + + // Latest protocol version with one header. + { + oneHeader, + oneHeader, + oneHeaderEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version with no headers. + { + noHeaders, + noHeaders, + noHeadersEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0035Version with one header. + { + oneHeader, + oneHeader, + oneHeaderEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version with no headers. + { + noHeaders, + noHeaders, + noHeadersEncoded, + wire.BIP0031Version, + }, + + // Protocol version BIP0031Version with one header. + { + oneHeader, + oneHeader, + oneHeaderEncoded, + wire.BIP0031Version, + }, + // Protocol version NetAddressTimeVersion with no headers. + { + noHeaders, + noHeaders, + noHeadersEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version NetAddressTimeVersion with one header. + { + oneHeader, + oneHeader, + oneHeaderEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion with no headers. + { + noHeaders, + noHeaders, + noHeadersEncoded, + wire.MultipleAddressVersion, + }, + + // Protocol version MultipleAddressVersion with one header. + { + oneHeader, + oneHeader, + oneHeaderEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgHeaders + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(&msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestHeadersWireErrors performs negative tests against wire encode and decode +// of MsgHeaders to confirm error paths work correctly. +func TestHeadersWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + wireErr := &wire.MessageError{} + + hash := mainNetGenesisHash + merkleHash := blockOne.Header.MerkleRoot + bits := uint32(0x1d00ffff) + nonce := uint32(0x9962e301) + bh := wire.NewBlockHeader(&hash, &merkleHash, bits, nonce) + bh.Version = blockOne.Header.Version + bh.Timestamp = blockOne.Header.Timestamp + + // Headers message with one header. + oneHeader := wire.NewMsgHeaders() + oneHeader.AddBlockHeader(bh) + oneHeaderEncoded := []byte{ + 0x01, // VarInt for number of headers. + 0x01, 0x00, 0x00, 0x00, // Version 1 + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot + 0x61, 0xbc, 0x66, 0x49, // Timestamp + 0xff, 0xff, 0x00, 0x1d, // Bits + 0x01, 0xe3, 0x62, 0x99, // Nonce + 0x00, // TxnCount (0 for headers message) + } + + // Message that forces an error by having more than the max allowed + // headers. + maxHeaders := wire.NewMsgHeaders() + for i := 0; i < wire.MaxBlockHeadersPerMsg; i++ { + maxHeaders.AddBlockHeader(bh) + } + maxHeaders.Headers = append(maxHeaders.Headers, bh) + maxHeadersEncoded := []byte{ + 0xfd, 0xd1, 0x07, // Varint for number of addresses (2001)7D1 + } + + // Intentionally invalid block header that has a transaction count used + // to force errors. + bhTrans := wire.NewBlockHeader(&hash, &merkleHash, bits, nonce) + bhTrans.Version = blockOne.Header.Version + bhTrans.Timestamp = blockOne.Header.Timestamp + + transHeader := wire.NewMsgHeaders() + transHeader.AddBlockHeader(bhTrans) + transHeaderEncoded := []byte{ + 0x01, // VarInt for number of headers. + 0x01, 0x00, 0x00, 0x00, // Version 1 + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot + 0x61, 0xbc, 0x66, 0x49, // Timestamp + 0xff, 0xff, 0x00, 0x1d, // Bits + 0x01, 0xe3, 0x62, 0x99, // Nonce + 0x01, // TxnCount (should be 0 for headers message, but 1 to force error) + } + + tests := []struct { + in *wire.MsgHeaders // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Latest protocol version with intentional read/write errors. + // Force error in header count. + {oneHeader, oneHeaderEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in block header. + {oneHeader, oneHeaderEncoded, pver, 5, io.ErrShortWrite, io.EOF}, + // Force error with greater than max headers. + {maxHeaders, maxHeadersEncoded, pver, 3, wireErr, wireErr}, + // Force error with number of transactions. + {transHeader, transHeaderEncoded, pver, 81, io.ErrShortWrite, io.EOF}, + // Force error with included transactions. + {transHeader, transHeaderEncoded, pver, len(transHeaderEncoded), nil, wireErr}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgHeaders + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msginv.go b/vendor/github.com/btcsuite/btcd/wire/msginv.go new file mode 100644 index 0000000000000000000000000000000000000000..905531ae595258f6e10e2d297ae60f9a6050556e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msginv.go @@ -0,0 +1,138 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// defaultInvListAlloc is the default size used for the backing array for an +// inventory list. The array will dynamically grow as needed, but this +// figure is intended to provide enough space for the max number of inventory +// vectors in a *typical* inventory message without needing to grow the backing +// array multiple times. Technically, the list can grow to MaxInvPerMsg, but +// rather than using that large figure, this figure more accurately reflects the +// typical case. +const defaultInvListAlloc = 1000 + +// MsgInv implements the Message interface and represents a bitcoin inv message. +// It is used to advertise a peer's known data such as blocks and transactions +// through inventory vectors. It may be sent unsolicited to inform other peers +// of the data or in response to a getblocks message (MsgGetBlocks). Each +// message is limited to a maximum number of inventory vectors, which is +// currently 50,000. +// +// Use the AddInvVect function to build up the list of inventory vectors when +// sending an inv message to another peer. +type MsgInv struct { + InvList []*InvVect +} + +// AddInvVect adds an inventory vector to the message. +func (msg *MsgInv) AddInvVect(iv *InvVect) error { + if len(msg.InvList)+1 > MaxInvPerMsg { + str := fmt.Sprintf("too many invvect in message [max %v]", + MaxInvPerMsg) + return messageError("MsgInv.AddInvVect", str) + } + + msg.InvList = append(msg.InvList, iv) + return nil +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgInv) BtcDecode(r io.Reader, pver uint32) error { + count, err := ReadVarInt(r, pver) + if err != nil { + return err + } + + // Limit to max inventory vectors per message. + if count > MaxInvPerMsg { + str := fmt.Sprintf("too many invvect in message [%v]", count) + return messageError("MsgInv.BtcDecode", str) + } + + msg.InvList = make([]*InvVect, 0, count) + for i := uint64(0); i < count; i++ { + iv := InvVect{} + err := readInvVect(r, pver, &iv) + if err != nil { + return err + } + msg.AddInvVect(&iv) + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgInv) BtcEncode(w io.Writer, pver uint32) error { + // Limit to max inventory vectors per message. + count := len(msg.InvList) + if count > MaxInvPerMsg { + str := fmt.Sprintf("too many invvect in message [%v]", count) + return messageError("MsgInv.BtcEncode", str) + } + + err := WriteVarInt(w, pver, uint64(count)) + if err != nil { + return err + } + + for _, iv := range msg.InvList { + err := writeInvVect(w, pver, iv) + if err != nil { + return err + } + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgInv) Command() string { + return CmdInv +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgInv) MaxPayloadLength(pver uint32) uint32 { + // Num inventory vectors (varInt) + max allowed inventory vectors. + return MaxVarIntPayload + (MaxInvPerMsg * maxInvVectPayload) +} + +// NewMsgInv returns a new bitcoin inv message that conforms to the Message +// interface. See MsgInv for details. +func NewMsgInv() *MsgInv { + return &MsgInv{ + InvList: make([]*InvVect, 0, defaultInvListAlloc), + } +} + +// NewMsgInvSizeHint returns a new bitcoin inv message that conforms to the +// Message interface. See MsgInv for details. This function differs from +// NewMsgInv in that it allows a default allocation size for the backing array +// which houses the inventory vector list. This allows callers who know in +// advance how large the inventory list will grow to avoid the overhead of +// growing the internal backing array several times when appending large amounts +// of inventory vectors with AddInvVect. Note that the specified hint is just +// that - a hint that is used for the default allocation size. Adding more +// (or less) inventory vectors will still work properly. The size hint is +// limited to MaxInvPerMsg. +func NewMsgInvSizeHint(sizeHint uint) *MsgInv { + // Limit the specified hint to the maximum allow per message. + if sizeHint > MaxInvPerMsg { + sizeHint = MaxInvPerMsg + } + + return &MsgInv{ + InvList: make([]*InvVect, 0, sizeHint), + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msginv_test.go b/vendor/github.com/btcsuite/btcd/wire/msginv_test.go new file mode 100644 index 0000000000000000000000000000000000000000..de50933af1a997e60ef2fc69116c0988a6654059 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msginv_test.go @@ -0,0 +1,332 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestInv tests the MsgInv API. +func TestInv(t *testing.T) { + pver := wire.ProtocolVersion + + // Ensure the command is expected value. + wantCmd := "inv" + msg := wire.NewMsgInv() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgInv: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + // Num inventory vectors (varInt) + max allowed inventory vectors. + wantPayload := uint32(1800009) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Ensure inventory vectors are added properly. + hash := wire.ShaHash{} + iv := wire.NewInvVect(wire.InvTypeBlock, &hash) + err := msg.AddInvVect(iv) + if err != nil { + t.Errorf("AddInvVect: %v", err) + } + if msg.InvList[0] != iv { + t.Errorf("AddInvVect: wrong invvect added - got %v, want %v", + spew.Sprint(msg.InvList[0]), spew.Sprint(iv)) + } + + // Ensure adding more than the max allowed inventory vectors per + // message returns an error. + for i := 0; i < wire.MaxInvPerMsg; i++ { + err = msg.AddInvVect(iv) + } + if err == nil { + t.Errorf("AddInvVect: expected error on too many inventory " + + "vectors not received") + } + + // Ensure creating the message with a size hint larger than the max + // works as expected. + msg = wire.NewMsgInvSizeHint(wire.MaxInvPerMsg + 1) + wantCap := wire.MaxInvPerMsg + if cap(msg.InvList) != wantCap { + t.Errorf("NewMsgInvSizeHint: wrong cap for size hint - "+ + "got %v, want %v", cap(msg.InvList), wantCap) + } + + return +} + +// TestInvWire tests the MsgInv wire encode and decode for various numbers +// of inventory vectors and protocol versions. +func TestInvWire(t *testing.T) { + // Block 203707 hash. + hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc" + blockHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Transation 1 of Block 203707 hash. + hashStr = "d28a3dc7392bf00a9855ee93dd9a81eff82a2c4fe57fbd42cfe71b487accfaf0" + txHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + iv := wire.NewInvVect(wire.InvTypeBlock, blockHash) + iv2 := wire.NewInvVect(wire.InvTypeTx, txHash) + + // Empty inv message. + NoInv := wire.NewMsgInv() + NoInvEncoded := []byte{ + 0x00, // Varint for number of inventory vectors + } + + // Inv message with multiple inventory vectors. + MultiInv := wire.NewMsgInv() + MultiInv.AddInvVect(iv) + MultiInv.AddInvVect(iv2) + MultiInvEncoded := []byte{ + 0x02, // Varint for number of inv vectors + 0x02, 0x00, 0x00, 0x00, // InvTypeBlock + 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, + 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, + 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, + 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash + 0x01, 0x00, 0x00, 0x00, // InvTypeTx + 0xf0, 0xfa, 0xcc, 0x7a, 0x48, 0x1b, 0xe7, 0xcf, + 0x42, 0xbd, 0x7f, 0xe5, 0x4f, 0x2c, 0x2a, 0xf8, + 0xef, 0x81, 0x9a, 0xdd, 0x93, 0xee, 0x55, 0x98, + 0x0a, 0xf0, 0x2b, 0x39, 0xc7, 0x3d, 0x8a, 0xd2, // Tx 1 of block 203707 hash + } + + tests := []struct { + in *wire.MsgInv // Message to encode + out *wire.MsgInv // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version with no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.ProtocolVersion, + }, + + // Latest protocol version with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0035Version with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.BIP0031Version, + }, + + // Protocol version BIP0031Version with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version NetAddressTimeVersion with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.MultipleAddressVersion, + }, + + // Protocol version MultipleAddressVersion with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgInv + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestInvWireErrors performs negative tests against wire encode and decode +// of MsgInv to confirm error paths work correctly. +func TestInvWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + wireErr := &wire.MessageError{} + + // Block 203707 hash. + hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc" + blockHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + iv := wire.NewInvVect(wire.InvTypeBlock, blockHash) + + // Base inv message used to induce errors. + baseInv := wire.NewMsgInv() + baseInv.AddInvVect(iv) + baseInvEncoded := []byte{ + 0x02, // Varint for number of inv vectors + 0x02, 0x00, 0x00, 0x00, // InvTypeBlock + 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, + 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, + 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, + 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash + } + + // Inv message that forces an error by having more than the max allowed + // inv vectors. + maxInv := wire.NewMsgInv() + for i := 0; i < wire.MaxInvPerMsg; i++ { + maxInv.AddInvVect(iv) + } + maxInv.InvList = append(maxInv.InvList, iv) + maxInvEncoded := []byte{ + 0xfd, 0x51, 0xc3, // Varint for number of inv vectors (50001) + } + + tests := []struct { + in *wire.MsgInv // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Latest protocol version with intentional read/write errors. + // Force error in inventory vector count + {baseInv, baseInvEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in inventory list. + {baseInv, baseInvEncoded, pver, 1, io.ErrShortWrite, io.EOF}, + // Force error with greater than max inventory vectors. + {maxInv, maxInvEncoded, pver, 3, wireErr, wireErr}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgInv + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgmempool.go b/vendor/github.com/btcsuite/btcd/wire/msgmempool.go new file mode 100644 index 0000000000000000000000000000000000000000..f6b08c2e49558505a985a5bc8aa298c887a20bbf --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgmempool.go @@ -0,0 +1,60 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// MsgMemPool implements the Message interface and represents a bitcoin mempool +// message. It is used to request a list of transactions still in the active +// memory pool of a relay. +// +// This message has no payload and was not added until protocol versions +// starting with BIP0035Version. +type MsgMemPool struct{} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgMemPool) BtcDecode(r io.Reader, pver uint32) error { + if pver < BIP0035Version { + str := fmt.Sprintf("mempool message invalid for protocol "+ + "version %d", pver) + return messageError("MsgMemPool.BtcDecode", str) + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgMemPool) BtcEncode(w io.Writer, pver uint32) error { + if pver < BIP0035Version { + str := fmt.Sprintf("mempool message invalid for protocol "+ + "version %d", pver) + return messageError("MsgMemPool.BtcEncode", str) + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgMemPool) Command() string { + return CmdMemPool +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgMemPool) MaxPayloadLength(pver uint32) uint32 { + return 0 +} + +// NewMsgMemPool returns a new bitcoin pong message that conforms to the Message +// interface. See MsgPong for details. +func NewMsgMemPool() *MsgMemPool { + return &MsgMemPool{} +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgmempool_test.go b/vendor/github.com/btcsuite/btcd/wire/msgmempool_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5a08ec79b7902fc4a2bad5078621e973969285bc --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgmempool_test.go @@ -0,0 +1,66 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "testing" + + "github.com/btcsuite/btcd/wire" +) + +func TestMemPool(t *testing.T) { + pver := wire.ProtocolVersion + + // Ensure the command is expected value. + wantCmd := "mempool" + msg := wire.NewMsgMemPool() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgMemPool: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value. + wantPayload := uint32(0) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Test encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, pver) + if err != nil { + t.Errorf("encode of MsgMemPool failed %v err <%v>", msg, err) + } + + // Older protocol versions should fail encode since message didn't + // exist yet. + oldPver := wire.BIP0035Version - 1 + err = msg.BtcEncode(&buf, oldPver) + if err == nil { + s := "encode of MsgMemPool passed for old protocol version %v err <%v>" + t.Errorf(s, msg, err) + } + + // Test decode with latest protocol version. + readmsg := wire.NewMsgMemPool() + err = readmsg.BtcDecode(&buf, pver) + if err != nil { + t.Errorf("decode of MsgMemPool failed [%v] err <%v>", buf, err) + } + + // Older protocol versions should fail decode since message didn't + // exist yet. + err = readmsg.BtcDecode(&buf, oldPver) + if err == nil { + s := "decode of MsgMemPool passed for old protocol version %v err <%v>" + t.Errorf(s, msg, err) + } + + return +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgmerkleblock.go b/vendor/github.com/btcsuite/btcd/wire/msgmerkleblock.go new file mode 100644 index 0000000000000000000000000000000000000000..7dd099d7fe9935ad99475fdfcaecf17d4224d34e --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgmerkleblock.go @@ -0,0 +1,163 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// maxFlagsPerMerkleBlock is the maximum number of flag bytes that could +// possibly fit into a merkle block. Since each transaction is represented by +// a single bit, this is the max number of transactions per block divided by +// 8 bits per byte. Then an extra one to cover partials. +const maxFlagsPerMerkleBlock = maxTxPerBlock / 8 + +// MsgMerkleBlock implements the Message interface and represents a bitcoin +// merkleblock message which is used to reset a Bloom filter. +// +// This message was not added until protocol version BIP0037Version. +type MsgMerkleBlock struct { + Header BlockHeader + Transactions uint32 + Hashes []*ShaHash + Flags []byte +} + +// AddTxHash adds a new transaction hash to the message. +func (msg *MsgMerkleBlock) AddTxHash(hash *ShaHash) error { + if len(msg.Hashes)+1 > maxTxPerBlock { + str := fmt.Sprintf("too many tx hashes for message [max %v]", + maxTxPerBlock) + return messageError("MsgMerkleBlock.AddTxHash", str) + } + + msg.Hashes = append(msg.Hashes, hash) + return nil +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32) error { + if pver < BIP0037Version { + str := fmt.Sprintf("merkleblock message invalid for protocol "+ + "version %d", pver) + return messageError("MsgMerkleBlock.BtcDecode", str) + } + + err := readBlockHeader(r, pver, &msg.Header) + if err != nil { + return err + } + + err = readElement(r, &msg.Transactions) + if err != nil { + return err + } + + // Read num block locator hashes and limit to max. + count, err := ReadVarInt(r, pver) + if err != nil { + return err + } + if count > maxTxPerBlock { + str := fmt.Sprintf("too many transaction hashes for message "+ + "[count %v, max %v]", count, maxTxPerBlock) + return messageError("MsgMerkleBlock.BtcDecode", str) + } + + msg.Hashes = make([]*ShaHash, 0, count) + for i := uint64(0); i < count; i++ { + var sha ShaHash + err := readElement(r, &sha) + if err != nil { + return err + } + msg.AddTxHash(&sha) + } + + msg.Flags, err = ReadVarBytes(r, pver, maxFlagsPerMerkleBlock, + "merkle block flags size") + if err != nil { + return err + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32) error { + if pver < BIP0037Version { + str := fmt.Sprintf("merkleblock message invalid for protocol "+ + "version %d", pver) + return messageError("MsgMerkleBlock.BtcEncode", str) + } + + // Read num transaction hashes and limit to max. + numHashes := len(msg.Hashes) + if numHashes > maxTxPerBlock { + str := fmt.Sprintf("too many transaction hashes for message "+ + "[count %v, max %v]", numHashes, maxTxPerBlock) + return messageError("MsgMerkleBlock.BtcDecode", str) + } + numFlagBytes := len(msg.Flags) + if numFlagBytes > maxFlagsPerMerkleBlock { + str := fmt.Sprintf("too many flag bytes for message [count %v, "+ + "max %v]", numFlagBytes, maxFlagsPerMerkleBlock) + return messageError("MsgMerkleBlock.BtcDecode", str) + } + + err := writeBlockHeader(w, pver, &msg.Header) + if err != nil { + return err + } + + err = writeElement(w, msg.Transactions) + if err != nil { + return err + } + + err = WriteVarInt(w, pver, uint64(numHashes)) + if err != nil { + return err + } + for _, hash := range msg.Hashes { + err = writeElement(w, hash) + if err != nil { + return err + } + } + + err = WriteVarBytes(w, pver, msg.Flags) + if err != nil { + return err + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgMerkleBlock) Command() string { + return CmdMerkleBlock +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgMerkleBlock) MaxPayloadLength(pver uint32) uint32 { + return MaxBlockPayload +} + +// NewMsgMerkleBlock returns a new bitcoin merkleblock message that conforms to +// the Message interface. See MsgMerkleBlock for details. +func NewMsgMerkleBlock(bh *BlockHeader) *MsgMerkleBlock { + return &MsgMerkleBlock{ + Header: *bh, + Transactions: 0, + Hashes: make([]*ShaHash, 0), + Flags: make([]byte, 0), + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgmerkleblock_test.go b/vendor/github.com/btcsuite/btcd/wire/msgmerkleblock_test.go new file mode 100644 index 0000000000000000000000000000000000000000..482a1c4a4fc72966e1771e706ad1db2c3f6ec3ca --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgmerkleblock_test.go @@ -0,0 +1,426 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "crypto/rand" + "io" + "reflect" + "testing" + "time" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestMerkleBlock tests the MsgMerkleBlock API. +func TestMerkleBlock(t *testing.T) { + pver := wire.ProtocolVersion + + // Block 1 header. + prevHash := &blockOne.Header.PrevBlock + merkleHash := &blockOne.Header.MerkleRoot + bits := blockOne.Header.Bits + nonce := blockOne.Header.Nonce + bh := wire.NewBlockHeader(prevHash, merkleHash, bits, nonce) + + // Ensure the command is expected value. + wantCmd := "merkleblock" + msg := wire.NewMsgMerkleBlock(bh) + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgBlock: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + // Num addresses (varInt) + max allowed addresses. + wantPayload := uint32(1000000) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Load maxTxPerBlock hashes + data := make([]byte, 32) + for i := 0; i < wire.MaxTxPerBlock; i++ { + rand.Read(data) + hash, err := wire.NewShaHash(data) + if err != nil { + t.Errorf("NewShaHash failed: %v\n", err) + return + } + + if err = msg.AddTxHash(hash); err != nil { + t.Errorf("AddTxHash failed: %v\n", err) + return + } + } + + // Add one more Tx to test failure. + rand.Read(data) + hash, err := wire.NewShaHash(data) + if err != nil { + t.Errorf("NewShaHash failed: %v\n", err) + return + } + + if err = msg.AddTxHash(hash); err == nil { + t.Errorf("AddTxHash succeeded when it should have failed") + return + } + + // Test encode with latest protocol version. + var buf bytes.Buffer + err = msg.BtcEncode(&buf, pver) + if err != nil { + t.Errorf("encode of MsgMerkleBlock failed %v err <%v>", msg, err) + } + + // Test decode with latest protocol version. + readmsg := wire.MsgMerkleBlock{} + err = readmsg.BtcDecode(&buf, pver) + if err != nil { + t.Errorf("decode of MsgMerkleBlock failed [%v] err <%v>", buf, err) + } + + // Force extra hash to test maxTxPerBlock. + msg.Hashes = append(msg.Hashes, hash) + err = msg.BtcEncode(&buf, pver) + if err == nil { + t.Errorf("encode of MsgMerkleBlock succeeded with too many " + + "tx hashes when it should have failed") + return + } + + // Force too many flag bytes to test maxFlagsPerMerkleBlock. + // Reset the number of hashes back to a valid value. + msg.Hashes = msg.Hashes[len(msg.Hashes)-1:] + msg.Flags = make([]byte, wire.MaxFlagsPerMerkleBlock+1) + err = msg.BtcEncode(&buf, pver) + if err == nil { + t.Errorf("encode of MsgMerkleBlock succeeded with too many " + + "flag bytes when it should have failed") + return + } +} + +// TestMerkleBlockCrossProtocol tests the MsgMerkleBlock API when encoding with +// the latest protocol version and decoding with BIP0031Version. +func TestMerkleBlockCrossProtocol(t *testing.T) { + // Block 1 header. + prevHash := &blockOne.Header.PrevBlock + merkleHash := &blockOne.Header.MerkleRoot + bits := blockOne.Header.Bits + nonce := blockOne.Header.Nonce + bh := wire.NewBlockHeader(prevHash, merkleHash, bits, nonce) + + msg := wire.NewMsgMerkleBlock(bh) + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, wire.ProtocolVersion) + if err != nil { + t.Errorf("encode of NewMsgFilterLoad failed %v err <%v>", msg, + err) + } + + // Decode with old protocol version. + var readmsg wire.MsgFilterLoad + err = readmsg.BtcDecode(&buf, wire.BIP0031Version) + if err == nil { + t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't have %v", + msg) + } +} + +// TestMerkleBlockWire tests the MsgMerkleBlock wire encode and decode for +// various numbers of transaction hashes and protocol versions. +func TestMerkleBlockWire(t *testing.T) { + tests := []struct { + in *wire.MsgMerkleBlock // Message to encode + out *wire.MsgMerkleBlock // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + &merkleBlockOne, &merkleBlockOne, merkleBlockOneBytes, + wire.ProtocolVersion, + }, + + // Protocol version BIP0037Version. + { + &merkleBlockOne, &merkleBlockOne, merkleBlockOneBytes, + wire.BIP0037Version, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgMerkleBlock + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(&msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestMerkleBlockWireErrors performs negative tests against wire encode and +// decode of MsgBlock to confirm error paths work correctly. +func TestMerkleBlockWireErrors(t *testing.T) { + // Use protocol version 70001 specifically here instead of the latest + // because the test data is using bytes encoded with that protocol + // version. + pver := uint32(70001) + pverNoMerkleBlock := wire.BIP0037Version - 1 + wireErr := &wire.MessageError{} + + tests := []struct { + in *wire.MsgMerkleBlock // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error in version. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 0, + io.ErrShortWrite, io.EOF, + }, + // Force error in prev block hash. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 4, + io.ErrShortWrite, io.EOF, + }, + // Force error in merkle root. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 36, + io.ErrShortWrite, io.EOF, + }, + // Force error in timestamp. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 68, + io.ErrShortWrite, io.EOF, + }, + // Force error in difficulty bits. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 72, + io.ErrShortWrite, io.EOF, + }, + // Force error in header nonce. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 76, + io.ErrShortWrite, io.EOF, + }, + // Force error in transaction count. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 80, + io.ErrShortWrite, io.EOF, + }, + // Force error in num hashes. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 84, + io.ErrShortWrite, io.EOF, + }, + // Force error in hashes. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 85, + io.ErrShortWrite, io.EOF, + }, + // Force error in num flag bytes. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 117, + io.ErrShortWrite, io.EOF, + }, + // Force error in flag bytes. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 118, + io.ErrShortWrite, io.EOF, + }, + // Force error due to unsupported protocol version. + { + &merkleBlockOne, merkleBlockOneBytes, pverNoMerkleBlock, + 119, wireErr, wireErr, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgMerkleBlock + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + } +} + +// TestMerkleBlockOverflowErrors performs tests to ensure encoding and decoding +// merkle blocks that are intentionally crafted to use large values for the +// number of hashes and flags are handled properly. This could otherwise +// potentially be used as an attack vector. +func TestMerkleBlockOverflowErrors(t *testing.T) { + // Use protocol version 70001 specifically here instead of the latest + // protocol version because the test data is using bytes encoded with + // that version. + pver := uint32(70001) + + // Create bytes for a merkle block that claims to have more than the max + // allowed tx hashes. + var buf bytes.Buffer + wire.TstWriteVarInt(&buf, pver, wire.MaxTxPerBlock+1) + numHashesOffset := 84 + exceedMaxHashes := make([]byte, numHashesOffset) + copy(exceedMaxHashes, merkleBlockOneBytes[:numHashesOffset]) + exceedMaxHashes = append(exceedMaxHashes, buf.Bytes()...) + + // Create bytes for a merkle block that claims to have more than the max + // allowed flag bytes. + buf.Reset() + wire.TstWriteVarInt(&buf, pver, wire.MaxFlagsPerMerkleBlock+1) + numFlagBytesOffset := 117 + exceedMaxFlagBytes := make([]byte, numFlagBytesOffset) + copy(exceedMaxFlagBytes, merkleBlockOneBytes[:numFlagBytesOffset]) + exceedMaxFlagBytes = append(exceedMaxFlagBytes, buf.Bytes()...) + + tests := []struct { + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + err error // Expected error + }{ + // Block that claims to have more than max allowed hashes. + {exceedMaxHashes, pver, &wire.MessageError{}}, + // Block that claims to have more than max allowed flag bytes. + {exceedMaxFlagBytes, pver, &wire.MessageError{}}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Decode from wire format. + var msg wire.MsgMerkleBlock + r := bytes.NewReader(test.buf) + err := msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, reflect.TypeOf(test.err)) + continue + } + } +} + +// merkleBlockOne is a merkle block created from block one of the block chain +// where the first transaction matches. +var merkleBlockOne = wire.MsgMerkleBlock{ + Header: wire.BlockHeader{ + Version: 1, + PrevBlock: wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + }), + MerkleRoot: wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, + }), + Timestamp: time.Unix(0x4966bc61, 0), // 2009-01-08 20:54:25 -0600 CST + Bits: 0x1d00ffff, // 486604799 + Nonce: 0x9962e301, // 2573394689 + }, + Transactions: 1, + Hashes: []*wire.ShaHash{ + (*wire.ShaHash)(&[wire.HashSize]byte{ // Make go vet happy. + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, + }), + }, + Flags: []byte{0x80}, +} + +// merkleBlockOneBytes is the serialized bytes for a merkle block created from +// block one of the block chain where the first transation matches. +var merkleBlockOneBytes = []byte{ + 0x01, 0x00, 0x00, 0x00, // Version 1 + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot + 0x61, 0xbc, 0x66, 0x49, // Timestamp + 0xff, 0xff, 0x00, 0x1d, // Bits + 0x01, 0xe3, 0x62, 0x99, // Nonce + 0x01, 0x00, 0x00, 0x00, // TxnCount + 0x01, // Num hashes + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // Hash + 0x01, // Num flag bytes + 0x80, // Flags +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgnotfound.go b/vendor/github.com/btcsuite/btcd/wire/msgnotfound.go new file mode 100644 index 0000000000000000000000000000000000000000..31dddbae669515ff3ab6335ccf760c2d262acf89 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgnotfound.go @@ -0,0 +1,107 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// MsgNotFound defines a bitcoin notfound message which is sent in response to +// a getdata message if any of the requested data in not available on the peer. +// Each message is limited to a maximum number of inventory vectors, which is +// currently 50,000. +// +// Use the AddInvVect function to build up the list of inventory vectors when +// sending a notfound message to another peer. +type MsgNotFound struct { + InvList []*InvVect +} + +// AddInvVect adds an inventory vector to the message. +func (msg *MsgNotFound) AddInvVect(iv *InvVect) error { + if len(msg.InvList)+1 > MaxInvPerMsg { + str := fmt.Sprintf("too many invvect in message [max %v]", + MaxInvPerMsg) + return messageError("MsgNotFound.AddInvVect", str) + } + + msg.InvList = append(msg.InvList, iv) + return nil +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgNotFound) BtcDecode(r io.Reader, pver uint32) error { + count, err := ReadVarInt(r, pver) + if err != nil { + return err + } + + // Limit to max inventory vectors per message. + if count > MaxInvPerMsg { + str := fmt.Sprintf("too many invvect in message [%v]", count) + return messageError("MsgNotFound.BtcDecode", str) + } + + msg.InvList = make([]*InvVect, 0, count) + for i := uint64(0); i < count; i++ { + iv := InvVect{} + err := readInvVect(r, pver, &iv) + if err != nil { + return err + } + msg.AddInvVect(&iv) + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgNotFound) BtcEncode(w io.Writer, pver uint32) error { + // Limit to max inventory vectors per message. + count := len(msg.InvList) + if count > MaxInvPerMsg { + str := fmt.Sprintf("too many invvect in message [%v]", count) + return messageError("MsgNotFound.BtcEncode", str) + } + + err := WriteVarInt(w, pver, uint64(count)) + if err != nil { + return err + } + + for _, iv := range msg.InvList { + err := writeInvVect(w, pver, iv) + if err != nil { + return err + } + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgNotFound) Command() string { + return CmdNotFound +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgNotFound) MaxPayloadLength(pver uint32) uint32 { + // Max var int 9 bytes + max InvVects at 36 bytes each. + // Num inventory vectors (varInt) + max allowed inventory vectors. + return MaxVarIntPayload + (MaxInvPerMsg * maxInvVectPayload) +} + +// NewMsgNotFound returns a new bitcoin notfound message that conforms to the +// Message interface. See MsgNotFound for details. +func NewMsgNotFound() *MsgNotFound { + return &MsgNotFound{ + InvList: make([]*InvVect, 0, defaultInvListAlloc), + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgnotfound_test.go b/vendor/github.com/btcsuite/btcd/wire/msgnotfound_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3a71cd811ffcd25be17c5989fc2e349e4c13b187 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgnotfound_test.go @@ -0,0 +1,321 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestNotFound tests the MsgNotFound API. +func TestNotFound(t *testing.T) { + pver := wire.ProtocolVersion + + // Ensure the command is expected value. + wantCmd := "notfound" + msg := wire.NewMsgNotFound() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgNotFound: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + // Num inventory vectors (varInt) + max allowed inventory vectors. + wantPayload := uint32(1800009) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Ensure inventory vectors are added properly. + hash := wire.ShaHash{} + iv := wire.NewInvVect(wire.InvTypeBlock, &hash) + err := msg.AddInvVect(iv) + if err != nil { + t.Errorf("AddInvVect: %v", err) + } + if msg.InvList[0] != iv { + t.Errorf("AddInvVect: wrong invvect added - got %v, want %v", + spew.Sprint(msg.InvList[0]), spew.Sprint(iv)) + } + + // Ensure adding more than the max allowed inventory vectors per + // message returns an error. + for i := 0; i < wire.MaxInvPerMsg; i++ { + err = msg.AddInvVect(iv) + } + if err == nil { + t.Errorf("AddInvVect: expected error on too many inventory " + + "vectors not received") + } + + return +} + +// TestNotFoundWire tests the MsgNotFound wire encode and decode for various +// numbers of inventory vectors and protocol versions. +func TestNotFoundWire(t *testing.T) { + // Block 203707 hash. + hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc" + blockHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Transation 1 of Block 203707 hash. + hashStr = "d28a3dc7392bf00a9855ee93dd9a81eff82a2c4fe57fbd42cfe71b487accfaf0" + txHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + iv := wire.NewInvVect(wire.InvTypeBlock, blockHash) + iv2 := wire.NewInvVect(wire.InvTypeTx, txHash) + + // Empty notfound message. + NoInv := wire.NewMsgNotFound() + NoInvEncoded := []byte{ + 0x00, // Varint for number of inventory vectors + } + + // NotFound message with multiple inventory vectors. + MultiInv := wire.NewMsgNotFound() + MultiInv.AddInvVect(iv) + MultiInv.AddInvVect(iv2) + MultiInvEncoded := []byte{ + 0x02, // Varint for number of inv vectors + 0x02, 0x00, 0x00, 0x00, // InvTypeBlock + 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, + 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, + 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, + 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash + 0x01, 0x00, 0x00, 0x00, // InvTypeTx + 0xf0, 0xfa, 0xcc, 0x7a, 0x48, 0x1b, 0xe7, 0xcf, + 0x42, 0xbd, 0x7f, 0xe5, 0x4f, 0x2c, 0x2a, 0xf8, + 0xef, 0x81, 0x9a, 0xdd, 0x93, 0xee, 0x55, 0x98, + 0x0a, 0xf0, 0x2b, 0x39, 0xc7, 0x3d, 0x8a, 0xd2, // Tx 1 of block 203707 hash + } + + tests := []struct { + in *wire.MsgNotFound // Message to encode + out *wire.MsgNotFound // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version with no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.ProtocolVersion, + }, + + // Latest protocol version with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0035Version with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.BIP0031Version, + }, + + // Protocol version BIP0031Version with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version NetAddressTimeVersion with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion no inv vectors. + { + NoInv, + NoInv, + NoInvEncoded, + wire.MultipleAddressVersion, + }, + + // Protocol version MultipleAddressVersion with multiple inv vectors. + { + MultiInv, + MultiInv, + MultiInvEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgNotFound + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestNotFoundWireErrors performs negative tests against wire encode and decode +// of MsgNotFound to confirm error paths work correctly. +func TestNotFoundWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + wireErr := &wire.MessageError{} + + // Block 203707 hash. + hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc" + blockHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + iv := wire.NewInvVect(wire.InvTypeBlock, blockHash) + + // Base message used to induce errors. + baseNotFound := wire.NewMsgNotFound() + baseNotFound.AddInvVect(iv) + baseNotFoundEncoded := []byte{ + 0x02, // Varint for number of inv vectors + 0x02, 0x00, 0x00, 0x00, // InvTypeBlock + 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, + 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, + 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, + 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash + } + + // Message that forces an error by having more than the max allowed inv + // vectors. + maxNotFound := wire.NewMsgNotFound() + for i := 0; i < wire.MaxInvPerMsg; i++ { + maxNotFound.AddInvVect(iv) + } + maxNotFound.InvList = append(maxNotFound.InvList, iv) + maxNotFoundEncoded := []byte{ + 0xfd, 0x51, 0xc3, // Varint for number of inv vectors (50001) + } + + tests := []struct { + in *wire.MsgNotFound // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error in inventory vector count + {baseNotFound, baseNotFoundEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in inventory list. + {baseNotFound, baseNotFoundEncoded, pver, 1, io.ErrShortWrite, io.EOF}, + // Force error with greater than max inventory vectors. + {maxNotFound, maxNotFoundEncoded, pver, 3, wireErr, wireErr}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgNotFound + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgping.go b/vendor/github.com/btcsuite/btcd/wire/msgping.go new file mode 100644 index 0000000000000000000000000000000000000000..c9e3f646bd02858ea4d72d88e912d48359a1d398 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgping.go @@ -0,0 +1,87 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "io" +) + +// MsgPing implements the Message interface and represents a bitcoin ping +// message. +// +// For versions BIP0031Version and earlier, it is used primarily to confirm +// that a connection is still valid. A transmission error is typically +// interpreted as a closed connection and that the peer should be removed. +// For versions AFTER BIP0031Version it contains an identifier which can be +// returned in the pong message to determine network timing. +// +// The payload for this message just consists of a nonce used for identifying +// it later. +type MsgPing struct { + // Unique value associated with message that is used to identify + // specific ping message. + Nonce uint64 +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgPing) BtcDecode(r io.Reader, pver uint32) error { + // There was no nonce for BIP0031Version and earlier. + // NOTE: > is not a mistake here. The BIP0031 was defined as AFTER + // the version unlike most others. + if pver > BIP0031Version { + err := readElement(r, &msg.Nonce) + if err != nil { + return err + } + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgPing) BtcEncode(w io.Writer, pver uint32) error { + // There was no nonce for BIP0031Version and earlier. + // NOTE: > is not a mistake here. The BIP0031 was defined as AFTER + // the version unlike most others. + if pver > BIP0031Version { + err := writeElement(w, msg.Nonce) + if err != nil { + return err + } + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgPing) Command() string { + return CmdPing +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgPing) MaxPayloadLength(pver uint32) uint32 { + plen := uint32(0) + // There was no nonce for BIP0031Version and earlier. + // NOTE: > is not a mistake here. The BIP0031 was defined as AFTER + // the version unlike most others. + if pver > BIP0031Version { + // Nonce 8 bytes. + plen += 8 + } + + return plen +} + +// NewMsgPing returns a new bitcoin ping message that conforms to the Message +// interface. See MsgPing for details. +func NewMsgPing(nonce uint64) *MsgPing { + return &MsgPing{ + Nonce: nonce, + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgping_test.go b/vendor/github.com/btcsuite/btcd/wire/msgping_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ae2ab214c368ec19f23c5d23ef70986247eda387 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgping_test.go @@ -0,0 +1,243 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestPing tests the MsgPing API against the latest protocol version. +func TestPing(t *testing.T) { + pver := wire.ProtocolVersion + + // Ensure we get the same nonce back out. + nonce, err := wire.RandomUint64() + if err != nil { + t.Errorf("RandomUint64: Error generating nonce: %v", err) + } + msg := wire.NewMsgPing(nonce) + if msg.Nonce != nonce { + t.Errorf("NewMsgPing: wrong nonce - got %v, want %v", + msg.Nonce, nonce) + } + + // Ensure the command is expected value. + wantCmd := "ping" + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgPing: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + wantPayload := uint32(8) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + return +} + +// TestPingBIP0031 tests the MsgPing API against the protocol version +// BIP0031Version. +func TestPingBIP0031(t *testing.T) { + // Use the protocol version just prior to BIP0031Version changes. + pver := wire.BIP0031Version + + nonce, err := wire.RandomUint64() + if err != nil { + t.Errorf("RandomUint64: Error generating nonce: %v", err) + } + msg := wire.NewMsgPing(nonce) + if msg.Nonce != nonce { + t.Errorf("NewMsgPing: wrong nonce - got %v, want %v", + msg.Nonce, nonce) + } + + // Ensure max payload is expected value for old protocol version. + wantPayload := uint32(0) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Test encode with old protocol version. + var buf bytes.Buffer + err = msg.BtcEncode(&buf, pver) + if err != nil { + t.Errorf("encode of MsgPing failed %v err <%v>", msg, err) + } + + // Test decode with old protocol version. + readmsg := wire.NewMsgPing(0) + err = readmsg.BtcDecode(&buf, pver) + if err != nil { + t.Errorf("decode of MsgPing failed [%v] err <%v>", buf, err) + } + + // Since this protocol version doesn't support the nonce, make sure + // it didn't get encoded and decoded back out. + if msg.Nonce == readmsg.Nonce { + t.Errorf("Should not get same nonce for protocol version %d", pver) + } + + return +} + +// TestPingCrossProtocol tests the MsgPing API when encoding with the latest +// protocol version and decoding with BIP0031Version. +func TestPingCrossProtocol(t *testing.T) { + nonce, err := wire.RandomUint64() + if err != nil { + t.Errorf("RandomUint64: Error generating nonce: %v", err) + } + msg := wire.NewMsgPing(nonce) + if msg.Nonce != nonce { + t.Errorf("NewMsgPing: wrong nonce - got %v, want %v", + msg.Nonce, nonce) + } + + // Encode with latest protocol version. + var buf bytes.Buffer + err = msg.BtcEncode(&buf, wire.ProtocolVersion) + if err != nil { + t.Errorf("encode of MsgPing failed %v err <%v>", msg, err) + } + + // Decode with old protocol version. + readmsg := wire.NewMsgPing(0) + err = readmsg.BtcDecode(&buf, wire.BIP0031Version) + if err != nil { + t.Errorf("decode of MsgPing failed [%v] err <%v>", buf, err) + } + + // Since one of the protocol versions doesn't support the nonce, make + // sure it didn't get encoded and decoded back out. + if msg.Nonce == readmsg.Nonce { + t.Error("Should not get same nonce for cross protocol") + } +} + +// TestPingWire tests the MsgPing wire encode and decode for various protocol +// versions. +func TestPingWire(t *testing.T) { + tests := []struct { + in wire.MsgPing // Message to encode + out wire.MsgPing // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + wire.MsgPing{Nonce: 123123}, // 0x1e0f3 + wire.MsgPing{Nonce: 123123}, // 0x1e0f3 + []byte{0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}, + wire.ProtocolVersion, + }, + + // Protocol version BIP0031Version+1 + { + wire.MsgPing{Nonce: 456456}, // 0x6f708 + wire.MsgPing{Nonce: 456456}, // 0x6f708 + []byte{0x08, 0xf7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00}, + wire.BIP0031Version + 1, + }, + + // Protocol version BIP0031Version + { + wire.MsgPing{Nonce: 789789}, // 0xc0d1d + wire.MsgPing{Nonce: 0}, // No nonce for pver + []byte{}, // No nonce for pver + wire.BIP0031Version, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgPing + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestPingWireErrors performs negative tests against wire encode and decode +// of MsgPing to confirm error paths work correctly. +func TestPingWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + + tests := []struct { + in *wire.MsgPing // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Latest protocol version with intentional read/write errors. + { + &wire.MsgPing{Nonce: 123123}, // 0x1e0f3 + []byte{0xf3, 0xe0, 0x01, 0x00}, + pver, + 2, + io.ErrShortWrite, + io.ErrUnexpectedEOF, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // Decode from wire format. + var msg wire.MsgPing + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgpong.go b/vendor/github.com/btcsuite/btcd/wire/msgpong.go new file mode 100644 index 0000000000000000000000000000000000000000..4c89acc8335702ea69d2d33d4aa9b06d4f46ce34 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgpong.go @@ -0,0 +1,88 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// MsgPong implements the Message interface and represents a bitcoin pong +// message which is used primarily to confirm that a connection is still valid +// in response to a bitcoin ping message (MsgPing). +// +// This message was not added until protocol versions AFTER BIP0031Version. +type MsgPong struct { + // Unique value associated with message that is used to identify + // specific ping message. + Nonce uint64 +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgPong) BtcDecode(r io.Reader, pver uint32) error { + // NOTE: <= is not a mistake here. The BIP0031 was defined as AFTER + // the version unlike most others. + if pver <= BIP0031Version { + str := fmt.Sprintf("pong message invalid for protocol "+ + "version %d", pver) + return messageError("MsgPong.BtcDecode", str) + } + + err := readElement(r, &msg.Nonce) + if err != nil { + return err + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgPong) BtcEncode(w io.Writer, pver uint32) error { + // NOTE: <= is not a mistake here. The BIP0031 was defined as AFTER + // the version unlike most others. + if pver <= BIP0031Version { + str := fmt.Sprintf("pong message invalid for protocol "+ + "version %d", pver) + return messageError("MsgPong.BtcEncode", str) + } + + err := writeElement(w, msg.Nonce) + if err != nil { + return err + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgPong) Command() string { + return CmdPong +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgPong) MaxPayloadLength(pver uint32) uint32 { + plen := uint32(0) + // The pong message did not exist for BIP0031Version and earlier. + // NOTE: > is not a mistake here. The BIP0031 was defined as AFTER + // the version unlike most others. + if pver > BIP0031Version { + // Nonce 8 bytes. + plen += 8 + } + + return plen +} + +// NewMsgPong returns a new bitcoin pong message that conforms to the Message +// interface. See MsgPong for details. +func NewMsgPong(nonce uint64) *MsgPong { + return &MsgPong{ + Nonce: nonce, + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgpong_test.go b/vendor/github.com/btcsuite/btcd/wire/msgpong_test.go new file mode 100644 index 0000000000000000000000000000000000000000..aac46fc482614ff1ff8ed39f63df78f175569cba --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgpong_test.go @@ -0,0 +1,276 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestPongLatest tests the MsgPong API against the latest protocol version. +func TestPongLatest(t *testing.T) { + pver := wire.ProtocolVersion + + nonce, err := wire.RandomUint64() + if err != nil { + t.Errorf("RandomUint64: error generating nonce: %v", err) + } + msg := wire.NewMsgPong(nonce) + if msg.Nonce != nonce { + t.Errorf("NewMsgPong: wrong nonce - got %v, want %v", + msg.Nonce, nonce) + } + + // Ensure the command is expected value. + wantCmd := "pong" + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgPong: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + wantPayload := uint32(8) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Test encode with latest protocol version. + var buf bytes.Buffer + err = msg.BtcEncode(&buf, pver) + if err != nil { + t.Errorf("encode of MsgPong failed %v err <%v>", msg, err) + } + + // Test decode with latest protocol version. + readmsg := wire.NewMsgPong(0) + err = readmsg.BtcDecode(&buf, pver) + if err != nil { + t.Errorf("decode of MsgPong failed [%v] err <%v>", buf, err) + } + + // Ensure nonce is the same. + if msg.Nonce != readmsg.Nonce { + t.Errorf("Should get same nonce for protocol version %d", pver) + } + + return +} + +// TestPongBIP0031 tests the MsgPong API against the protocol version +// BIP0031Version. +func TestPongBIP0031(t *testing.T) { + // Use the protocol version just prior to BIP0031Version changes. + pver := wire.BIP0031Version + + nonce, err := wire.RandomUint64() + if err != nil { + t.Errorf("Error generating nonce: %v", err) + } + msg := wire.NewMsgPong(nonce) + if msg.Nonce != nonce { + t.Errorf("Should get same nonce back out.") + } + + // Ensure max payload is expected value for old protocol version. + size := msg.MaxPayloadLength(pver) + if size != 0 { + t.Errorf("Max length should be 0 for pong protocol version %d.", + pver) + } + + // Test encode with old protocol version. + var buf bytes.Buffer + err = msg.BtcEncode(&buf, pver) + if err == nil { + t.Errorf("encode of MsgPong succeeded when it shouldn't have %v", + msg) + } + + // Test decode with old protocol version. + readmsg := wire.NewMsgPong(0) + err = readmsg.BtcDecode(&buf, pver) + if err == nil { + t.Errorf("decode of MsgPong succeeded when it shouldn't have %v", + spew.Sdump(buf)) + } + + // Since this protocol version doesn't support pong, make sure the + // nonce didn't get encoded and decoded back out. + if msg.Nonce == readmsg.Nonce { + t.Errorf("Should not get same nonce for protocol version %d", pver) + } + + return +} + +// TestPongCrossProtocol tests the MsgPong API when encoding with the latest +// protocol version and decoding with BIP0031Version. +func TestPongCrossProtocol(t *testing.T) { + nonce, err := wire.RandomUint64() + if err != nil { + t.Errorf("Error generating nonce: %v", err) + } + msg := wire.NewMsgPong(nonce) + if msg.Nonce != nonce { + t.Errorf("Should get same nonce back out.") + } + + // Encode with latest protocol version. + var buf bytes.Buffer + err = msg.BtcEncode(&buf, wire.ProtocolVersion) + if err != nil { + t.Errorf("encode of MsgPong failed %v err <%v>", msg, err) + } + + // Decode with old protocol version. + readmsg := wire.NewMsgPong(0) + err = readmsg.BtcDecode(&buf, wire.BIP0031Version) + if err == nil { + t.Errorf("encode of MsgPong succeeded when it shouldn't have %v", + msg) + } + + // Since one of the protocol versions doesn't support the pong message, + // make sure the nonce didn't get encoded and decoded back out. + if msg.Nonce == readmsg.Nonce { + t.Error("Should not get same nonce for cross protocol") + } +} + +// TestPongWire tests the MsgPong wire encode and decode for various protocol +// versions. +func TestPongWire(t *testing.T) { + tests := []struct { + in wire.MsgPong // Message to encode + out wire.MsgPong // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + wire.MsgPong{Nonce: 123123}, // 0x1e0f3 + wire.MsgPong{Nonce: 123123}, // 0x1e0f3 + []byte{0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}, + wire.ProtocolVersion, + }, + + // Protocol version BIP0031Version+1 + { + wire.MsgPong{Nonce: 456456}, // 0x6f708 + wire.MsgPong{Nonce: 456456}, // 0x6f708 + []byte{0x08, 0xf7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00}, + wire.BIP0031Version + 1, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgPong + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestPongWireErrors performs negative tests against wire encode and decode +// of MsgPong to confirm error paths work correctly. +func TestPongWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + pverNoPong := wire.BIP0031Version + wireErr := &wire.MessageError{} + + basePong := wire.NewMsgPong(123123) // 0x1e0f3 + basePongEncoded := []byte{ + 0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + } + + tests := []struct { + in *wire.MsgPong // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Latest protocol version with intentional read/write errors. + // Force error in nonce. + {basePong, basePongEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error due to unsupported protocol version. + {basePong, basePongEncoded, pverNoPong, 4, wireErr, wireErr}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgPong + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgreject.go b/vendor/github.com/btcsuite/btcd/wire/msgreject.go new file mode 100644 index 0000000000000000000000000000000000000000..bf8659a4c914a012ff5b34ba430fe5ed0d9260c5 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgreject.go @@ -0,0 +1,184 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// RejectCode represents a numeric value by which a remote peer indicates +// why a message was rejected. +type RejectCode uint8 + +// These constants define the various supported reject codes. +const ( + RejectMalformed RejectCode = 0x01 + RejectInvalid RejectCode = 0x10 + RejectObsolete RejectCode = 0x11 + RejectDuplicate RejectCode = 0x12 + RejectNonstandard RejectCode = 0x40 + RejectDust RejectCode = 0x41 + RejectInsufficientFee RejectCode = 0x42 + RejectCheckpoint RejectCode = 0x43 +) + +// Map of reject codes back strings for pretty printing. +var rejectCodeStrings = map[RejectCode]string{ + RejectMalformed: "REJECT_MALFORMED", + RejectInvalid: "REJECT_INVALID", + RejectObsolete: "REJECT_OBSOLETE", + RejectDuplicate: "REJECT_DUPLICATE", + RejectNonstandard: "REJECT_NONSTANDARD", + RejectDust: "REJECT_DUST", + RejectInsufficientFee: "REJECT_INSUFFICIENTFEE", + RejectCheckpoint: "REJECT_CHECKPOINT", +} + +// String returns the RejectCode in human-readable form. +func (code RejectCode) String() string { + if s, ok := rejectCodeStrings[code]; ok { + return s + } + + return fmt.Sprintf("Unknown RejectCode (%d)", uint8(code)) +} + +// MsgReject implements the Message interface and represents a bitcoin reject +// message. +// +// This message was not added until protocol version RejectVersion. +type MsgReject struct { + // Cmd is the command for the message which was rejected such as + // as CmdBlock or CmdTx. This can be obtained from the Command function + // of a Message. + Cmd string + + // RejectCode is a code indicating why the command was rejected. It + // is encoded as a uint8 on the wire. + Code RejectCode + + // Reason is a human-readable string with specific details (over and + // above the reject code) about why the command was rejected. + Reason string + + // Hash identifies a specific block or transaction that was rejected + // and therefore only applies the MsgBlock and MsgTx messages. + Hash ShaHash +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgReject) BtcDecode(r io.Reader, pver uint32) error { + if pver < RejectVersion { + str := fmt.Sprintf("reject message invalid for protocol "+ + "version %d", pver) + return messageError("MsgReject.BtcDecode", str) + } + + // Command that was rejected. + cmd, err := ReadVarString(r, pver) + if err != nil { + return err + } + msg.Cmd = cmd + + // Code indicating why the command was rejected. + err = readElement(r, &msg.Code) + if err != nil { + return err + } + + // Human readable string with specific details (over and above the + // reject code above) about why the command was rejected. + reason, err := ReadVarString(r, pver) + if err != nil { + return err + } + msg.Reason = reason + + // CmdBlock and CmdTx messages have an additional hash field that + // identifies the specific block or transaction. + if msg.Cmd == CmdBlock || msg.Cmd == CmdTx { + err := readElement(r, &msg.Hash) + if err != nil { + return err + } + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgReject) BtcEncode(w io.Writer, pver uint32) error { + if pver < RejectVersion { + str := fmt.Sprintf("reject message invalid for protocol "+ + "version %d", pver) + return messageError("MsgReject.BtcEncode", str) + } + + // Command that was rejected. + err := WriteVarString(w, pver, msg.Cmd) + if err != nil { + return err + } + + // Code indicating why the command was rejected. + err = writeElement(w, msg.Code) + if err != nil { + return err + } + + // Human readable string with specific details (over and above the + // reject code above) about why the command was rejected. + err = WriteVarString(w, pver, msg.Reason) + if err != nil { + return err + } + + // CmdBlock and CmdTx messages have an additional hash field that + // identifies the specific block or transaction. + if msg.Cmd == CmdBlock || msg.Cmd == CmdTx { + err := writeElement(w, &msg.Hash) + if err != nil { + return err + } + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgReject) Command() string { + return CmdReject +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgReject) MaxPayloadLength(pver uint32) uint32 { + plen := uint32(0) + // The reject message did not exist before protocol version + // RejectVersion. + if pver >= RejectVersion { + // Unfortunately the bitcoin protocol does not enforce a sane + // limit on the length of the reason, so the max payload is the + // overall maximum message payload. + plen = MaxMessagePayload + } + + return plen +} + +// NewMsgReject returns a new bitcoin reject message that conforms to the +// Message interface. See MsgReject for details. +func NewMsgReject(command string, code RejectCode, reason string) *MsgReject { + return &MsgReject{ + Cmd: command, + Code: code, + Reason: reason, + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgreject_test.go b/vendor/github.com/btcsuite/btcd/wire/msgreject_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5645f97f44367f3af83f460570a7154d93a12a4f --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgreject_test.go @@ -0,0 +1,385 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestRejectCodeStringer tests the stringized output for the reject code type. +func TestRejectCodeStringer(t *testing.T) { + tests := []struct { + in wire.RejectCode + want string + }{ + {wire.RejectMalformed, "REJECT_MALFORMED"}, + {wire.RejectInvalid, "REJECT_INVALID"}, + {wire.RejectObsolete, "REJECT_OBSOLETE"}, + {wire.RejectDuplicate, "REJECT_DUPLICATE"}, + {wire.RejectNonstandard, "REJECT_NONSTANDARD"}, + {wire.RejectDust, "REJECT_DUST"}, + {wire.RejectInsufficientFee, "REJECT_INSUFFICIENTFEE"}, + {wire.RejectCheckpoint, "REJECT_CHECKPOINT"}, + {0xff, "Unknown RejectCode (255)"}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.in.String() + if result != test.want { + t.Errorf("String #%d\n got: %s want: %s", i, result, + test.want) + continue + } + } + +} + +// TestRejectLatest tests the MsgPong API against the latest protocol version. +func TestRejectLatest(t *testing.T) { + pver := wire.ProtocolVersion + + // Create reject message data. + rejCommand := (&wire.MsgBlock{}).Command() + rejCode := wire.RejectDuplicate + rejReason := "duplicate block" + rejHash := mainNetGenesisHash + + // Ensure we get the correct data back out. + msg := wire.NewMsgReject(rejCommand, rejCode, rejReason) + msg.Hash = rejHash + if msg.Cmd != rejCommand { + t.Errorf("NewMsgReject: wrong rejected command - got %v, "+ + "want %v", msg.Cmd, rejCommand) + } + if msg.Code != rejCode { + t.Errorf("NewMsgReject: wrong rejected code - got %v, "+ + "want %v", msg.Code, rejCode) + } + if msg.Reason != rejReason { + t.Errorf("NewMsgReject: wrong rejected reason - got %v, "+ + "want %v", msg.Reason, rejReason) + } + + // Ensure the command is expected value. + wantCmd := "reject" + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgReject: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + wantPayload := uint32(wire.MaxMessagePayload) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Test encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, pver) + if err != nil { + t.Errorf("encode of MsgReject failed %v err <%v>", msg, err) + } + + // Test decode with latest protocol version. + readMsg := wire.MsgReject{} + err = readMsg.BtcDecode(&buf, pver) + if err != nil { + t.Errorf("decode of MsgReject failed %v err <%v>", buf.Bytes(), + err) + } + + // Ensure decoded data is the same. + if msg.Cmd != readMsg.Cmd { + t.Errorf("Should get same reject command - got %v, want %v", + readMsg.Cmd, msg.Cmd) + } + if msg.Code != readMsg.Code { + t.Errorf("Should get same reject code - got %v, want %v", + readMsg.Code, msg.Code) + } + if msg.Reason != readMsg.Reason { + t.Errorf("Should get same reject reason - got %v, want %v", + readMsg.Reason, msg.Reason) + } + if msg.Hash != readMsg.Hash { + t.Errorf("Should get same reject hash - got %v, want %v", + readMsg.Hash, msg.Hash) + } +} + +// TestRejectBeforeAdded tests the MsgReject API against a protocol version +// before the version which introduced it (RejectVersion). +func TestRejectBeforeAdded(t *testing.T) { + // Use the protocol version just prior to RejectVersion. + pver := wire.RejectVersion - 1 + + // Create reject message data. + rejCommand := (&wire.MsgBlock{}).Command() + rejCode := wire.RejectDuplicate + rejReason := "duplicate block" + rejHash := mainNetGenesisHash + + msg := wire.NewMsgReject(rejCommand, rejCode, rejReason) + msg.Hash = rejHash + + // Ensure max payload is expected value for old protocol version. + size := msg.MaxPayloadLength(pver) + if size != 0 { + t.Errorf("Max length should be 0 for reject protocol version %d.", + pver) + } + + // Test encode with old protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, pver) + if err == nil { + t.Errorf("encode of MsgReject succeeded when it shouldn't "+ + "have %v", msg) + } + + // // Test decode with old protocol version. + readMsg := wire.MsgReject{} + err = readMsg.BtcDecode(&buf, pver) + if err == nil { + t.Errorf("decode of MsgReject succeeded when it shouldn't "+ + "have %v", spew.Sdump(buf.Bytes())) + } + + // Since this protocol version doesn't support reject, make sure various + // fields didn't get encoded and decoded back out. + if msg.Cmd == readMsg.Cmd { + t.Errorf("Should not get same reject command for protocol "+ + "version %d", pver) + } + if msg.Code == readMsg.Code { + t.Errorf("Should not get same reject code for protocol "+ + "version %d", pver) + } + if msg.Reason == readMsg.Reason { + t.Errorf("Should not get same reject reason for protocol "+ + "version %d", pver) + } + if msg.Hash == readMsg.Hash { + t.Errorf("Should not get same reject hash for protocol "+ + "version %d", pver) + } +} + +// TestRejectCrossProtocol tests the MsgReject API when encoding with the latest +// protocol version and decoded with a version before the version which +// introduced it (RejectVersion). +func TestRejectCrossProtocol(t *testing.T) { + // Create reject message data. + rejCommand := (&wire.MsgBlock{}).Command() + rejCode := wire.RejectDuplicate + rejReason := "duplicate block" + rejHash := mainNetGenesisHash + + msg := wire.NewMsgReject(rejCommand, rejCode, rejReason) + msg.Hash = rejHash + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, wire.ProtocolVersion) + if err != nil { + t.Errorf("encode of MsgReject failed %v err <%v>", msg, err) + } + + // Decode with old protocol version. + readMsg := wire.MsgReject{} + err = readMsg.BtcDecode(&buf, wire.RejectVersion-1) + if err == nil { + t.Errorf("encode of MsgReject succeeded when it shouldn't "+ + "have %v", msg) + } + + // Since one of the protocol versions doesn't support the reject + // message, make sure the various fields didn't get encoded and decoded + // back out. + if msg.Cmd == readMsg.Cmd { + t.Errorf("Should not get same reject command for cross protocol") + } + if msg.Code == readMsg.Code { + t.Errorf("Should not get same reject code for cross protocol") + } + if msg.Reason == readMsg.Reason { + t.Errorf("Should not get same reject reason for cross protocol") + } + if msg.Hash == readMsg.Hash { + t.Errorf("Should not get same reject hash for cross protocol") + } +} + +// TestRejectWire tests the MsgReject wire encode and decode for various +// protocol versions. +func TestRejectWire(t *testing.T) { + tests := []struct { + msg wire.MsgReject // Message to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version rejected command version (no hash). + { + wire.MsgReject{ + Cmd: "version", + Code: wire.RejectDuplicate, + Reason: "duplicate version", + }, + []byte{ + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // "version" + 0x12, // wire.RejectDuplicate + 0x11, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, // "duplicate version" + }, + wire.ProtocolVersion, + }, + // Latest protocol version rejected command block (has hash). + { + wire.MsgReject{ + Cmd: "block", + Code: wire.RejectDuplicate, + Reason: "duplicate block", + Hash: mainNetGenesisHash, + }, + []byte{ + 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, // "block" + 0x12, // wire.RejectDuplicate + 0x0f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, // "duplicate block" + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // mainNetGenesisHash + }, + wire.ProtocolVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.msg.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgReject + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(msg, test.msg) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.msg)) + continue + } + } +} + +// TestRejectWireErrors performs negative tests against wire encode and decode +// of MsgReject to confirm error paths work correctly. +func TestRejectWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + pverNoReject := wire.RejectVersion - 1 + wireErr := &wire.MessageError{} + + baseReject := wire.NewMsgReject("block", wire.RejectDuplicate, + "duplicate block") + baseReject.Hash = mainNetGenesisHash + baseRejectEncoded := []byte{ + 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, // "block" + 0x12, // wire.RejectDuplicate + 0x0f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, // "duplicate block" + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // mainNetGenesisHash + } + + tests := []struct { + in *wire.MsgReject // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Latest protocol version with intentional read/write errors. + // Force error in reject command. + {baseReject, baseRejectEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in reject code. + {baseReject, baseRejectEncoded, pver, 6, io.ErrShortWrite, io.EOF}, + // Force error in reject reason. + {baseReject, baseRejectEncoded, pver, 7, io.ErrShortWrite, io.EOF}, + // Force error in reject hash. + {baseReject, baseRejectEncoded, pver, 23, io.ErrShortWrite, io.EOF}, + // Force error due to unsupported protocol version. + {baseReject, baseRejectEncoded, pverNoReject, 6, wireErr, wireErr}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgReject + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgsendheaders.go b/vendor/github.com/btcsuite/btcd/wire/msgsendheaders.go new file mode 100644 index 0000000000000000000000000000000000000000..532e4aa1da254a23b7b6d9a6798927460ac13b9c --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgsendheaders.go @@ -0,0 +1,60 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// MsgSendHeaders implements the Message interface and represents a bitcoin +// sendheaders message. It is used to request the peer send block headers +// rather than inventory vectors. +// +// This message has no payload and was not added until protocol versions +// starting with SendHeadersVersion. +type MsgSendHeaders struct{} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgSendHeaders) BtcDecode(r io.Reader, pver uint32) error { + if pver < SendHeadersVersion { + str := fmt.Sprintf("sendheaders message invalid for protocol "+ + "version %d", pver) + return messageError("MsgSendHeaders.BtcDecode", str) + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgSendHeaders) BtcEncode(w io.Writer, pver uint32) error { + if pver < SendHeadersVersion { + str := fmt.Sprintf("sendheaders message invalid for protocol "+ + "version %d", pver) + return messageError("MsgSendHeaders.BtcEncode", str) + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgSendHeaders) Command() string { + return CmdSendHeaders +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgSendHeaders) MaxPayloadLength(pver uint32) uint32 { + return 0 +} + +// NewMsgSendHeaders returns a new bitcoin sendheaders message that conforms to +// the Message interface. See MsgSendHeaders for details. +func NewMsgSendHeaders() *MsgSendHeaders { + return &MsgSendHeaders{} +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgsendheaders_test.go b/vendor/github.com/btcsuite/btcd/wire/msgsendheaders_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e09db6ad12bbf15adb5104d72cf3abdb44641a15 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgsendheaders_test.go @@ -0,0 +1,191 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestSendHeaders tests the MsgSendHeaders API against the latest protocol +// version. +func TestSendHeaders(t *testing.T) { + pver := wire.ProtocolVersion + + // Ensure the command is expected value. + wantCmd := "sendheaders" + msg := wire.NewMsgSendHeaders() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgSendHeaders: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value. + wantPayload := uint32(0) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Test encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, pver) + if err != nil { + t.Errorf("encode of MsgSendHeaders failed %v err <%v>", msg, + err) + } + + // Older protocol versions should fail encode since message didn't + // exist yet. + oldPver := wire.SendHeadersVersion - 1 + err = msg.BtcEncode(&buf, oldPver) + if err == nil { + s := "encode of MsgSendHeaders passed for old protocol " + + "version %v err <%v>" + t.Errorf(s, msg, err) + } + + // Test decode with latest protocol version. + readmsg := wire.NewMsgSendHeaders() + err = readmsg.BtcDecode(&buf, pver) + if err != nil { + t.Errorf("decode of MsgSendHeaders failed [%v] err <%v>", buf, + err) + } + + // Older protocol versions should fail decode since message didn't + // exist yet. + err = readmsg.BtcDecode(&buf, oldPver) + if err == nil { + s := "decode of MsgSendHeaders passed for old protocol " + + "version %v err <%v>" + t.Errorf(s, msg, err) + } + + return +} + +// TestSendHeadersBIP0130 tests the MsgSendHeaders API against the protocol +// prior to version SendHeadersVersion. +func TestSendHeadersBIP0130(t *testing.T) { + // Use the protocol version just prior to SendHeadersVersion changes. + pver := wire.SendHeadersVersion - 1 + + msg := wire.NewMsgSendHeaders() + + // Test encode with old protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, pver) + if err == nil { + t.Errorf("encode of MsgSendHeaders succeeded when it should " + + "have failed") + } + + // Test decode with old protocol version. + readmsg := wire.NewMsgSendHeaders() + err = readmsg.BtcDecode(&buf, pver) + if err == nil { + t.Errorf("decode of MsgSendHeaders succeeded when it should " + + "have failed") + } + + return +} + +// TestSendHeadersCrossProtocol tests the MsgSendHeaders API when encoding with +// the latest protocol version and decoding with SendHeadersVersion. +func TestSendHeadersCrossProtocol(t *testing.T) { + msg := wire.NewMsgSendHeaders() + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, wire.ProtocolVersion) + if err != nil { + t.Errorf("encode of MsgSendHeaders failed %v err <%v>", msg, + err) + } + + // Decode with old protocol version. + readmsg := wire.NewMsgSendHeaders() + err = readmsg.BtcDecode(&buf, wire.SendHeadersVersion) + if err != nil { + t.Errorf("decode of MsgSendHeaders failed [%v] err <%v>", buf, + err) + } +} + +// TestSendHeadersWire tests the MsgSendHeaders wire encode and decode for +// various protocol versions. +func TestSendHeadersWire(t *testing.T) { + msgSendHeaders := wire.NewMsgSendHeaders() + msgSendHeadersEncoded := []byte{} + + tests := []struct { + in *wire.MsgSendHeaders // Message to encode + out *wire.MsgSendHeaders // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + msgSendHeaders, + msgSendHeaders, + msgSendHeadersEncoded, + wire.ProtocolVersion, + }, + + // Protocol version SendHeadersVersion+1 + { + msgSendHeaders, + msgSendHeaders, + msgSendHeadersEncoded, + wire.SendHeadersVersion + 1, + }, + + // Protocol version SendHeadersVersion + { + msgSendHeaders, + msgSendHeaders, + msgSendHeadersEncoded, + wire.SendHeadersVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgSendHeaders + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgtx.go b/vendor/github.com/btcsuite/btcd/wire/msgtx.go new file mode 100644 index 0000000000000000000000000000000000000000..4542dd2986f07219be5ae7c69c3778f56c8feef8 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgtx.go @@ -0,0 +1,591 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "strconv" +) + +const ( + // TxVersion is the current latest supported transaction version. + TxVersion = 1 + + // MaxTxInSequenceNum is the maximum sequence number the sequence field + // of a transaction input can be. + MaxTxInSequenceNum uint32 = 0xffffffff + + // MaxPrevOutIndex is the maximum index the index field of a previous + // outpoint can be. + MaxPrevOutIndex uint32 = 0xffffffff +) + +// defaultTxInOutAlloc is the default size used for the backing array for +// transaction inputs and outputs. The array will dynamically grow as needed, +// but this figure is intended to provide enough space for the number of +// inputs and outputs in a typical transaction without needing to grow the +// backing array multiple times. +const defaultTxInOutAlloc = 15 + +const ( + // minTxInPayload is the minimum payload size for a transaction input. + // PreviousOutPoint.Hash + PreviousOutPoint.Index 4 bytes + Varint for + // SignatureScript length 1 byte + Sequence 4 bytes. + minTxInPayload = 9 + HashSize + + // maxTxInPerMessage is the maximum number of transactions inputs that + // a transaction which fits into a message could possibly have. + maxTxInPerMessage = (MaxMessagePayload / minTxInPayload) + 1 + + // minTxOutPayload is the minimum payload size for a transaction output. + // Value 8 bytes + Varint for PkScript length 1 byte. + minTxOutPayload = 9 + + // maxTxOutPerMessage is the maximum number of transactions outputs that + // a transaction which fits into a message could possibly have. + maxTxOutPerMessage = (MaxMessagePayload / minTxOutPayload) + 1 + + // minTxPayload is the minimum payload size for a transaction. Note + // that any realistically usable transaction must have at least one + // input or output, but that is a rule enforced at a higher layer, so + // it is intentionally not included here. + // Version 4 bytes + Varint number of transaction inputs 1 byte + Varint + // number of transaction outputs 1 byte + LockTime 4 bytes + min input + // payload + min output payload. + minTxPayload = 10 +) + +// OutPoint defines a bitcoin data type that is used to track previous +// transaction outputs. +type OutPoint struct { + Hash ShaHash + Index uint32 +} + +// NewOutPoint returns a new bitcoin transaction outpoint point with the +// provided hash and index. +func NewOutPoint(hash *ShaHash, index uint32) *OutPoint { + return &OutPoint{ + Hash: *hash, + Index: index, + } +} + +// String returns the OutPoint in the human-readable form "hash:index". +func (o OutPoint) String() string { + // Allocate enough for hash string, colon, and 10 digits. Although + // at the time of writing, the number of digits can be no greater than + // the length of the decimal representation of maxTxOutPerMessage, the + // maximum message payload may increase in the future and this + // optimization may go unnoticed, so allocate space for 10 decimal + // digits, which will fit any uint32. + buf := make([]byte, 2*HashSize+1, 2*HashSize+1+10) + copy(buf, o.Hash.String()) + buf[2*HashSize] = ':' + buf = strconv.AppendUint(buf, uint64(o.Index), 10) + return string(buf) +} + +// TxIn defines a bitcoin transaction input. +type TxIn struct { + PreviousOutPoint OutPoint + SignatureScript []byte + Sequence uint32 +} + +// SerializeSize returns the number of bytes it would take to serialize the +// the transaction input. +func (t *TxIn) SerializeSize() int { + // Outpoint Hash 32 bytes + Outpoint Index 4 bytes + Sequence 4 bytes + + // serialized varint size for the length of SignatureScript + + // SignatureScript bytes. + return 40 + VarIntSerializeSize(uint64(len(t.SignatureScript))) + + len(t.SignatureScript) +} + +// NewTxIn returns a new bitcoin transaction input with the provided +// previous outpoint point and signature script with a default sequence of +// MaxTxInSequenceNum. +func NewTxIn(prevOut *OutPoint, signatureScript []byte) *TxIn { + return &TxIn{ + PreviousOutPoint: *prevOut, + SignatureScript: signatureScript, + Sequence: MaxTxInSequenceNum, + } +} + +// TxOut defines a bitcoin transaction output. +type TxOut struct { + Value int64 + PkScript []byte +} + +// SerializeSize returns the number of bytes it would take to serialize the +// the transaction output. +func (t *TxOut) SerializeSize() int { + // Value 8 bytes + serialized varint size for the length of PkScript + + // PkScript bytes. + return 8 + VarIntSerializeSize(uint64(len(t.PkScript))) + len(t.PkScript) +} + +// NewTxOut returns a new bitcoin transaction output with the provided +// transaction value and public key script. +func NewTxOut(value int64, pkScript []byte) *TxOut { + return &TxOut{ + Value: value, + PkScript: pkScript, + } +} + +// MsgTx implements the Message interface and represents a bitcoin tx message. +// It is used to deliver transaction information in response to a getdata +// message (MsgGetData) for a given transaction. +// +// Use the AddTxIn and AddTxOut functions to build up the list of transaction +// inputs and outputs. +type MsgTx struct { + Version int32 + TxIn []*TxIn + TxOut []*TxOut + LockTime uint32 +} + +// AddTxIn adds a transaction input to the message. +func (msg *MsgTx) AddTxIn(ti *TxIn) { + msg.TxIn = append(msg.TxIn, ti) +} + +// AddTxOut adds a transaction output to the message. +func (msg *MsgTx) AddTxOut(to *TxOut) { + msg.TxOut = append(msg.TxOut, to) +} + +// TxSha generates the ShaHash name for the transaction. +func (msg *MsgTx) TxSha() ShaHash { + // Encode the transaction and calculate double sha256 on the result. + // Ignore the error returns since the only way the encode could fail + // is being out of memory or due to nil pointers, both of which would + // cause a run-time panic. + buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSize())) + _ = msg.Serialize(buf) + return DoubleSha256SH(buf.Bytes()) +} + +// Copy creates a deep copy of a transaction so that the original does not get +// modified when the copy is manipulated. +func (msg *MsgTx) Copy() *MsgTx { + // Create new tx and start by copying primitive values and making space + // for the transaction inputs and outputs. + newTx := MsgTx{ + Version: msg.Version, + TxIn: make([]*TxIn, 0, len(msg.TxIn)), + TxOut: make([]*TxOut, 0, len(msg.TxOut)), + LockTime: msg.LockTime, + } + + // Deep copy the old TxIn data. + for _, oldTxIn := range msg.TxIn { + // Deep copy the old previous outpoint. + oldOutPoint := oldTxIn.PreviousOutPoint + newOutPoint := OutPoint{} + newOutPoint.Hash.SetBytes(oldOutPoint.Hash[:]) + newOutPoint.Index = oldOutPoint.Index + + // Deep copy the old signature script. + var newScript []byte + oldScript := oldTxIn.SignatureScript + oldScriptLen := len(oldScript) + if oldScriptLen > 0 { + newScript = make([]byte, oldScriptLen, oldScriptLen) + copy(newScript, oldScript[:oldScriptLen]) + } + + // Create new txIn with the deep copied data and append it to + // new Tx. + newTxIn := TxIn{ + PreviousOutPoint: newOutPoint, + SignatureScript: newScript, + Sequence: oldTxIn.Sequence, + } + newTx.TxIn = append(newTx.TxIn, &newTxIn) + } + + // Deep copy the old TxOut data. + for _, oldTxOut := range msg.TxOut { + // Deep copy the old PkScript + var newScript []byte + oldScript := oldTxOut.PkScript + oldScriptLen := len(oldScript) + if oldScriptLen > 0 { + newScript = make([]byte, oldScriptLen, oldScriptLen) + copy(newScript, oldScript[:oldScriptLen]) + } + + // Create new txOut with the deep copied data and append it to + // new Tx. + newTxOut := TxOut{ + Value: oldTxOut.Value, + PkScript: newScript, + } + newTx.TxOut = append(newTx.TxOut, &newTxOut) + } + + return &newTx +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +// See Deserialize for decoding transactions stored to disk, such as in a +// database, as opposed to decoding transactions from the wire. +func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32) error { + var buf [4]byte + _, err := io.ReadFull(r, buf[:]) + if err != nil { + return err + } + msg.Version = int32(binary.LittleEndian.Uint32(buf[:])) + + count, err := ReadVarInt(r, pver) + if err != nil { + return err + } + + // Prevent more input transactions than could possibly fit into a + // message. It would be possible to cause memory exhaustion and panics + // without a sane upper bound on this count. + if count > uint64(maxTxInPerMessage) { + str := fmt.Sprintf("too many input transactions to fit into "+ + "max message size [count %d, max %d]", count, + maxTxInPerMessage) + return messageError("MsgTx.BtcDecode", str) + } + + msg.TxIn = make([]*TxIn, count) + for i := uint64(0); i < count; i++ { + ti := TxIn{} + err = readTxIn(r, pver, msg.Version, &ti) + if err != nil { + return err + } + msg.TxIn[i] = &ti + } + + count, err = ReadVarInt(r, pver) + if err != nil { + return err + } + + // Prevent more output transactions than could possibly fit into a + // message. It would be possible to cause memory exhaustion and panics + // without a sane upper bound on this count. + if count > uint64(maxTxOutPerMessage) { + str := fmt.Sprintf("too many output transactions to fit into "+ + "max message size [count %d, max %d]", count, + maxTxOutPerMessage) + return messageError("MsgTx.BtcDecode", str) + } + + msg.TxOut = make([]*TxOut, count) + for i := uint64(0); i < count; i++ { + to := TxOut{} + err = readTxOut(r, pver, msg.Version, &to) + if err != nil { + return err + } + msg.TxOut[i] = &to + } + + _, err = io.ReadFull(r, buf[:]) + if err != nil { + return err + } + msg.LockTime = binary.LittleEndian.Uint32(buf[:]) + + return nil +} + +// Deserialize decodes a transaction from r into the receiver using a format +// that is suitable for long-term storage such as a database while respecting +// the Version field in the transaction. This function differs from BtcDecode +// in that BtcDecode decodes from the bitcoin wire protocol as it was sent +// across the network. The wire encoding can technically differ depending on +// the protocol version and doesn't even really need to match the format of a +// stored transaction at all. As of the time this comment was written, the +// encoded transaction is the same in both instances, but there is a distinct +// difference and separating the two allows the API to be flexible enough to +// deal with changes. +func (msg *MsgTx) Deserialize(r io.Reader) error { + // At the current time, there is no difference between the wire encoding + // at protocol version 0 and the stable long-term storage format. As + // a result, make use of BtcDecode. + return msg.BtcDecode(r, 0) +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +// See Serialize for encoding transactions to be stored to disk, such as in a +// database, as opposed to encoding transactions for the wire. +func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32) error { + var buf [4]byte + binary.LittleEndian.PutUint32(buf[:], uint32(msg.Version)) + _, err := w.Write(buf[:]) + if err != nil { + return err + } + + count := uint64(len(msg.TxIn)) + err = WriteVarInt(w, pver, count) + if err != nil { + return err + } + + for _, ti := range msg.TxIn { + err = writeTxIn(w, pver, msg.Version, ti) + if err != nil { + return err + } + } + + count = uint64(len(msg.TxOut)) + err = WriteVarInt(w, pver, count) + if err != nil { + return err + } + + for _, to := range msg.TxOut { + err = writeTxOut(w, pver, msg.Version, to) + if err != nil { + return err + } + } + + binary.LittleEndian.PutUint32(buf[:], msg.LockTime) + _, err = w.Write(buf[:]) + if err != nil { + return err + } + + return nil +} + +// Serialize encodes the transaction to w using a format that suitable for +// long-term storage such as a database while respecting the Version field in +// the transaction. This function differs from BtcEncode in that BtcEncode +// encodes the transaction to the bitcoin wire protocol in order to be sent +// across the network. The wire encoding can technically differ depending on +// the protocol version and doesn't even really need to match the format of a +// stored transaction at all. As of the time this comment was written, the +// encoded transaction is the same in both instances, but there is a distinct +// difference and separating the two allows the API to be flexible enough to +// deal with changes. +func (msg *MsgTx) Serialize(w io.Writer) error { + // At the current time, there is no difference between the wire encoding + // at protocol version 0 and the stable long-term storage format. As + // a result, make use of BtcEncode. + return msg.BtcEncode(w, 0) + +} + +// SerializeSize returns the number of bytes it would take to serialize the +// the transaction. +func (msg *MsgTx) SerializeSize() int { + // Version 4 bytes + LockTime 4 bytes + Serialized varint size for the + // number of transaction inputs and outputs. + n := 8 + VarIntSerializeSize(uint64(len(msg.TxIn))) + + VarIntSerializeSize(uint64(len(msg.TxOut))) + + for _, txIn := range msg.TxIn { + n += txIn.SerializeSize() + } + + for _, txOut := range msg.TxOut { + n += txOut.SerializeSize() + } + + return n +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgTx) Command() string { + return CmdTx +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 { + return MaxBlockPayload +} + +// PkScriptLocs returns a slice containing the start of each public key script +// within the raw serialized transaction. The caller can easily obtain the +// length of each script by using len on the script available via the +// appropriate transaction output entry. +func (msg *MsgTx) PkScriptLocs() []int { + numTxOut := len(msg.TxOut) + if numTxOut == 0 { + return nil + } + + // The starting offset in the serialized transaction of the first + // transaction output is: + // + // Version 4 bytes + serialized varint size for the number of + // transaction inputs and outputs + serialized size of each transaction + // input. + n := 4 + VarIntSerializeSize(uint64(len(msg.TxIn))) + + VarIntSerializeSize(uint64(numTxOut)) + for _, txIn := range msg.TxIn { + n += txIn.SerializeSize() + } + + // Calculate and set the appropriate offset for each public key script. + pkScriptLocs := make([]int, numTxOut) + for i, txOut := range msg.TxOut { + // The offset of the script in the transaction output is: + // + // Value 8 bytes + serialized varint size for the length of + // PkScript. + n += 8 + VarIntSerializeSize(uint64(len(txOut.PkScript))) + pkScriptLocs[i] = n + n += len(txOut.PkScript) + } + + return pkScriptLocs +} + +// NewMsgTx returns a new bitcoin tx message that conforms to the Message +// interface. The return instance has a default version of TxVersion and there +// are no transaction inputs or outputs. Also, the lock time is set to zero +// to indicate the transaction is valid immediately as opposed to some time in +// future. +func NewMsgTx() *MsgTx { + return &MsgTx{ + Version: TxVersion, + TxIn: make([]*TxIn, 0, defaultTxInOutAlloc), + TxOut: make([]*TxOut, 0, defaultTxInOutAlloc), + } +} + +// readOutPoint reads the next sequence of bytes from r as an OutPoint. +func readOutPoint(r io.Reader, pver uint32, version int32, op *OutPoint) error { + _, err := io.ReadFull(r, op.Hash[:]) + if err != nil { + return err + } + + var buf [4]byte + _, err = io.ReadFull(r, buf[:]) + if err != nil { + return err + } + op.Index = binary.LittleEndian.Uint32(buf[:]) + return nil +} + +// writeOutPoint encodes op to the bitcoin protocol encoding for an OutPoint +// to w. +func writeOutPoint(w io.Writer, pver uint32, version int32, op *OutPoint) error { + _, err := w.Write(op.Hash[:]) + if err != nil { + return err + } + + var buf [4]byte + binary.LittleEndian.PutUint32(buf[:], op.Index) + _, err = w.Write(buf[:]) + if err != nil { + return err + } + return nil +} + +// readTxIn reads the next sequence of bytes from r as a transaction input +// (TxIn). +func readTxIn(r io.Reader, pver uint32, version int32, ti *TxIn) error { + var op OutPoint + err := readOutPoint(r, pver, version, &op) + if err != nil { + return err + } + ti.PreviousOutPoint = op + + ti.SignatureScript, err = ReadVarBytes(r, pver, MaxMessagePayload, + "transaction input signature script") + if err != nil { + return err + } + + var buf [4]byte + _, err = io.ReadFull(r, buf[:]) + if err != nil { + return err + } + ti.Sequence = binary.LittleEndian.Uint32(buf[:]) + + return nil +} + +// writeTxIn encodes ti to the bitcoin protocol encoding for a transaction +// input (TxIn) to w. +func writeTxIn(w io.Writer, pver uint32, version int32, ti *TxIn) error { + err := writeOutPoint(w, pver, version, &ti.PreviousOutPoint) + if err != nil { + return err + } + + err = WriteVarBytes(w, pver, ti.SignatureScript) + if err != nil { + return err + } + + var buf [4]byte + binary.LittleEndian.PutUint32(buf[:], ti.Sequence) + _, err = w.Write(buf[:]) + if err != nil { + return err + } + + return nil +} + +// readTxOut reads the next sequence of bytes from r as a transaction output +// (TxOut). +func readTxOut(r io.Reader, pver uint32, version int32, to *TxOut) error { + var buf [8]byte + _, err := io.ReadFull(r, buf[:]) + if err != nil { + return err + } + to.Value = int64(binary.LittleEndian.Uint64(buf[:])) + + to.PkScript, err = ReadVarBytes(r, pver, MaxMessagePayload, + "transaction output public key script") + if err != nil { + return err + } + + return nil +} + +// writeTxOut encodes to into the bitcoin protocol encoding for a transaction +// output (TxOut) to w. +func writeTxOut(w io.Writer, pver uint32, version int32, to *TxOut) error { + var buf [8]byte + binary.LittleEndian.PutUint64(buf[:], uint64(to.Value)) + _, err := w.Write(buf[:]) + if err != nil { + return err + } + + err = WriteVarBytes(w, pver, to.PkScript) + if err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgtx_test.go b/vendor/github.com/btcsuite/btcd/wire/msgtx_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8ecc5067f1f3b021df80f7f335afef1fc2b60981 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgtx_test.go @@ -0,0 +1,740 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "fmt" + "io" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestTx tests the MsgTx API. +func TestTx(t *testing.T) { + pver := wire.ProtocolVersion + + // Block 100000 hash. + hashStr := "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" + hash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Ensure the command is expected value. + wantCmd := "tx" + msg := wire.NewMsgTx() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgAddr: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value for latest protocol version. + // Num addresses (varInt) + max allowed addresses. + wantPayload := uint32(1000 * 1000) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Ensure we get the same transaction output point data back out. + // NOTE: This is a block hash and made up index, but we're only + // testing package functionality. + prevOutIndex := uint32(1) + prevOut := wire.NewOutPoint(hash, prevOutIndex) + if !prevOut.Hash.IsEqual(hash) { + t.Errorf("NewOutPoint: wrong hash - got %v, want %v", + spew.Sprint(&prevOut.Hash), spew.Sprint(hash)) + } + if prevOut.Index != prevOutIndex { + t.Errorf("NewOutPoint: wrong index - got %v, want %v", + prevOut.Index, prevOutIndex) + } + prevOutStr := fmt.Sprintf("%s:%d", hash.String(), prevOutIndex) + if s := prevOut.String(); s != prevOutStr { + t.Errorf("OutPoint.String: unexpected result - got %v, "+ + "want %v", s, prevOutStr) + } + + // Ensure we get the same transaction input back out. + sigScript := []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62} + txIn := wire.NewTxIn(prevOut, sigScript) + if !reflect.DeepEqual(&txIn.PreviousOutPoint, prevOut) { + t.Errorf("NewTxIn: wrong prev outpoint - got %v, want %v", + spew.Sprint(&txIn.PreviousOutPoint), + spew.Sprint(prevOut)) + } + if !bytes.Equal(txIn.SignatureScript, sigScript) { + t.Errorf("NewTxIn: wrong signature script - got %v, want %v", + spew.Sdump(txIn.SignatureScript), + spew.Sdump(sigScript)) + } + + // Ensure we get the same transaction output back out. + txValue := int64(5000000000) + pkScript := []byte{ + 0x41, // OP_DATA_65 + 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, + 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, + 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, + 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, + 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, + 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, + 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, + 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, + 0xa6, // 65-byte signature + 0xac, // OP_CHECKSIG + } + txOut := wire.NewTxOut(txValue, pkScript) + if txOut.Value != txValue { + t.Errorf("NewTxOut: wrong pk script - got %v, want %v", + txOut.Value, txValue) + + } + if !bytes.Equal(txOut.PkScript, pkScript) { + t.Errorf("NewTxOut: wrong pk script - got %v, want %v", + spew.Sdump(txOut.PkScript), + spew.Sdump(pkScript)) + } + + // Ensure transaction inputs are added properly. + msg.AddTxIn(txIn) + if !reflect.DeepEqual(msg.TxIn[0], txIn) { + t.Errorf("AddTxIn: wrong transaction input added - got %v, want %v", + spew.Sprint(msg.TxIn[0]), spew.Sprint(txIn)) + } + + // Ensure transaction outputs are added properly. + msg.AddTxOut(txOut) + if !reflect.DeepEqual(msg.TxOut[0], txOut) { + t.Errorf("AddTxIn: wrong transaction output added - got %v, want %v", + spew.Sprint(msg.TxOut[0]), spew.Sprint(txOut)) + } + + // Ensure the copy produced an identical transaction message. + newMsg := msg.Copy() + if !reflect.DeepEqual(newMsg, msg) { + t.Errorf("Copy: mismatched tx messages - got %v, want %v", + spew.Sdump(newMsg), spew.Sdump(msg)) + } + + return +} + +// TestTxSha tests the ability to generate the hash of a transaction accurately. +func TestTxSha(t *testing.T) { + // Hash of first transaction from block 113875. + hashStr := "f051e59b5e2503ac626d03aaeac8ab7be2d72ba4b7e97119c5852d70d52dcb86" + wantHash, err := wire.NewShaHashFromStr(hashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + return + } + + // First transaction from block 113875. + msgTx := wire.NewMsgTx() + txIn := wire.TxIn{ + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}, + Sequence: 0xffffffff, + } + txOut := wire.TxOut{ + Value: 5000000000, + PkScript: []byte{ + 0x41, // OP_DATA_65 + 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, + 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, + 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, + 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, + 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, + 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, + 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, + 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, + 0xa6, // 65-byte signature + 0xac, // OP_CHECKSIG + }, + } + msgTx.AddTxIn(&txIn) + msgTx.AddTxOut(&txOut) + msgTx.LockTime = 0 + + // Ensure the hash produced is expected. + txHash := msgTx.TxSha() + if !txHash.IsEqual(wantHash) { + t.Errorf("TxSha: wrong hash - got %v, want %v", + spew.Sprint(txHash), spew.Sprint(wantHash)) + } +} + +// TestTxWire tests the MsgTx wire encode and decode for various numbers +// of transaction inputs and outputs and protocol versions. +func TestTxWire(t *testing.T) { + // Empty tx message. + noTx := wire.NewMsgTx() + noTx.Version = 1 + noTxEncoded := []byte{ + 0x01, 0x00, 0x00, 0x00, // Version + 0x00, // Varint for number of input transactions + 0x00, // Varint for number of output transactions + 0x00, 0x00, 0x00, 0x00, // Lock time + } + + tests := []struct { + in *wire.MsgTx // Message to encode + out *wire.MsgTx // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version with no transactions. + { + noTx, + noTx, + noTxEncoded, + wire.ProtocolVersion, + }, + + // Latest protocol version with multiple transactions. + { + multiTx, + multiTx, + multiTxEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version with no transactions. + { + noTx, + noTx, + noTxEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0035Version with multiple transactions. + { + multiTx, + multiTx, + multiTxEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version with no transactions. + { + noTx, + noTx, + noTxEncoded, + wire.BIP0031Version, + }, + + // Protocol version BIP0031Version with multiple transactions. + { + multiTx, + multiTx, + multiTxEncoded, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion with no transactions. + { + noTx, + noTx, + noTxEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version NetAddressTimeVersion with multiple transactions. + { + multiTx, + multiTx, + multiTxEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion with no transactions. + { + noTx, + noTx, + noTxEncoded, + wire.MultipleAddressVersion, + }, + + // Protocol version MultipleAddressVersion with multiple transactions. + { + multiTx, + multiTx, + multiTxEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgTx + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(&msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestTxWireErrors performs negative tests against wire encode and decode +// of MsgTx to confirm error paths work correctly. +func TestTxWireErrors(t *testing.T) { + // Use protocol version 60002 specifically here instead of the latest + // because the test data is using bytes encoded with that protocol + // version. + pver := uint32(60002) + + tests := []struct { + in *wire.MsgTx // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error in version. + {multiTx, multiTxEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in number of transaction inputs. + {multiTx, multiTxEncoded, pver, 4, io.ErrShortWrite, io.EOF}, + // Force error in transaction input previous block hash. + {multiTx, multiTxEncoded, pver, 5, io.ErrShortWrite, io.EOF}, + // Force error in transaction input previous block output index. + {multiTx, multiTxEncoded, pver, 37, io.ErrShortWrite, io.EOF}, + // Force error in transaction input signature script length. + {multiTx, multiTxEncoded, pver, 41, io.ErrShortWrite, io.EOF}, + // Force error in transaction input signature script. + {multiTx, multiTxEncoded, pver, 42, io.ErrShortWrite, io.EOF}, + // Force error in transaction input sequence. + {multiTx, multiTxEncoded, pver, 49, io.ErrShortWrite, io.EOF}, + // Force error in number of transaction outputs. + {multiTx, multiTxEncoded, pver, 53, io.ErrShortWrite, io.EOF}, + // Force error in transaction output value. + {multiTx, multiTxEncoded, pver, 54, io.ErrShortWrite, io.EOF}, + // Force error in transaction output pk script length. + {multiTx, multiTxEncoded, pver, 62, io.ErrShortWrite, io.EOF}, + // Force error in transaction output pk script. + {multiTx, multiTxEncoded, pver, 63, io.ErrShortWrite, io.EOF}, + // Force error in transaction output lock time. + {multiTx, multiTxEncoded, pver, 206, io.ErrShortWrite, io.EOF}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // Decode from wire format. + var msg wire.MsgTx + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + } +} + +// TestTxSerialize tests MsgTx serialize and deserialize. +func TestTxSerialize(t *testing.T) { + noTx := wire.NewMsgTx() + noTx.Version = 1 + noTxEncoded := []byte{ + 0x01, 0x00, 0x00, 0x00, // Version + 0x00, // Varint for number of input transactions + 0x00, // Varint for number of output transactions + 0x00, 0x00, 0x00, 0x00, // Lock time + } + + tests := []struct { + in *wire.MsgTx // Message to encode + out *wire.MsgTx // Expected decoded message + buf []byte // Serialized data + pkScriptLocs []int // Expected output script locations + }{ + // No transactions. + { + noTx, + noTx, + noTxEncoded, + nil, + }, + + // Multiple transactions. + { + multiTx, + multiTx, + multiTxEncoded, + multiTxPkScriptLocs, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Serialize the transaction. + var buf bytes.Buffer + err := test.in.Serialize(&buf) + if err != nil { + t.Errorf("Serialize #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("Serialize #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Deserialize the transaction. + var tx wire.MsgTx + rbuf := bytes.NewReader(test.buf) + err = tx.Deserialize(rbuf) + if err != nil { + t.Errorf("Deserialize #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&tx, test.out) { + t.Errorf("Deserialize #%d\n got: %s want: %s", i, + spew.Sdump(&tx), spew.Sdump(test.out)) + continue + } + + // Ensure the public key script locations are accurate. + pkScriptLocs := test.in.PkScriptLocs() + if !reflect.DeepEqual(pkScriptLocs, test.pkScriptLocs) { + t.Errorf("PkScriptLocs #%d\n got: %s want: %s", i, + spew.Sdump(pkScriptLocs), + spew.Sdump(test.pkScriptLocs)) + continue + } + for j, loc := range pkScriptLocs { + wantPkScript := test.in.TxOut[j].PkScript + gotPkScript := test.buf[loc : loc+len(wantPkScript)] + if !bytes.Equal(gotPkScript, wantPkScript) { + t.Errorf("PkScriptLocs #%d:%d\n unexpected "+ + "script got: %s want: %s", i, j, + spew.Sdump(gotPkScript), + spew.Sdump(wantPkScript)) + } + } + } +} + +// TestTxSerializeErrors performs negative tests against wire encode and decode +// of MsgTx to confirm error paths work correctly. +func TestTxSerializeErrors(t *testing.T) { + tests := []struct { + in *wire.MsgTx // Value to encode + buf []byte // Serialized data + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error in version. + {multiTx, multiTxEncoded, 0, io.ErrShortWrite, io.EOF}, + // Force error in number of transaction inputs. + {multiTx, multiTxEncoded, 4, io.ErrShortWrite, io.EOF}, + // Force error in transaction input previous block hash. + {multiTx, multiTxEncoded, 5, io.ErrShortWrite, io.EOF}, + // Force error in transaction input previous block output index. + {multiTx, multiTxEncoded, 37, io.ErrShortWrite, io.EOF}, + // Force error in transaction input signature script length. + {multiTx, multiTxEncoded, 41, io.ErrShortWrite, io.EOF}, + // Force error in transaction input signature script. + {multiTx, multiTxEncoded, 42, io.ErrShortWrite, io.EOF}, + // Force error in transaction input sequence. + {multiTx, multiTxEncoded, 49, io.ErrShortWrite, io.EOF}, + // Force error in number of transaction outputs. + {multiTx, multiTxEncoded, 53, io.ErrShortWrite, io.EOF}, + // Force error in transaction output value. + {multiTx, multiTxEncoded, 54, io.ErrShortWrite, io.EOF}, + // Force error in transaction output pk script length. + {multiTx, multiTxEncoded, 62, io.ErrShortWrite, io.EOF}, + // Force error in transaction output pk script. + {multiTx, multiTxEncoded, 63, io.ErrShortWrite, io.EOF}, + // Force error in transaction output lock time. + {multiTx, multiTxEncoded, 206, io.ErrShortWrite, io.EOF}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Serialize the transaction. + w := newFixedWriter(test.max) + err := test.in.Serialize(w) + if err != test.writeErr { + t.Errorf("Serialize #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // Deserialize the transaction. + var tx wire.MsgTx + r := newFixedReader(test.max, test.buf) + err = tx.Deserialize(r) + if err != test.readErr { + t.Errorf("Deserialize #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + } +} + +// TestTxOverflowErrors performs tests to ensure deserializing transactions +// which are intentionally crafted to use large values for the variable number +// of inputs and outputs are handled properly. This could otherwise potentially +// be used as an attack vector. +func TestTxOverflowErrors(t *testing.T) { + // Use protocol version 70001 and transaction version 1 specifically + // here instead of the latest values because the test data is using + // bytes encoded with those versions. + pver := uint32(70001) + txVer := uint32(1) + + tests := []struct { + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + version uint32 // Transaction version + err error // Expected error + }{ + // Transaction that claims to have ~uint64(0) inputs. + { + []byte{ + 0x00, 0x00, 0x00, 0x01, // Version + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, // Varint for number of input transactions + }, pver, txVer, &wire.MessageError{}, + }, + + // Transaction that claims to have ~uint64(0) outputs. + { + []byte{ + 0x00, 0x00, 0x00, 0x01, // Version + 0x00, // Varint for number of input transactions + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, // Varint for number of output transactions + }, pver, txVer, &wire.MessageError{}, + }, + + // Transaction that has an input with a signature script that + // claims to have ~uint64(0) length. + { + []byte{ + 0x00, 0x00, 0x00, 0x01, // Version + 0x01, // Varint for number of input transactions + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash + 0xff, 0xff, 0xff, 0xff, // Prevous output index + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, // Varint for length of signature script + }, pver, txVer, &wire.MessageError{}, + }, + + // Transaction that has an output with a public key script + // that claims to have ~uint64(0) length. + { + []byte{ + 0x00, 0x00, 0x00, 0x01, // Version + 0x01, // Varint for number of input transactions + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash + 0xff, 0xff, 0xff, 0xff, // Prevous output index + 0x00, // Varint for length of signature script + 0xff, 0xff, 0xff, 0xff, // Sequence + 0x01, // Varint for number of output transactions + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Transaction amount + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, // Varint for length of public key script + }, pver, txVer, &wire.MessageError{}, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Decode from wire format. + var msg wire.MsgTx + r := bytes.NewReader(test.buf) + err := msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, reflect.TypeOf(test.err)) + continue + } + + // Decode from wire format. + r = bytes.NewReader(test.buf) + err = msg.Deserialize(r) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("Deserialize #%d wrong error got: %v, want: %v", + i, err, reflect.TypeOf(test.err)) + continue + } + } +} + +// TestTxSerializeSize performs tests to ensure the serialize size for various +// transactions is accurate. +func TestTxSerializeSize(t *testing.T) { + // Empty tx message. + noTx := wire.NewMsgTx() + noTx.Version = 1 + + tests := []struct { + in *wire.MsgTx // Tx to encode + size int // Expected serialized size + }{ + // No inputs or outpus. + {noTx, 10}, + + // Transcaction with an input and an output. + {multiTx, 210}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + serializedSize := test.in.SerializeSize() + if serializedSize != test.size { + t.Errorf("MsgTx.SerializeSize: #%d got: %d, want: %d", i, + serializedSize, test.size) + continue + } + } +} + +// multiTx is a MsgTx with an input and output and used in various tests. +var multiTx = &wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: wire.ShaHash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{ + 0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62, + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 0x12a05f200, + PkScript: []byte{ + 0x41, // OP_DATA_65 + 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, + 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, + 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, + 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, + 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, + 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, + 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, + 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, + 0xa6, // 65-byte signature + 0xac, // OP_CHECKSIG + }, + }, + { + Value: 0x5f5e100, + PkScript: []byte{ + 0x41, // OP_DATA_65 + 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, + 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, + 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, + 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, + 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, + 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, + 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, + 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, + 0xa6, // 65-byte signature + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 0, +} + +// multiTxEncoded is the wire encoded bytes for multiTx using protocol version +// 60002 and is used in the various tests. +var multiTxEncoded = []byte{ + 0x01, 0x00, 0x00, 0x00, // Version + 0x01, // Varint for number of input transactions + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash + 0xff, 0xff, 0xff, 0xff, // Prevous output index + 0x07, // Varint for length of signature script + 0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62, // Signature script + 0xff, 0xff, 0xff, 0xff, // Sequence + 0x02, // Varint for number of output transactions + 0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount + 0x43, // Varint for length of pk script + 0x41, // OP_DATA_65 + 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, + 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, + 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, + 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, + 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, + 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, + 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, + 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, + 0xa6, // 65-byte signature + 0xac, // OP_CHECKSIG + 0x00, 0xe1, 0xf5, 0x05, 0x00, 0x00, 0x00, 0x00, // Transaction amount + 0x43, // Varint for length of pk script + 0x41, // OP_DATA_65 + 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, + 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, + 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, + 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, + 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, + 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, + 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, + 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, + 0xa6, // 65-byte signature + 0xac, // OP_CHECKSIG + 0x00, 0x00, 0x00, 0x00, // Lock time +} + +// multiTxPkScriptLocs is the location information for the public key scripts +// located in multiTx. +var multiTxPkScriptLocs = []int{63, 139} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgverack.go b/vendor/github.com/btcsuite/btcd/wire/msgverack.go new file mode 100644 index 0000000000000000000000000000000000000000..6d89e61aaaeb2ce2c181a645458f015725bc5272 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgverack.go @@ -0,0 +1,46 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "io" +) + +// MsgVerAck defines a bitcoin verack message which is used for a peer to +// acknowledge a version message (MsgVersion) after it has used the information +// to negotiate parameters. It implements the Message interface. +// +// This message has no payload. +type MsgVerAck struct{} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgVerAck) BtcDecode(r io.Reader, pver uint32) error { + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgVerAck) BtcEncode(w io.Writer, pver uint32) error { + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgVerAck) Command() string { + return CmdVerAck +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgVerAck) MaxPayloadLength(pver uint32) uint32 { + return 0 +} + +// NewMsgVerAck returns a new bitcoin verack message that conforms to the +// Message interface. +func NewMsgVerAck() *MsgVerAck { + return &MsgVerAck{} +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgverack_test.go b/vendor/github.com/btcsuite/btcd/wire/msgverack_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3f02898f127d3cc91bc538980e9d15a2a8690f23 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgverack_test.go @@ -0,0 +1,122 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestVerAck tests the MsgVerAck API. +func TestVerAck(t *testing.T) { + pver := wire.ProtocolVersion + + // Ensure the command is expected value. + wantCmd := "verack" + msg := wire.NewMsgVerAck() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgVerAck: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value. + wantPayload := uint32(0) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + return +} + +// TestVerAckWire tests the MsgVerAck wire encode and decode for various +// protocol versions. +func TestVerAckWire(t *testing.T) { + msgVerAck := wire.NewMsgVerAck() + msgVerAckEncoded := []byte{} + + tests := []struct { + in *wire.MsgVerAck // Message to encode + out *wire.MsgVerAck // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + msgVerAck, + msgVerAck, + msgVerAckEncoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0035Version. + { + msgVerAck, + msgVerAck, + msgVerAckEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version. + { + msgVerAck, + msgVerAck, + msgVerAckEncoded, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion. + { + msgVerAck, + msgVerAck, + msgVerAckEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion. + { + msgVerAck, + msgVerAck, + msgVerAckEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgVerAck + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgversion.go b/vendor/github.com/btcsuite/btcd/wire/msgversion.go new file mode 100644 index 0000000000000000000000000000000000000000..6fd3fc6b0a0681263cf3c0e17b6f57637ae13230 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgversion.go @@ -0,0 +1,292 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "bytes" + "fmt" + "io" + "net" + "strings" + "time" +) + +// MaxUserAgentLen is the maximum allowed length for the user agent field in a +// version message (MsgVersion). +const MaxUserAgentLen = 2000 + +// DefaultUserAgent for wire in the stack +const DefaultUserAgent = "/btcwire:0.4.0/" + +// MsgVersion implements the Message interface and represents a bitcoin version +// message. It is used for a peer to advertise itself as soon as an outbound +// connection is made. The remote peer then uses this information along with +// its own to negotiate. The remote peer must then respond with a version +// message of its own containing the negotiated values followed by a verack +// message (MsgVerAck). This exchange must take place before any further +// communication is allowed to proceed. +type MsgVersion struct { + // Version of the protocol the node is using. + ProtocolVersion int32 + + // Bitfield which identifies the enabled services. + Services ServiceFlag + + // Time the message was generated. This is encoded as an int64 on the wire. + Timestamp time.Time + + // Address of the remote peer. + AddrYou NetAddress + + // Address of the local peer. + AddrMe NetAddress + + // Unique value associated with message that is used to detect self + // connections. + Nonce uint64 + + // The user agent that generated messsage. This is a encoded as a varString + // on the wire. This has a max length of MaxUserAgentLen. + UserAgent string + + // Last block seen by the generator of the version message. + LastBlock int32 + + // Don't announce transactions to peer. + DisableRelayTx bool +} + +// HasService returns whether the specified service is supported by the peer +// that generated the message. +func (msg *MsgVersion) HasService(service ServiceFlag) bool { + return msg.Services&service == service +} + +// AddService adds service as a supported service by the peer generating the +// message. +func (msg *MsgVersion) AddService(service ServiceFlag) { + msg.Services |= service +} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// The version message is special in that the protocol version hasn't been +// negotiated yet. As a result, the pver field is ignored and any fields which +// are added in new versions are optional. This also mean that r must be a +// *bytes.Buffer so the number of remaining bytes can be ascertained. +// +// This is part of the Message interface implementation. +func (msg *MsgVersion) BtcDecode(r io.Reader, pver uint32) error { + buf, ok := r.(*bytes.Buffer) + if !ok { + return fmt.Errorf("MsgVersion.BtcDecode reader is not a " + + "*bytes.Buffer") + } + + var sec int64 + err := readElements(buf, &msg.ProtocolVersion, &msg.Services, &sec) + if err != nil { + return err + } + msg.Timestamp = time.Unix(sec, 0) + + err = readNetAddress(buf, pver, &msg.AddrYou, false) + if err != nil { + return err + } + + // Protocol versions >= 106 added a from address, nonce, and user agent + // field and they are only considered present if there are bytes + // remaining in the message. + if buf.Len() > 0 { + err = readNetAddress(buf, pver, &msg.AddrMe, false) + if err != nil { + return err + } + } + if buf.Len() > 0 { + err = readElement(buf, &msg.Nonce) + if err != nil { + return err + } + } + if buf.Len() > 0 { + userAgent, err := ReadVarString(buf, pver) + if err != nil { + return err + } + err = validateUserAgent(userAgent) + if err != nil { + return err + } + msg.UserAgent = userAgent + } + + // Protocol versions >= 209 added a last known block field. It is only + // considered present if there are bytes remaining in the message. + if buf.Len() > 0 { + err = readElement(buf, &msg.LastBlock) + if err != nil { + return err + } + } + + // There was no relay transactions field before BIP0037Version, but + // the default behavior prior to the addition of the field was to always + // relay transactions. + if buf.Len() > 0 { + // It's safe to ignore the error here since the buffer has at + // least one byte and that byte will result in a boolean value + // regardless of its value. Also, the wire encoding for the + // field is true when transactions should be relayed, so reverse + // it for the DisableRelayTx field. + var relayTx bool + readElement(r, &relayTx) + msg.DisableRelayTx = !relayTx + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgVersion) BtcEncode(w io.Writer, pver uint32) error { + err := validateUserAgent(msg.UserAgent) + if err != nil { + return err + } + + err = writeElements(w, msg.ProtocolVersion, msg.Services, + msg.Timestamp.Unix()) + if err != nil { + return err + } + + err = writeNetAddress(w, pver, &msg.AddrYou, false) + if err != nil { + return err + } + + err = writeNetAddress(w, pver, &msg.AddrMe, false) + if err != nil { + return err + } + + err = writeElement(w, msg.Nonce) + if err != nil { + return err + } + + err = WriteVarString(w, pver, msg.UserAgent) + if err != nil { + return err + } + + err = writeElement(w, msg.LastBlock) + if err != nil { + return err + } + + // There was no relay transactions field before BIP0037Version. Also, + // the wire encoding for the field is true when transactions should be + // relayed, so reverse it from the DisableRelayTx field. + if pver >= BIP0037Version { + err = writeElement(w, !msg.DisableRelayTx) + if err != nil { + return err + } + } + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgVersion) Command() string { + return CmdVersion +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgVersion) MaxPayloadLength(pver uint32) uint32 { + // XXX: <= 106 different + + // Protocol version 4 bytes + services 8 bytes + timestamp 8 bytes + + // remote and local net addresses + nonce 8 bytes + length of user + // agent (varInt) + max allowed useragent length + last block 4 bytes + + // relay transactions flag 1 byte. + return 33 + (maxNetAddressPayload(pver) * 2) + MaxVarIntPayload + + MaxUserAgentLen +} + +// NewMsgVersion returns a new bitcoin version message that conforms to the +// Message interface using the passed parameters and defaults for the remaining +// fields. +func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64, + lastBlock int32) *MsgVersion { + + // Limit the timestamp to one second precision since the protocol + // doesn't support better. + return &MsgVersion{ + ProtocolVersion: int32(ProtocolVersion), + Services: 0, + Timestamp: time.Unix(time.Now().Unix(), 0), + AddrYou: *you, + AddrMe: *me, + Nonce: nonce, + UserAgent: DefaultUserAgent, + LastBlock: lastBlock, + DisableRelayTx: false, + } +} + +// NewMsgVersionFromConn is a convenience function that extracts the remote +// and local address from conn and returns a new bitcoin version message that +// conforms to the Message interface. See NewMsgVersion. +func NewMsgVersionFromConn(conn net.Conn, nonce uint64, + lastBlock int32) (*MsgVersion, error) { + + // Don't assume any services until we know otherwise. + lna, err := NewNetAddress(conn.LocalAddr(), 0) + if err != nil { + return nil, err + } + + // Don't assume any services until we know otherwise. + rna, err := NewNetAddress(conn.RemoteAddr(), 0) + if err != nil { + return nil, err + } + + return NewMsgVersion(lna, rna, nonce, lastBlock), nil +} + +// validateUserAgent checks userAgent length against MaxUserAgentLen +func validateUserAgent(userAgent string) error { + if len(userAgent) > MaxUserAgentLen { + str := fmt.Sprintf("user agent too long [len %v, max %v]", + len(userAgent), MaxUserAgentLen) + return messageError("MsgVersion", str) + } + return nil +} + +// AddUserAgent adds a user agent to the user agent string for the version +// message. The version string is not defined to any strict format, although +// it is recommended to use the form "major.minor.revision" e.g. "2.6.41". +func (msg *MsgVersion) AddUserAgent(name string, version string, + comments ...string) error { + + newUserAgent := fmt.Sprintf("%s:%s", name, version) + if len(comments) != 0 { + newUserAgent = fmt.Sprintf("%s(%s)", newUserAgent, + strings.Join(comments, "; ")) + } + newUserAgent = fmt.Sprintf("%s%s/", msg.UserAgent, newUserAgent) + err := validateUserAgent(newUserAgent) + if err != nil { + return err + } + msg.UserAgent = newUserAgent + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/wire/msgversion_test.go b/vendor/github.com/btcsuite/btcd/wire/msgversion_test.go new file mode 100644 index 0000000000000000000000000000000000000000..37f64b8371758935f64a3a59047a50aade8fc2bd --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/msgversion_test.go @@ -0,0 +1,596 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "net" + "reflect" + "strings" + "testing" + "time" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestVersion tests the MsgVersion API. +func TestVersion(t *testing.T) { + pver := wire.ProtocolVersion + + // Create version message data. + lastBlock := int32(234234) + tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333} + me, err := wire.NewNetAddress(tcpAddrMe, wire.SFNodeNetwork) + if err != nil { + t.Errorf("NewNetAddress: %v", err) + } + tcpAddrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333} + you, err := wire.NewNetAddress(tcpAddrYou, wire.SFNodeNetwork) + if err != nil { + t.Errorf("NewNetAddress: %v", err) + } + nonce, err := wire.RandomUint64() + if err != nil { + t.Errorf("RandomUint64: error generating nonce: %v", err) + } + + // Ensure we get the correct data back out. + msg := wire.NewMsgVersion(me, you, nonce, lastBlock) + if msg.ProtocolVersion != int32(pver) { + t.Errorf("NewMsgVersion: wrong protocol version - got %v, want %v", + msg.ProtocolVersion, pver) + } + if !reflect.DeepEqual(&msg.AddrMe, me) { + t.Errorf("NewMsgVersion: wrong me address - got %v, want %v", + spew.Sdump(&msg.AddrMe), spew.Sdump(me)) + } + if !reflect.DeepEqual(&msg.AddrYou, you) { + t.Errorf("NewMsgVersion: wrong you address - got %v, want %v", + spew.Sdump(&msg.AddrYou), spew.Sdump(you)) + } + if msg.Nonce != nonce { + t.Errorf("NewMsgVersion: wrong nonce - got %v, want %v", + msg.Nonce, nonce) + } + if msg.UserAgent != wire.DefaultUserAgent { + t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v", + msg.UserAgent, wire.DefaultUserAgent) + } + if msg.LastBlock != lastBlock { + t.Errorf("NewMsgVersion: wrong last block - got %v, want %v", + msg.LastBlock, lastBlock) + } + if msg.DisableRelayTx != false { + t.Errorf("NewMsgVersion: disable relay tx is not false by "+ + "default - got %v, want %v", msg.DisableRelayTx, false) + } + + msg.AddUserAgent("myclient", "1.2.3", "optional", "comments") + customUserAgent := wire.DefaultUserAgent + "myclient:1.2.3(optional; comments)/" + if msg.UserAgent != customUserAgent { + t.Errorf("AddUserAgent: wrong user agent - got %s, want %s", + msg.UserAgent, customUserAgent) + } + + msg.AddUserAgent("mygui", "3.4.5") + customUserAgent += "mygui:3.4.5/" + if msg.UserAgent != customUserAgent { + t.Errorf("AddUserAgent: wrong user agent - got %s, want %s", + msg.UserAgent, customUserAgent) + } + + // accounting for ":", "/" + err = msg.AddUserAgent(strings.Repeat("t", + wire.MaxUserAgentLen-len(customUserAgent)-2+1), "") + if _, ok := err.(*wire.MessageError); !ok { + t.Errorf("AddUserAgent: expected error not received "+ + "- got %v, want %T", err, wire.MessageError{}) + + } + + // Version message should not have any services set by default. + if msg.Services != 0 { + t.Errorf("NewMsgVersion: wrong default services - got %v, want %v", + msg.Services, 0) + + } + if msg.HasService(wire.SFNodeNetwork) { + t.Errorf("HasService: SFNodeNetwork service is set") + } + + // Ensure the command is expected value. + wantCmd := "version" + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgVersion: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value. + // Protocol version 4 bytes + services 8 bytes + timestamp 8 bytes + + // remote and local net addresses + nonce 8 bytes + length of user agent + // (varInt) + max allowed user agent length + last block 4 bytes + + // relay transactions flag 1 byte. + wantPayload := uint32(2102) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Ensure adding the full service node flag works. + msg.AddService(wire.SFNodeNetwork) + if msg.Services != wire.SFNodeNetwork { + t.Errorf("AddService: wrong services - got %v, want %v", + msg.Services, wire.SFNodeNetwork) + } + if !msg.HasService(wire.SFNodeNetwork) { + t.Errorf("HasService: SFNodeNetwork service not set") + } + + // Use a fake connection. + conn := &fakeConn{localAddr: tcpAddrMe, remoteAddr: tcpAddrYou} + msg, err = wire.NewMsgVersionFromConn(conn, nonce, lastBlock) + if err != nil { + t.Errorf("NewMsgVersionFromConn: %v", err) + } + + // Ensure we get the correct connection data back out. + if !msg.AddrMe.IP.Equal(tcpAddrMe.IP) { + t.Errorf("NewMsgVersionFromConn: wrong me ip - got %v, want %v", + msg.AddrMe.IP, tcpAddrMe.IP) + } + if !msg.AddrYou.IP.Equal(tcpAddrYou.IP) { + t.Errorf("NewMsgVersionFromConn: wrong you ip - got %v, want %v", + msg.AddrYou.IP, tcpAddrYou.IP) + } + + // Use a fake connection with local UDP addresses to force a failure. + conn = &fakeConn{ + localAddr: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}, + remoteAddr: tcpAddrYou, + } + msg, err = wire.NewMsgVersionFromConn(conn, nonce, lastBlock) + if err != wire.ErrInvalidNetAddr { + t.Errorf("NewMsgVersionFromConn: expected error not received "+ + "- got %v, want %v", err, wire.ErrInvalidNetAddr) + } + + // Use a fake connection with remote UDP addresses to force a failure. + conn = &fakeConn{ + localAddr: tcpAddrMe, + remoteAddr: &net.UDPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333}, + } + msg, err = wire.NewMsgVersionFromConn(conn, nonce, lastBlock) + if err != wire.ErrInvalidNetAddr { + t.Errorf("NewMsgVersionFromConn: expected error not received "+ + "- got %v, want %v", err, wire.ErrInvalidNetAddr) + } + + return +} + +// TestVersionWire tests the MsgVersion wire encode and decode for various +// protocol versions. +func TestVersionWire(t *testing.T) { + // verRelayTxFalse and verRelayTxFalseEncoded is a version message as of + // BIP0037Version with the transaction relay disabled. + baseVersionBIP0037Copy := *baseVersionBIP0037 + verRelayTxFalse := &baseVersionBIP0037Copy + verRelayTxFalse.DisableRelayTx = true + verRelayTxFalseEncoded := make([]byte, len(baseVersionBIP0037Encoded)) + copy(verRelayTxFalseEncoded, baseVersionBIP0037Encoded) + verRelayTxFalseEncoded[len(verRelayTxFalseEncoded)-1] = 0 + + tests := []struct { + in *wire.MsgVersion // Message to encode + out *wire.MsgVersion // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + baseVersionBIP0037, + baseVersionBIP0037, + baseVersionBIP0037Encoded, + wire.ProtocolVersion, + }, + + // Protocol version BIP0037Version with relay transactions field + // true. + { + baseVersionBIP0037, + baseVersionBIP0037, + baseVersionBIP0037Encoded, + wire.BIP0037Version, + }, + + // Protocol version BIP0037Version with relay transactions field + // false. + { + verRelayTxFalse, + verRelayTxFalse, + verRelayTxFalseEncoded, + wire.BIP0037Version, + }, + + // Protocol version BIP0035Version. + { + baseVersion, + baseVersion, + baseVersionEncoded, + wire.BIP0035Version, + }, + + // Protocol version BIP0031Version. + { + baseVersion, + baseVersion, + baseVersionEncoded, + wire.BIP0031Version, + }, + + // Protocol version NetAddressTimeVersion. + { + baseVersion, + baseVersion, + baseVersionEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version MultipleAddressVersion. + { + baseVersion, + baseVersion, + baseVersionEncoded, + wire.MultipleAddressVersion, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg wire.MsgVersion + rbuf := bytes.NewBuffer(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} + +// TestVersionWireErrors performs negative tests against wire encode and +// decode of MsgGetHeaders to confirm error paths work correctly. +func TestVersionWireErrors(t *testing.T) { + // Use protocol version 60002 specifically here instead of the latest + // because the test data is using bytes encoded with that protocol + // version. + pver := uint32(60002) + wireErr := &wire.MessageError{} + + // Ensure calling MsgVersion.BtcDecode with a non *bytes.Buffer returns + // error. + fr := newFixedReader(0, []byte{}) + if err := baseVersion.BtcDecode(fr, pver); err == nil { + t.Errorf("Did not received error when calling " + + "MsgVersion.BtcDecode with non *bytes.Buffer") + } + + // Copy the base version and change the user agent to exceed max limits. + bvc := *baseVersion + exceedUAVer := &bvc + newUA := "/" + strings.Repeat("t", wire.MaxUserAgentLen-8+1) + ":0.0.1/" + exceedUAVer.UserAgent = newUA + + // Encode the new UA length as a varint. + var newUAVarIntBuf bytes.Buffer + err := wire.TstWriteVarInt(&newUAVarIntBuf, pver, uint64(len(newUA))) + if err != nil { + t.Errorf("WriteVarInt: error %v", err) + } + + // Make a new buffer big enough to hold the base version plus the new + // bytes for the bigger varint to hold the new size of the user agent + // and the new user agent string. Then stich it all together. + newLen := len(baseVersionEncoded) - len(baseVersion.UserAgent) + newLen = newLen + len(newUAVarIntBuf.Bytes()) - 1 + len(newUA) + exceedUAVerEncoded := make([]byte, newLen) + copy(exceedUAVerEncoded, baseVersionEncoded[0:80]) + copy(exceedUAVerEncoded[80:], newUAVarIntBuf.Bytes()) + copy(exceedUAVerEncoded[83:], []byte(newUA)) + copy(exceedUAVerEncoded[83+len(newUA):], baseVersionEncoded[97:100]) + + tests := []struct { + in *wire.MsgVersion // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error in protocol version. + {baseVersion, baseVersionEncoded, pver, 0, io.ErrShortWrite, io.EOF}, + // Force error in services. + {baseVersion, baseVersionEncoded, pver, 4, io.ErrShortWrite, io.EOF}, + // Force error in timestamp. + {baseVersion, baseVersionEncoded, pver, 12, io.ErrShortWrite, io.EOF}, + // Force error in remote address. + {baseVersion, baseVersionEncoded, pver, 20, io.ErrShortWrite, io.EOF}, + // Force error in local address. + {baseVersion, baseVersionEncoded, pver, 47, io.ErrShortWrite, io.ErrUnexpectedEOF}, + // Force error in nonce. + {baseVersion, baseVersionEncoded, pver, 73, io.ErrShortWrite, io.ErrUnexpectedEOF}, + // Force error in user agent length. + {baseVersion, baseVersionEncoded, pver, 81, io.ErrShortWrite, io.EOF}, + // Force error in user agent. + {baseVersion, baseVersionEncoded, pver, 82, io.ErrShortWrite, io.ErrUnexpectedEOF}, + // Force error in last block. + {baseVersion, baseVersionEncoded, pver, 98, io.ErrShortWrite, io.ErrUnexpectedEOF}, + // Force error in relay tx - no read error should happen since + // it's optional. + { + baseVersionBIP0037, baseVersionBIP0037Encoded, + wire.BIP0037Version, 101, io.ErrShortWrite, nil, + }, + // Force error due to user agent too big + {exceedUAVer, exceedUAVerEncoded, pver, newLen, wireErr, wireErr}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := test.in.BtcEncode(w, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { + t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.writeErr { + t.Errorf("BtcEncode #%d wrong error got: %v, "+ + "want: %v", i, err, test.writeErr) + continue + } + } + + // Decode from wire format. + var msg wire.MsgVersion + buf := bytes.NewBuffer(test.buf[0:test.max]) + err = msg.BtcDecode(buf, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + + // For errors which are not of type wire.MessageError, check + // them for equality. + if _, ok := err.(*wire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + } +} + +// TestVersionOptionalFields performs tests to ensure that an encoded version +// messages that omit optional fields are handled correctly. +func TestVersionOptionalFields(t *testing.T) { + // onlyRequiredVersion is a version message that only contains the + // required versions and all other values set to their default values. + onlyRequiredVersion := wire.MsgVersion{ + ProtocolVersion: 60002, + Services: wire.SFNodeNetwork, + Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST) + AddrYou: wire.NetAddress{ + Timestamp: time.Time{}, // Zero value -- no timestamp in version + Services: wire.SFNodeNetwork, + IP: net.ParseIP("192.168.0.1"), + Port: 8333, + }, + } + onlyRequiredVersionEncoded := make([]byte, len(baseVersionEncoded)-55) + copy(onlyRequiredVersionEncoded, baseVersionEncoded) + + // addrMeVersion is a version message that contains all fields through + // the AddrMe field. + addrMeVersion := onlyRequiredVersion + addrMeVersion.AddrMe = wire.NetAddress{ + Timestamp: time.Time{}, // Zero value -- no timestamp in version + Services: wire.SFNodeNetwork, + IP: net.ParseIP("127.0.0.1"), + Port: 8333, + } + addrMeVersionEncoded := make([]byte, len(baseVersionEncoded)-29) + copy(addrMeVersionEncoded, baseVersionEncoded) + + // nonceVersion is a version message that contains all fields through + // the Nonce field. + nonceVersion := addrMeVersion + nonceVersion.Nonce = 123123 // 0x1e0f3 + nonceVersionEncoded := make([]byte, len(baseVersionEncoded)-21) + copy(nonceVersionEncoded, baseVersionEncoded) + + // uaVersion is a version message that contains all fields through + // the UserAgent field. + uaVersion := nonceVersion + uaVersion.UserAgent = "/btcdtest:0.0.1/" + uaVersionEncoded := make([]byte, len(baseVersionEncoded)-4) + copy(uaVersionEncoded, baseVersionEncoded) + + // lastBlockVersion is a version message that contains all fields + // through the LastBlock field. + lastBlockVersion := uaVersion + lastBlockVersion.LastBlock = 234234 // 0x392fa + lastBlockVersionEncoded := make([]byte, len(baseVersionEncoded)) + copy(lastBlockVersionEncoded, baseVersionEncoded) + + tests := []struct { + msg *wire.MsgVersion // Expected message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + { + &onlyRequiredVersion, + onlyRequiredVersionEncoded, + wire.ProtocolVersion, + }, + { + &addrMeVersion, + addrMeVersionEncoded, + wire.ProtocolVersion, + }, + { + &nonceVersion, + nonceVersionEncoded, + wire.ProtocolVersion, + }, + { + &uaVersion, + uaVersionEncoded, + wire.ProtocolVersion, + }, + { + &lastBlockVersion, + lastBlockVersionEncoded, + wire.ProtocolVersion, + }, + } + + for i, test := range tests { + // Decode the message from wire format. + var msg wire.MsgVersion + rbuf := bytes.NewBuffer(test.buf) + err := msg.BtcDecode(rbuf, test.pver) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.msg) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.msg)) + continue + } + } +} + +// baseVersion is used in the various tests as a baseline MsgVersion. +var baseVersion = &wire.MsgVersion{ + ProtocolVersion: 60002, + Services: wire.SFNodeNetwork, + Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST) + AddrYou: wire.NetAddress{ + Timestamp: time.Time{}, // Zero value -- no timestamp in version + Services: wire.SFNodeNetwork, + IP: net.ParseIP("192.168.0.1"), + Port: 8333, + }, + AddrMe: wire.NetAddress{ + Timestamp: time.Time{}, // Zero value -- no timestamp in version + Services: wire.SFNodeNetwork, + IP: net.ParseIP("127.0.0.1"), + Port: 8333, + }, + Nonce: 123123, // 0x1e0f3 + UserAgent: "/btcdtest:0.0.1/", + LastBlock: 234234, // 0x392fa +} + +// baseVersionEncoded is the wire encoded bytes for baseVersion using protocol +// version 60002 and is used in the various tests. +var baseVersionEncoded = []byte{ + 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork + 0x29, 0xab, 0x5f, 0x49, 0x00, 0x00, 0x00, 0x00, // 64-bit Timestamp + // AddrYou -- No timestamp for NetAddress in version message + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x01, // IP 192.168.0.1 + 0x20, 0x8d, // Port 8333 in big-endian + // AddrMe -- No timestamp for NetAddress in version message + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1 + 0x20, 0x8d, // Port 8333 in big-endian + 0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // Nonce + 0x10, // Varint for user agent length + 0x2f, 0x62, 0x74, 0x63, 0x64, 0x74, 0x65, 0x73, + 0x74, 0x3a, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x2f, // User agent + 0xfa, 0x92, 0x03, 0x00, // Last block +} + +// baseVersionBIP0037 is used in the various tests as a baseline MsgVersion for +// BIP0037. +var baseVersionBIP0037 = &wire.MsgVersion{ + ProtocolVersion: 70001, + Services: wire.SFNodeNetwork, + Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST) + AddrYou: wire.NetAddress{ + Timestamp: time.Time{}, // Zero value -- no timestamp in version + Services: wire.SFNodeNetwork, + IP: net.ParseIP("192.168.0.1"), + Port: 8333, + }, + AddrMe: wire.NetAddress{ + Timestamp: time.Time{}, // Zero value -- no timestamp in version + Services: wire.SFNodeNetwork, + IP: net.ParseIP("127.0.0.1"), + Port: 8333, + }, + Nonce: 123123, // 0x1e0f3 + UserAgent: "/btcdtest:0.0.1/", + LastBlock: 234234, // 0x392fa +} + +// baseVersionBIP0037Encoded is the wire encoded bytes for baseVersionBIP0037 +// using protocol version BIP0037Version and is used in the various tests. +var baseVersionBIP0037Encoded = []byte{ + 0x71, 0x11, 0x01, 0x00, // Protocol version 70001 + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork + 0x29, 0xab, 0x5f, 0x49, 0x00, 0x00, 0x00, 0x00, // 64-bit Timestamp + // AddrYou -- No timestamp for NetAddress in version message + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x01, // IP 192.168.0.1 + 0x20, 0x8d, // Port 8333 in big-endian + // AddrMe -- No timestamp for NetAddress in version message + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1 + 0x20, 0x8d, // Port 8333 in big-endian + 0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // Nonce + 0x10, // Varint for user agent length + 0x2f, 0x62, 0x74, 0x63, 0x64, 0x74, 0x65, 0x73, + 0x74, 0x3a, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x2f, // User agent + 0xfa, 0x92, 0x03, 0x00, // Last block + 0x01, // Relay tx +} diff --git a/vendor/github.com/btcsuite/btcd/wire/netaddress.go b/vendor/github.com/btcsuite/btcd/wire/netaddress.go new file mode 100644 index 0000000000000000000000000000000000000000..cf5a795605e9cb501b6d759170398b52511966be --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/netaddress.go @@ -0,0 +1,172 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "encoding/binary" + "errors" + "io" + "net" + "time" +) + +// ErrInvalidNetAddr describes an error that indicates the caller didn't specify +// a TCP address as required. +var ErrInvalidNetAddr = errors.New("provided net.Addr is not a net.TCPAddr") + +// maxNetAddressPayload returns the max payload size for a bitcoin NetAddress +// based on the protocol version. +func maxNetAddressPayload(pver uint32) uint32 { + // Services 8 bytes + ip 16 bytes + port 2 bytes. + plen := uint32(26) + + // NetAddressTimeVersion added a timestamp field. + if pver >= NetAddressTimeVersion { + // Timestamp 4 bytes. + plen += 4 + } + + return plen +} + +// NetAddress defines information about a peer on the network including the time +// it was last seen, the services it supports, its IP address, and port. +type NetAddress struct { + // Last time the address was seen. This is, unfortunately, encoded as a + // uint32 on the wire and therefore is limited to 2106. This field is + // not present in the bitcoin version message (MsgVersion) nor was it + // added until protocol version >= NetAddressTimeVersion. + Timestamp time.Time + + // Bitfield which identifies the services supported by the address. + Services ServiceFlag + + // IP address of the peer. + IP net.IP + + // Port the peer is using. This is encoded in big endian on the wire + // which differs from most everything else. + Port uint16 +} + +// HasService returns whether the specified service is supported by the address. +func (na *NetAddress) HasService(service ServiceFlag) bool { + if na.Services&service == service { + return true + } + return false +} + +// AddService adds service as a supported service by the peer generating the +// message. +func (na *NetAddress) AddService(service ServiceFlag) { + na.Services |= service +} + +// SetAddress is a convenience function to set the IP address and port in one +// call. +func (na *NetAddress) SetAddress(ip net.IP, port uint16) { + na.IP = ip + na.Port = port +} + +// NewNetAddressIPPort returns a new NetAddress using the provided IP, port, and +// supported services with defaults for the remaining fields. +func NewNetAddressIPPort(ip net.IP, port uint16, services ServiceFlag) *NetAddress { + // Limit the timestamp to one second precision since the protocol + // doesn't support better. + na := NetAddress{ + Timestamp: time.Unix(time.Now().Unix(), 0), + Services: services, + IP: ip, + Port: port, + } + return &na +} + +// NewNetAddress returns a new NetAddress using the provided TCP address and +// supported services with defaults for the remaining fields. +// +// Note that addr must be a net.TCPAddr. An ErrInvalidNetAddr is returned +// if it is not. +func NewNetAddress(addr net.Addr, services ServiceFlag) (*NetAddress, error) { + tcpAddr, ok := addr.(*net.TCPAddr) + if !ok { + return nil, ErrInvalidNetAddr + } + + na := NewNetAddressIPPort(tcpAddr.IP, uint16(tcpAddr.Port), services) + return na, nil +} + +// readNetAddress reads an encoded NetAddress from r depending on the protocol +// version and whether or not the timestamp is included per ts. Some messages +// like version do not include the timestamp. +func readNetAddress(r io.Reader, pver uint32, na *NetAddress, ts bool) error { + var timestamp time.Time + var services ServiceFlag + var ip [16]byte + var port uint16 + + // NOTE: The bitcoin protocol uses a uint32 for the timestamp so it will + // stop working somewhere around 2106. Also timestamp wasn't added until + // protocol version >= NetAddressTimeVersion + if ts && pver >= NetAddressTimeVersion { + var stamp uint32 + err := readElement(r, &stamp) + if err != nil { + return err + } + timestamp = time.Unix(int64(stamp), 0) + } + + err := readElements(r, &services, &ip) + if err != nil { + return err + } + // Sigh. Bitcoin protocol mixes little and big endian. + err = binary.Read(r, binary.BigEndian, &port) + if err != nil { + return err + } + + na.Timestamp = timestamp + na.Services = services + na.SetAddress(net.IP(ip[:]), port) + return nil +} + +// writeNetAddress serializes a NetAddress to w depending on the protocol +// version and whether or not the timestamp is included per ts. Some messages +// like version do not include the timestamp. +func writeNetAddress(w io.Writer, pver uint32, na *NetAddress, ts bool) error { + // NOTE: The bitcoin protocol uses a uint32 for the timestamp so it will + // stop working somewhere around 2106. Also timestamp wasn't added until + // until protocol version >= NetAddressTimeVersion. + if ts && pver >= NetAddressTimeVersion { + err := writeElement(w, uint32(na.Timestamp.Unix())) + if err != nil { + return err + } + } + + // Ensure to always write 16 bytes even if the ip is nil. + var ip [16]byte + if na.IP != nil { + copy(ip[:], na.IP.To16()) + } + err := writeElements(w, na.Services, ip) + if err != nil { + return err + } + + // Sigh. Bitcoin protocol mixes little and big endian. + err = binary.Write(w, binary.BigEndian, na.Port) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/btcsuite/btcd/wire/netaddress_test.go b/vendor/github.com/btcsuite/btcd/wire/netaddress_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a03c586f1086c9c7285c6a1eec47f9cad70af624 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/netaddress_test.go @@ -0,0 +1,294 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "io" + "net" + "reflect" + "testing" + "time" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestNetAddress tests the NetAddress API. +func TestNetAddress(t *testing.T) { + ip := net.ParseIP("127.0.0.1") + port := 8333 + + // Test NewNetAddress. + tcpAddr := &net.TCPAddr{ + IP: ip, + Port: port, + } + na, err := wire.NewNetAddress(tcpAddr, 0) + if err != nil { + t.Errorf("NewNetAddress: %v", err) + } + + // Ensure we get the same ip, port, and services back out. + if !na.IP.Equal(ip) { + t.Errorf("NetNetAddress: wrong ip - got %v, want %v", na.IP, ip) + } + if na.Port != uint16(port) { + t.Errorf("NetNetAddress: wrong port - got %v, want %v", na.Port, + port) + } + if na.Services != 0 { + t.Errorf("NetNetAddress: wrong services - got %v, want %v", + na.Services, 0) + } + if na.HasService(wire.SFNodeNetwork) { + t.Errorf("HasService: SFNodeNetwork service is set") + } + + // Ensure adding the full service node flag works. + na.AddService(wire.SFNodeNetwork) + if na.Services != wire.SFNodeNetwork { + t.Errorf("AddService: wrong services - got %v, want %v", + na.Services, wire.SFNodeNetwork) + } + if !na.HasService(wire.SFNodeNetwork) { + t.Errorf("HasService: SFNodeNetwork service not set") + } + + // Ensure max payload is expected value for latest protocol version. + pver := wire.ProtocolVersion + wantPayload := uint32(30) + maxPayload := wire.TstMaxNetAddressPayload(wire.ProtocolVersion) + if maxPayload != wantPayload { + t.Errorf("maxNetAddressPayload: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Protocol version before NetAddressTimeVersion when timestamp was + // added. Ensure max payload is expected value for it. + pver = wire.NetAddressTimeVersion - 1 + wantPayload = 26 + maxPayload = wire.TstMaxNetAddressPayload(pver) + if maxPayload != wantPayload { + t.Errorf("maxNetAddressPayload: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Check for expected failure on wrong address type. + udpAddr := &net.UDPAddr{} + _, err = wire.NewNetAddress(udpAddr, 0) + if err != wire.ErrInvalidNetAddr { + t.Errorf("NewNetAddress: expected error not received - "+ + "got %v, want %v", err, wire.ErrInvalidNetAddr) + } +} + +// TestNetAddressWire tests the NetAddress wire encode and decode for various +// protocol versions and timestamp flag combinations. +func TestNetAddressWire(t *testing.T) { + // baseNetAddr is used in the various tests as a baseline NetAddress. + baseNetAddr := wire.NetAddress{ + Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST + Services: wire.SFNodeNetwork, + IP: net.ParseIP("127.0.0.1"), + Port: 8333, + } + + // baseNetAddrNoTS is baseNetAddr with a zero value for the timestamp. + baseNetAddrNoTS := baseNetAddr + baseNetAddrNoTS.Timestamp = time.Time{} + + // baseNetAddrEncoded is the wire encoded bytes of baseNetAddr. + baseNetAddrEncoded := []byte{ + 0x29, 0xab, 0x5f, 0x49, // Timestamp + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1 + 0x20, 0x8d, // Port 8333 in big-endian + } + + // baseNetAddrNoTSEncoded is the wire encoded bytes of baseNetAddrNoTS. + baseNetAddrNoTSEncoded := []byte{ + // No timestamp + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1 + 0x20, 0x8d, // Port 8333 in big-endian + } + + tests := []struct { + in wire.NetAddress // NetAddress to encode + out wire.NetAddress // Expected decoded NetAddress + ts bool // Include timestamp? + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version without ts flag. + { + baseNetAddr, + baseNetAddrNoTS, + false, + baseNetAddrNoTSEncoded, + wire.ProtocolVersion, + }, + + // Latest protocol version with ts flag. + { + baseNetAddr, + baseNetAddr, + true, + baseNetAddrEncoded, + wire.ProtocolVersion, + }, + + // Protocol version NetAddressTimeVersion without ts flag. + { + baseNetAddr, + baseNetAddrNoTS, + false, + baseNetAddrNoTSEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version NetAddressTimeVersion with ts flag. + { + baseNetAddr, + baseNetAddr, + true, + baseNetAddrEncoded, + wire.NetAddressTimeVersion, + }, + + // Protocol version NetAddressTimeVersion-1 without ts flag. + { + baseNetAddr, + baseNetAddrNoTS, + false, + baseNetAddrNoTSEncoded, + wire.NetAddressTimeVersion - 1, + }, + + // Protocol version NetAddressTimeVersion-1 with timestamp. + // Even though the timestamp flag is set, this shouldn't have a + // timestamp since it is a protocol version before it was + // added. + { + baseNetAddr, + baseNetAddrNoTS, + true, + baseNetAddrNoTSEncoded, + wire.NetAddressTimeVersion - 1, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + var buf bytes.Buffer + err := wire.TstWriteNetAddress(&buf, test.pver, &test.in, test.ts) + if err != nil { + t.Errorf("writeNetAddress #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("writeNetAddress #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var na wire.NetAddress + rbuf := bytes.NewReader(test.buf) + err = wire.TstReadNetAddress(rbuf, test.pver, &na, test.ts) + if err != nil { + t.Errorf("readNetAddress #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(na, test.out) { + t.Errorf("readNetAddress #%d\n got: %s want: %s", i, + spew.Sdump(na), spew.Sdump(test.out)) + continue + } + } +} + +// TestNetAddressWireErrors performs negative tests against wire encode and +// decode NetAddress to confirm error paths work correctly. +func TestNetAddressWireErrors(t *testing.T) { + pver := wire.ProtocolVersion + pverNAT := wire.NetAddressTimeVersion - 1 + + // baseNetAddr is used in the various tests as a baseline NetAddress. + baseNetAddr := wire.NetAddress{ + Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST + Services: wire.SFNodeNetwork, + IP: net.ParseIP("127.0.0.1"), + Port: 8333, + } + + tests := []struct { + in *wire.NetAddress // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + ts bool // Include timestamp flag + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Latest protocol version with timestamp and intentional + // read/write errors. + // Force errors on timestamp. + {&baseNetAddr, []byte{}, pver, true, 0, io.ErrShortWrite, io.EOF}, + // Force errors on services. + {&baseNetAddr, []byte{}, pver, true, 4, io.ErrShortWrite, io.EOF}, + // Force errors on ip. + {&baseNetAddr, []byte{}, pver, true, 12, io.ErrShortWrite, io.EOF}, + // Force errors on port. + {&baseNetAddr, []byte{}, pver, true, 28, io.ErrShortWrite, io.EOF}, + + // Latest protocol version with no timestamp and intentional + // read/write errors. + // Force errors on services. + {&baseNetAddr, []byte{}, pver, false, 0, io.ErrShortWrite, io.EOF}, + // Force errors on ip. + {&baseNetAddr, []byte{}, pver, false, 8, io.ErrShortWrite, io.EOF}, + // Force errors on port. + {&baseNetAddr, []byte{}, pver, false, 24, io.ErrShortWrite, io.EOF}, + + // Protocol version before NetAddressTimeVersion with timestamp + // flag set (should not have timestamp due to old protocol + // version) and intentional read/write errors. + // Force errors on services. + {&baseNetAddr, []byte{}, pverNAT, true, 0, io.ErrShortWrite, io.EOF}, + // Force errors on ip. + {&baseNetAddr, []byte{}, pverNAT, true, 8, io.ErrShortWrite, io.EOF}, + // Force errors on port. + {&baseNetAddr, []byte{}, pverNAT, true, 24, io.ErrShortWrite, io.EOF}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode to wire format. + w := newFixedWriter(test.max) + err := wire.TstWriteNetAddress(w, test.pver, test.in, test.ts) + if err != test.writeErr { + t.Errorf("writeNetAddress #%d wrong error got: %v, want: %v", + i, err, test.writeErr) + continue + } + + // Decode from wire format. + var na wire.NetAddress + r := newFixedReader(test.max, test.buf) + err = wire.TstReadNetAddress(r, test.pver, &na, test.ts) + if err != test.readErr { + t.Errorf("readNetAddress #%d wrong error got: %v, want: %v", + i, err, test.readErr) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/protocol.go b/vendor/github.com/btcsuite/btcd/wire/protocol.go new file mode 100644 index 0000000000000000000000000000000000000000..ba2c209fbe43982f594087d08e7d17d8a95f4fc8 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/protocol.go @@ -0,0 +1,144 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "strconv" + "strings" +) + +const ( + // ProtocolVersion is the latest protocol version this package supports. + ProtocolVersion uint32 = 70012 + + // MultipleAddressVersion is the protocol version which added multiple + // addresses per message (pver >= MultipleAddressVersion). + MultipleAddressVersion uint32 = 209 + + // NetAddressTimeVersion is the protocol version which added the + // timestamp field (pver >= NetAddressTimeVersion). + NetAddressTimeVersion uint32 = 31402 + + // BIP0031Version is the protocol version AFTER which a pong message + // and nonce field in ping were added (pver > BIP0031Version). + BIP0031Version uint32 = 60000 + + // BIP0035Version is the protocol version which added the mempool + // message (pver >= BIP0035Version). + BIP0035Version uint32 = 60002 + + // BIP0037Version is the protocol version which added new connection + // bloom filtering related messages and extended the version message + // with a relay flag (pver >= BIP0037Version). + BIP0037Version uint32 = 70001 + + // BIP0111Version is the protocol version which added the SFNodeBloom + // service flag. + BIP0111Version uint32 = 70011 + + // SendHeadersVersion is the protocol version which added a new + // sendheaders message. + SendHeadersVersion uint32 = 70012 + + // RejectVersion is the protocol version which added a new reject + // message. + RejectVersion uint32 = 70002 +) + +// ServiceFlag identifies services supported by a bitcoin peer. +type ServiceFlag uint64 + +const ( + // SFNodeNetwork is a flag used to indicate a peer is a full node. + SFNodeNetwork ServiceFlag = 1 << iota + + // SFNodeGetUTXO is a flag used to indicate a peer supports the + // getutxos and utxos commands (BIP0064). + SFNodeGetUTXO + + // SFNodeBloom is a flag used to indiciate a peer supports bloom + // filtering. + SFNodeBloom +) + +// Map of service flags back to their constant names for pretty printing. +var sfStrings = map[ServiceFlag]string{ + SFNodeNetwork: "SFNodeNetwork", + SFNodeGetUTXO: "SFNodeGetUTXO", + SFNodeBloom: "SFNodeBloom", +} + +// orderedSFStrings is an ordered list of service flags from highest to +// lowest. +var orderedSFStrings = []ServiceFlag{ + SFNodeNetwork, + SFNodeGetUTXO, + SFNodeBloom, +} + +// String returns the ServiceFlag in human-readable form. +func (f ServiceFlag) String() string { + // No flags are set. + if f == 0 { + return "0x0" + } + + // Add individual bit flags. + s := "" + for _, flag := range orderedSFStrings { + if f&flag == flag { + s += sfStrings[flag] + "|" + f -= flag + } + } + + // Add any remaining flags which aren't accounted for as hex. + s = strings.TrimRight(s, "|") + if f != 0 { + s += "|0x" + strconv.FormatUint(uint64(f), 16) + } + s = strings.TrimLeft(s, "|") + return s +} + +// BitcoinNet represents which bitcoin network a message belongs to. +type BitcoinNet uint32 + +// Constants used to indicate the message bitcoin network. They can also be +// used to seek to the next message when a stream's state is unknown, but +// this package does not provide that functionality since it's generally a +// better idea to simply disconnect clients that are misbehaving over TCP. +const ( + // MainNet represents the main bitcoin network. + MainNet BitcoinNet = 0xd9b4bef9 + + // TestNet represents the regression test network. + TestNet BitcoinNet = 0xdab5bffa + + // TestNet3 represents the test network (version 3). + TestNet3 BitcoinNet = 0x0709110b + + // SimNet represents the simulation test network. + SimNet BitcoinNet = 0x12141c16 +) + +// bnStrings is a map of bitcoin networks back to their constant names for +// pretty printing. +var bnStrings = map[BitcoinNet]string{ + MainNet: "MainNet", + TestNet: "TestNet", + TestNet3: "TestNet3", + SimNet: "SimNet", +} + +// String returns the BitcoinNet in human-readable form. +func (n BitcoinNet) String() string { + if s, ok := bnStrings[n]; ok { + return s + } + + return fmt.Sprintf("Unknown BitcoinNet (%d)", uint32(n)) +} diff --git a/vendor/github.com/btcsuite/btcd/wire/protocol_test.go b/vendor/github.com/btcsuite/btcd/wire/protocol_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fee87968d53fa778b7839e9841f057e965244885 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/protocol_test.go @@ -0,0 +1,59 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "testing" + + "github.com/btcsuite/btcd/wire" +) + +// TestServiceFlagStringer tests the stringized output for service flag types. +func TestServiceFlagStringer(t *testing.T) { + tests := []struct { + in wire.ServiceFlag + want string + }{ + {0, "0x0"}, + {wire.SFNodeNetwork, "SFNodeNetwork"}, + {wire.SFNodeGetUTXO, "SFNodeGetUTXO"}, + {wire.SFNodeBloom, "SFNodeBloom"}, + {0xffffffff, "SFNodeNetwork|SFNodeGetUTXO|SFNodeBloom|0xfffffff8"}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.in.String() + if result != test.want { + t.Errorf("String #%d\n got: %s want: %s", i, result, + test.want) + continue + } + } +} + +// TestBitcoinNetStringer tests the stringized output for bitcoin net types. +func TestBitcoinNetStringer(t *testing.T) { + tests := []struct { + in wire.BitcoinNet + want string + }{ + {wire.MainNet, "MainNet"}, + {wire.TestNet, "TestNet"}, + {wire.TestNet3, "TestNet3"}, + {wire.SimNet, "SimNet"}, + {0xffffffff, "Unknown BitcoinNet (4294967295)"}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result := test.in.String() + if result != test.want { + t.Errorf("String #%d\n got: %s want: %s", i, result, + test.want) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/btcd/wire/shahash.go b/vendor/github.com/btcsuite/btcd/wire/shahash.go new file mode 100644 index 0000000000000000000000000000000000000000..b8bd50137e7090eebdd29bc0785a99a43f74ea93 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/shahash.go @@ -0,0 +1,111 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "encoding/hex" + "fmt" +) + +// HashSize is the array size used to store sha hashes. See ShaHash. +const HashSize = 32 + +// MaxHashStringSize is the maximum length of a ShaHash hash string. +const MaxHashStringSize = HashSize * 2 + +// ErrHashStrSize describes an error that indicates the caller specified a hash +// string that has too many characters. +var ErrHashStrSize = fmt.Errorf("max hash string length is %v bytes", MaxHashStringSize) + +// ShaHash is used in several of the bitcoin messages and common structures. It +// typically represents the double sha256 of data. +type ShaHash [HashSize]byte + +// String returns the ShaHash as the hexadecimal string of the byte-reversed +// hash. +func (hash ShaHash) String() string { + for i := 0; i < HashSize/2; i++ { + hash[i], hash[HashSize-1-i] = hash[HashSize-1-i], hash[i] + } + return hex.EncodeToString(hash[:]) +} + +// Bytes returns the bytes which represent the hash as a byte slice. +// +// NOTE: This makes a copy of the bytes and should have probably been named +// CloneBytes. It is generally cheaper to just slice the hash directly thereby +// reusing the same bytes rather than calling this method. +func (hash *ShaHash) Bytes() []byte { + newHash := make([]byte, HashSize) + copy(newHash, hash[:]) + + return newHash +} + +// SetBytes sets the bytes which represent the hash. An error is returned if +// the number of bytes passed in is not HashSize. +func (hash *ShaHash) SetBytes(newHash []byte) error { + nhlen := len(newHash) + if nhlen != HashSize { + return fmt.Errorf("invalid sha length of %v, want %v", nhlen, + HashSize) + } + copy(hash[:], newHash) + + return nil +} + +// IsEqual returns true if target is the same as hash. +func (hash *ShaHash) IsEqual(target *ShaHash) bool { + return *hash == *target +} + +// NewShaHash returns a new ShaHash from a byte slice. An error is returned if +// the number of bytes passed in is not HashSize. +func NewShaHash(newHash []byte) (*ShaHash, error) { + var sh ShaHash + err := sh.SetBytes(newHash) + if err != nil { + return nil, err + } + return &sh, err +} + +// NewShaHashFromStr creates a ShaHash from a hash string. The string should be +// the hexadecimal string of a byte-reversed hash, but any missing characters +// result in zero padding at the end of the ShaHash. +func NewShaHashFromStr(hash string) (*ShaHash, error) { + // Return error if hash string is too long. + if len(hash) > MaxHashStringSize { + return nil, ErrHashStrSize + } + + // Hex decoder expects the hash to be a multiple of two. + if len(hash)%2 != 0 { + hash = "0" + hash + } + + // Convert string hash to bytes. + buf, err := hex.DecodeString(hash) + if err != nil { + return nil, err + } + + // Un-reverse the decoded bytes, copying into in leading bytes of a + // ShaHash. There is no need to explicitly pad the result as any + // missing (when len(buf) < HashSize) bytes from the decoded hex string + // will remain zeros at the end of the ShaHash. + var ret ShaHash + blen := len(buf) + mid := blen / 2 + if blen%2 != 0 { + mid++ + } + blen-- + for i, b := range buf[:mid] { + ret[i], ret[blen-i] = buf[blen-i], b + } + return &ret, nil +} diff --git a/vendor/github.com/btcsuite/btcd/wire/shahash_test.go b/vendor/github.com/btcsuite/btcd/wire/shahash_test.go new file mode 100644 index 0000000000000000000000000000000000000000..908bfc4e448554290e6991763d0781eabc9f4736 --- /dev/null +++ b/vendor/github.com/btcsuite/btcd/wire/shahash_test.go @@ -0,0 +1,182 @@ +// Copyright (c) 2013-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "encoding/hex" + "testing" + + "github.com/btcsuite/btcd/wire" +) + +// TestShaHash tests the ShaHash API. +func TestShaHash(t *testing.T) { + + // Hash of block 234439. + blockHashStr := "14a0810ac680a3eb3f82edc878cea25ec41d6b790744e5daeef" + blockHash, err := wire.NewShaHashFromStr(blockHashStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Hash of block 234440 as byte slice. + buf := []byte{ + 0x79, 0xa6, 0x1a, 0xdb, 0xc6, 0xe5, 0xa2, 0xe1, + 0x39, 0xd2, 0x71, 0x3a, 0x54, 0x6e, 0xc7, 0xc8, + 0x75, 0x63, 0x2e, 0x75, 0xf1, 0xdf, 0x9c, 0x3f, + 0xa6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } + + hash, err := wire.NewShaHash(buf) + if err != nil { + t.Errorf("NewShaHash: unexpected error %v", err) + } + + // Ensure proper size. + if len(hash) != wire.HashSize { + t.Errorf("NewShaHash: hash length mismatch - got: %v, want: %v", + len(hash), wire.HashSize) + } + + // Ensure contents match. + if !bytes.Equal(hash[:], buf) { + t.Errorf("NewShaHash: hash contents mismatch - got: %v, want: %v", + hash[:], buf) + } + + // Ensure contents of hash of block 234440 don't match 234439. + if hash.IsEqual(blockHash) { + t.Errorf("IsEqual: hash contents should not match - got: %v, want: %v", + hash, blockHash) + } + + // Set hash from byte slice and ensure contents match. + err = hash.SetBytes(blockHash.Bytes()) + if err != nil { + t.Errorf("SetBytes: %v", err) + } + if !hash.IsEqual(blockHash) { + t.Errorf("IsEqual: hash contents mismatch - got: %v, want: %v", + hash, blockHash) + } + + // Invalid size for SetBytes. + err = hash.SetBytes([]byte{0x00}) + if err == nil { + t.Errorf("SetBytes: failed to received expected err - got: nil") + } + + // Invalid size for NewShaHash. + invalidHash := make([]byte, wire.HashSize+1) + _, err = wire.NewShaHash(invalidHash) + if err == nil { + t.Errorf("NewShaHash: failed to received expected err - got: nil") + } +} + +// TestShaHashString tests the stringized output for sha hashes. +func TestShaHashString(t *testing.T) { + // Block 100000 hash. + wantStr := "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" + hash := wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39, + 0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2, + 0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa, + 0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + }) + + hashStr := hash.String() + if hashStr != wantStr { + t.Errorf("String: wrong hash string - got %v, want %v", + hashStr, wantStr) + } +} + +// TestNewShaHashFromStr executes tests against the NewShaHashFromStr function. +func TestNewShaHashFromStr(t *testing.T) { + tests := []struct { + in string + want wire.ShaHash + err error + }{ + // Genesis hash. + { + "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", + mainNetGenesisHash, + nil, + }, + + // Genesis hash with stripped leading zeros. + { + "19d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", + mainNetGenesisHash, + nil, + }, + + // Empty string. + { + "", + wire.ShaHash{}, + nil, + }, + + // Single digit hash. + { + "1", + wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }), + nil, + }, + + // Block 203707 with stripped leading zeros. + { + "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc", + wire.ShaHash([wire.HashSize]byte{ // Make go vet happy. + 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, + 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, + 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, + 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }), + nil, + }, + + // Hash string that is too long. + { + "01234567890123456789012345678901234567890123456789012345678912345", + wire.ShaHash{}, + wire.ErrHashStrSize, + }, + + // Hash string that is contains non-hex chars. + { + "abcdefg", + wire.ShaHash{}, + hex.InvalidByteError('g'), + }, + } + + unexpectedErrStr := "NewShaHashFromStr #%d failed to detect expected error - got: %v want: %v" + unexpectedResultStr := "NewShaHashFromStr #%d got: %v want: %v" + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + result, err := wire.NewShaHashFromStr(test.in) + if err != test.err { + t.Errorf(unexpectedErrStr, i, err, test.err) + continue + } else if err != nil { + // Got expected error. Move on to the next test. + continue + } + if !test.want.IsEqual(result) { + t.Errorf(unexpectedResultStr, i, result, &test.want) + continue + } + } +} diff --git a/vendor/github.com/btcsuite/fastsha256/LICENSE b/vendor/github.com/btcsuite/fastsha256/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..12c3e4f7eb4ac749ad3916b770ef64cde0331531 --- /dev/null +++ b/vendor/github.com/btcsuite/fastsha256/LICENSE @@ -0,0 +1,19 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * Copyright (c) 2013 Conformal Systems LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/vendor/github.com/btcsuite/fastsha256/fastsha256_test.go b/vendor/github.com/btcsuite/fastsha256/fastsha256_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b787f4c3e017bb0bcb88953625d173798a02df8f --- /dev/null +++ b/vendor/github.com/btcsuite/fastsha256/fastsha256_test.go @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2013 Conformal Systems LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package fastsha256 + +import ( + "crypto/sha256" + "fmt" + "strings" + "testing" +) + +func TestSHA256(t *testing.T) { + expected := "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + msg := "abc" + digest := Sum256([]byte(msg)) + got := fmt.Sprintf("%0x", digest) + if got != expected { + t.Errorf("sha256 invalid digest") + return + } +} + +func TestSHA256New(t *testing.T) { + expected := "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + d := New() + msg := "abc" + d.Write([]byte(msg)) + digest := d.Sum(nil) + got := fmt.Sprintf("%0x", digest) + if got != expected { + t.Errorf("new invalid digest %s %x", expected, got) + return + } +} + +func TestSHA256Go(t *testing.T) { + expected := "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + d := sha256.New() + msg := "abc" + d.Write([]byte(msg)) + digest := d.Sum(nil) + got := fmt.Sprintf("%0x", digest) + if got != expected { + t.Errorf("append invalid digest %s %s", expected, got) + return + } +} + +func TestSHA256Append(t *testing.T) { + expected := "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + d := New() + d.Write([]byte("a")) + d.Write([]byte("b")) + d.Write([]byte("c")) + digest := d.Sum(nil) + got := fmt.Sprintf("%0x", digest) + if got != expected { + t.Errorf("append invalid digest %s %s", expected, got) + return + } +} + +func TestSHA256AppendGo(t *testing.T) { + expected := "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + d := sha256.New() + d.Write([]byte("a")) + d.Write([]byte("b")) + d.Write([]byte("c")) + digest := d.Sum(nil) + got := fmt.Sprintf("%0x", digest) + if got != expected { + t.Errorf("append invalid digest %s %s", expected, got) + return + } +} + +func TestSHA256AppendAndSum(t *testing.T) { + expected := "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + d := New() + msg := "a" + d.Write([]byte(msg)) + digest := d.Sum(nil) + + msg = "b" + d.Write([]byte(msg)) + digest = d.Sum(nil) + + msg = "c" + d.Write([]byte(msg)) + digest = d.Sum(nil) + + got := fmt.Sprintf("%0x", digest) + if got != expected { + t.Errorf("invalid digest %x %x", expected, got) + return + } +} + +func TestSHA256Rolling(t *testing.T) { + var b []byte + for i := 0; i < 20000; i++ { + b = append(b, byte(i%256)) + + d := New() + d.Write(b) + digest := d.Sum(nil) + + digest2 := sha256.Sum256(b) + + if string(digest) != string(digest2[:]) { + t.Errorf("invalid digest %x %x", digest, digest2) + return + } + } +} + +func TestSHA256RollingDirect(t *testing.T) { + var b []byte + for i := 0; i < 20000; i++ { + b = append(b, byte(i%256)) + + digest := Sum256(b) + digest2 := sha256.Sum256(b) + + if string(digest[:]) != string(digest2[:]) { + t.Errorf("invalid digest %x %x", digest, digest2) + return + } + } +} + +func DoubleSha256(b []byte) []byte { + hasher := New() + hasher.Write(b) + sum := hasher.Sum(nil) + hasher.Reset() + hasher.Write(sum) + return hasher.Sum(nil) +} + +func TestDoubleSha256(t *testing.T) { + var b []byte + for i := 0; i < 20000; i++ { + b = append(b, byte(i%256)) + DoubleSha256(b) + } +} + +func TestEmpty(t *testing.T) { + var b []byte + DoubleSha256(b) +} + +var t = strings.Repeat("a", 2049) + +func BenchmarkSha256(b *testing.B) { + for i := 0; i < b.N; i++ { + Sum256([]byte(t)) + } +} + +func BenchmarkSha256Go(b *testing.B) { + for i := 0; i < b.N; i++ { + sha256.Sum256([]byte(t)) + } +} diff --git a/vendor/github.com/btcsuite/fastsha256/sha256.go b/vendor/github.com/btcsuite/fastsha256/sha256.go new file mode 100644 index 0000000000000000000000000000000000000000..a5464b0184b60ab645509858d5a7189486dfacc0 --- /dev/null +++ b/vendor/github.com/btcsuite/fastsha256/sha256.go @@ -0,0 +1,218 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fastsha256 implements the SHA224 and SHA256 hash algorithms as defined +// in FIPS 180-4. +package fastsha256 + +import ( + "crypto" + "hash" +) + +func init() { + crypto.RegisterHash(crypto.SHA224, New224) + crypto.RegisterHash(crypto.SHA256, New) +} + +// The size of a SHA256 checksum in bytes. +const Size = 32 + +// The size of a SHA224 checksum in bytes. +const Size224 = 28 + +// The blocksize of SHA256 and SHA224 in bytes. +const BlockSize = 64 + +const ( + chunk = 64 + init0 = 0x6A09E667 + init1 = 0xBB67AE85 + init2 = 0x3C6EF372 + init3 = 0xA54FF53A + init4 = 0x510E527F + init5 = 0x9B05688C + init6 = 0x1F83D9AB + init7 = 0x5BE0CD19 + init0_224 = 0xC1059ED8 + init1_224 = 0x367CD507 + init2_224 = 0x3070DD17 + init3_224 = 0xF70E5939 + init4_224 = 0xFFC00B31 + init5_224 = 0x68581511 + init6_224 = 0x64F98FA7 + init7_224 = 0xBEFA4FA4 +) + +// digest represents the partial evaluation of a checksum. +type digest struct { + h [8]uint32 + x [chunk]byte + nx int + len uint64 + is224 bool // mark if this digest is SHA-224 +} + +func (d *digest) Reset() { + if !d.is224 { + d.h[0] = init0 + d.h[1] = init1 + d.h[2] = init2 + d.h[3] = init3 + d.h[4] = init4 + d.h[5] = init5 + d.h[6] = init6 + d.h[7] = init7 + } else { + d.h[0] = init0_224 + d.h[1] = init1_224 + d.h[2] = init2_224 + d.h[3] = init3_224 + d.h[4] = init4_224 + d.h[5] = init5_224 + d.h[6] = init6_224 + d.h[7] = init7_224 + } + d.nx = 0 + d.len = 0 +} + +// New returns a new hash.Hash computing the SHA256 checksum. +func New() hash.Hash { + d := new(digest) + d.Reset() + return d +} + +// New224 returns a new hash.Hash computing the SHA224 checksum. +func New224() hash.Hash { + d := new(digest) + d.is224 = true + d.Reset() + return d +} + +func (d *digest) Size() int { + if !d.is224 { + return Size + } + return Size224 +} + +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { + nn = len(p) + d.len += uint64(nn) + if d.nx > 0 { + n := copy(d.x[d.nx:], p) + d.nx += n + if d.nx == chunk { + block(d, d.x[:]) + d.nx = 0 + } + p = p[n:] + } + if len(p) >= chunk { + n := len(p) &^ (chunk - 1) + block(d, p[:n]) + p = p[n:] + } + if len(p) > 0 { + d.nx = copy(d.x[:], p) + } + return +} + +func (d *digest) Sum(in []byte) []byte { + // Make a copy of d so that caller can keep writing and summing. + d0 := *d + hash := d0.checkSum() + if d0.is224 { + return append(in, hash[:Size224]...) + } + return append(in, hash[:]...) +} + +func (d *digest) checkSum() [Size]byte { + len := d.len + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. + var tmp [64]byte + tmp[0] = 0x80 + if len%64 < 56 { + d.Write(tmp[0 : 56-len%64]) + } else { + d.Write(tmp[0 : 64+56-len%64]) + } + + // Length in bits. + len <<= 3 + for i := uint(0); i < 8; i++ { + tmp[i] = byte(len >> (56 - 8*i)) + } + d.Write(tmp[0:8]) + + if d.nx != 0 { + panic("d.nx != 0") + } + + h := d.h[:] + if d.is224 { + h = d.h[:7] + } + + var digest [Size]byte + for i, s := range h { + digest[i*4] = byte(s >> 24) + digest[i*4+1] = byte(s >> 16) + digest[i*4+2] = byte(s >> 8) + digest[i*4+3] = byte(s) + } + + return digest +} + +// Sum256 returns the SHA256 checksum of the data. +func Sum256(data []byte) [Size]byte { + var d digest + d.Reset() + d.Write(data) + return d.checkSum() +} + +// Sum224 returns the SHA224 checksum of the data. +func Sum224(data []byte) (sum224 [Size224]byte) { + var d digest + d.is224 = true + d.Reset() + d.Write(data) + sum := d.checkSum() + copy(sum224[:], sum[:Size224]) + return +} + +// MidState256 returns the internal hashing state after hashing the first chunk +// (BlockSize) of the data. This implemenation does not provide any mechanism +// to initialize the internal hashing state, so this information can't be used +// to skip hashing the first chunk on subsequent calls, but it is exposed so +// sophisticated callers can make use of it. +func MidState256(data []byte) [Size]byte { + if len(data) > BlockSize { + data = data[:BlockSize] + } + + var d digest + d.Reset() + d.Write(data) + + var midState [Size]byte + for i, s := range d.h { + midState[i*4] = byte(s >> 24) + midState[i*4+1] = byte(s >> 16) + midState[i*4+2] = byte(s >> 8) + midState[i*4+3] = byte(s) + } + + return midState +} diff --git a/vendor/github.com/btcsuite/fastsha256/sha256_test.go b/vendor/github.com/btcsuite/fastsha256/sha256_test.go new file mode 100644 index 0000000000000000000000000000000000000000..398f7be92f9fee7f95c8493161c2b7d139c7ea29 --- /dev/null +++ b/vendor/github.com/btcsuite/fastsha256/sha256_test.go @@ -0,0 +1,202 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// SHA256 hash algorithm. See FIPS 180-2. + +package fastsha256 + +import ( + "fmt" + "io" + "testing" +) + +type sha256Test struct { + out string + in string +} + +var golden = []sha256Test{ + {"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""}, + {"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", "a"}, + {"fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603", "ab"}, + {"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"}, + {"88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", "abcd"}, + {"36bbe50ed96841d10443bcb670d6554f0a34b761be67ec9c4a8ad2c0c44ca42c", "abcde"}, + {"bef57ec7f53a6d40beb640a780a639c83bc29ac8a9816f1fc6c5c6dcd93c4721", "abcdef"}, + {"7d1a54127b222502f5b79b5fb0803061152a44f92b37e23c6527baf665d4da9a", "abcdefg"}, + {"9c56cc51b374c3ba189210d5b6d4bf57790d351c96c47c02190ecf1e430635ab", "abcdefgh"}, + {"19cc02f26df43cc571bc9ed7b0c4d29224a3ec229529221725ef76d021c8326f", "abcdefghi"}, + {"72399361da6a7754fec986dca5b7cbaf1c810a28ded4abaf56b2106d06cb78b0", "abcdefghij"}, + {"a144061c271f152da4d151034508fed1c138b8c976339de229c3bb6d4bbb4fce", "Discard medicine more than two years old."}, + {"6dae5caa713a10ad04b46028bf6dad68837c581616a1589a265a11288d4bb5c4", "He who has a shady past knows that nice guys finish last."}, + {"ae7a702a9509039ddbf29f0765e70d0001177914b86459284dab8b348c2dce3f", "I wouldn't marry him with a ten foot pole."}, + {"6748450b01c568586715291dfa3ee018da07d36bb7ea6f180c1af6270215c64f", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {"14b82014ad2b11f661b5ae6a99b75105c2ffac278cd071cd6c05832793635774", "The days of the digital watch are numbered. -Tom Stoppard"}, + {"7102cfd76e2e324889eece5d6c41921b1e142a4ac5a2692be78803097f6a48d8", "Nepal premier won't resign."}, + {"23b1018cd81db1d67983c5f7417c44da9deb582459e378d7a068552ea649dc9f", "For every action there is an equal and opposite government program."}, + {"8001f190dfb527261c4cfcab70c98e8097a7a1922129bc4096950e57c7999a5a", "His money is twice tainted: 'taint yours and 'taint mine."}, + {"8c87deb65505c3993eb24b7a150c4155e82eee6960cf0c3a8114ff736d69cad5", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {"bfb0a67a19cdec3646498b2e0f751bddc41bba4b7f30081b0b932aad214d16d7", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {"7f9a0b9bf56332e19f5a0ec1ad9c1425a153da1c624868fda44561d6b74daf36", "size: a.out: bad magic"}, + {"b13f81b8aad9e3666879af19886140904f7f429ef083286195982a7588858cfc", "The major problem is with sendmail. -Mark Horton"}, + {"b26c38d61519e894480c70c8374ea35aa0ad05b2ae3d6674eec5f52a69305ed4", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {"049d5e26d4f10222cd841a119e38bd8d2e0d1129728688449575d4ff42b842c1", "If the enemy is within range, then so are you."}, + {"0e116838e3cc1c1a14cd045397e29b4d087aa11b0853fc69ec82e90330d60949", "It's well we cannot hear the screams/That we create in others' dreams."}, + {"4f7d8eb5bcf11de2a56b971021a444aa4eafd6ecd0f307b5109e4e776cd0fe46", "You remind me of a TV show, but that's all right: I watch it anyway."}, + {"61c0cc4c4bd8406d5120b3fb4ebc31ce87667c162f29468b3c779675a85aebce", "C is as portable as Stonehedge!!"}, + {"1fb2eb3688093c4a3f80cd87a5547e2ce940a4f923243a79a2a1e242220693ac", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {"395585ce30617b62c80b93e8208ce866d4edc811a177fdb4b82d3911d8696423", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {"4f9b189a13d030838269dce846b16a1ce9ce81fe63e65de2f636863336a98fe6", "How can you write a big system without C++? -Paul Glick"}, +} + +var golden224 = []sha256Test{ + {"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""}, + {"abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5", "a"}, + {"db3cda86d4429a1d39c148989566b38f7bda0156296bd364ba2f878b", "ab"}, + {"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"}, + {"a76654d8e3550e9a2d67a0eeb6c67b220e5885eddd3fde135806e601", "abcd"}, + {"bdd03d560993e675516ba5a50638b6531ac2ac3d5847c61916cfced6", "abcde"}, + {"7043631cb415556a275a4ebecb802c74ee9f6153908e1792a90b6a98", "abcdef"}, + {"d1884e711701ad81abe0c77a3b0ea12e19ba9af64077286c72fc602d", "abcdefg"}, + {"17eb7d40f0356f8598e89eafad5f6c759b1f822975d9c9b737c8a517", "abcdefgh"}, + {"aeb35915346c584db820d2de7af3929ffafef9222a9bcb26516c7334", "abcdefghi"}, + {"d35e1e5af29ddb0d7e154357df4ad9842afee527c689ee547f753188", "abcdefghij"}, + {"19297f1cef7ddc8a7e947f5c5a341e10f7245045e425db67043988d7", "Discard medicine more than two years old."}, + {"0f10c2eb436251f777fbbd125e260d36aecf180411726c7c885f599a", "He who has a shady past knows that nice guys finish last."}, + {"4d1842104919f314cad8a3cd20b3cba7e8ed3e7abed62b57441358f6", "I wouldn't marry him with a ten foot pole."}, + {"a8ba85c6fe0c48fbffc72bbb2f03fcdbc87ae2dc7a56804d1590fb3b", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {"5543fbab26e67e8885b1a852d567d1cb8b9bfe42e0899584c50449a9", "The days of the digital watch are numbered. -Tom Stoppard"}, + {"65ca107390f5da9efa05d28e57b221657edc7e43a9a18fb15b053ddb", "Nepal premier won't resign."}, + {"84953962be366305a9cc9b5cd16ed019edc37ac96c0deb3e12cca116", "For every action there is an equal and opposite government program."}, + {"35a189ce987151dfd00b3577583cc6a74b9869eecf894459cb52038d", "His money is twice tainted: 'taint yours and 'taint mine."}, + {"2fc333713983edfd4ef2c0da6fb6d6415afb94987c91e4069eb063e6", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {"cbe32d38d577a1b355960a4bc3c659c2dc4670859a19777a875842c4", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {"a2dc118ce959e027576413a7b440c875cdc8d40df9141d6ef78a57e1", "size: a.out: bad magic"}, + {"d10787e24052bcff26dc484787a54ed819e4e4511c54890ee977bf81", "The major problem is with sendmail. -Mark Horton"}, + {"62efcf16ab8a893acdf2f348aaf06b63039ff1bf55508c830532c9fb", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {"3e9b7e4613c59f58665104c5fa86c272db5d3a2ff30df5bb194a5c99", "If the enemy is within range, then so are you."}, + {"5999c208b8bdf6d471bb7c359ac5b829e73a8211dff686143a4e7f18", "It's well we cannot hear the screams/That we create in others' dreams."}, + {"3b2d67ff54eabc4ef737b14edf87c64280ef582bcdf2a6d56908b405", "You remind me of a TV show, but that's all right: I watch it anyway."}, + {"d0733595d20e4d3d6b5c565a445814d1bbb2fd08b9a3b8ffb97930c6", "C is as portable as Stonehedge!!"}, + {"43fb8aeed8a833175c9295c1165415f98c866ef08a4922959d673507", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {"ec18e66e93afc4fb1604bc2baedbfd20b44c43d76e65c0996d7851c6", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {"86ed2eaa9c75ba98396e5c9fb2f679ecf0ea2ed1e0ee9ceecb4a9332", "How can you write a big system without C++? -Paul Glick"}, +} + +func TestGolden(t *testing.T) { + for i := 0; i < len(golden); i++ { + g := golden[i] + s := fmt.Sprintf("%x", Sum256([]byte(g.in))) + if s != g.out { + t.Fatalf("Sum256 function: sha256(%s) = %s want %s", g.in, s, g.out) + } + c := New() + for j := 0; j < 3; j++ { + if j < 2 { + io.WriteString(c, g.in) + } else { + io.WriteString(c, g.in[0:len(g.in)/2]) + c.Sum(nil) + io.WriteString(c, g.in[len(g.in)/2:]) + } + s := fmt.Sprintf("%x", c.Sum(nil)) + if s != g.out { + t.Fatalf("sha256[%d](%s) = %s want %s", j, g.in, s, g.out) + } + c.Reset() + } + } + for i := 0; i < len(golden224); i++ { + g := golden224[i] + s := fmt.Sprintf("%x", Sum224([]byte(g.in))) + if s != g.out { + t.Fatalf("Sum224 function: sha224(%s) = %s want %s", g.in, s, g.out) + } + c := New224() + for j := 0; j < 3; j++ { + if j < 2 { + io.WriteString(c, g.in) + } else { + io.WriteString(c, g.in[0:len(g.in)/2]) + c.Sum(nil) + io.WriteString(c, g.in[len(g.in)/2:]) + } + s := fmt.Sprintf("%x", c.Sum(nil)) + if s != g.out { + t.Fatalf("sha224[%d](%s) = %s want %s", j, g.in, s, g.out) + } + c.Reset() + } + } +} + +func TestMidState(t *testing.T) { + tests := []sha256Test{ + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", ""}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "a"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "ab"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "abc"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "abcd"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "abcde"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "abcdef"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "abcdefg"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "abcdefgh"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "abcdefghi"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "abcdefghij"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "Discard medicine more than two years old."}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "He who has a shady past knows that nice guys finish last."}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "I wouldn't marry him with a ten foot pole."}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "The days of the digital watch are numbered. -Tom Stoppard"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "Nepal premier won't resign."}, + {"8ee9e4eafd25c794d91d3c27a903ab349b9abbf463b665031f830879b279fc64", "For every action there is an equal and opposite government program."}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "His money is twice tainted: 'taint yours and 'taint mine."}, + {"70808347da63934bd55fe167fb933295d5a8cfd7b9775ed20f4f8c8165a3c4ac", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, + {"c0b5fdab3680a85a1ed902a4c64b334bb9245f3766759167bbbd63bac6fd2d40", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "size: a.out: bad magic"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "The major problem is with sendmail. -Mark Horton"}, + {"0cec7aa455df798587963f9f24225e2b8fa2af19e27e959eaa2c4cfe2589899e", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "If the enemy is within range, then so are you."}, + {"89b7cf839f8717c70523b064e6515db1e7845dc6aefcb346d4052ab124821c8a", "It's well we cannot hear the screams/That we create in others' dreams."}, + {"57bc2c73b864f27946a25389b4dbb2fd607fd14349aafaaeea6a9633b28a36f5", "You remind me of a TV show, but that's all right: I watch it anyway."}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "C is as portable as Stonehedge!!"}, + {"f11d7deea50ae03424ec5e26b045c13183ab81026a73e87fd0a220f1b0738bc1", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, + {"9314c87a870e0b6ff1450fa456b2610087b5c794fcea56c29e67bc17b185d19a", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, + {"6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19", "How can you write a big system without C++? -Paul Glick"}, + } + + for _, test := range tests { + s := fmt.Sprintf("%x", MidState256([]byte(test.in))) + if s != test.out { + t.Errorf("Sum256 function: sha256(%s) = %s want %s", test.in, s, test.out) + continue + } + } +} + +var bench = New() +var buf = make([]byte, 8192) + +func benchmarkSize(b *testing.B, size int) { + b.SetBytes(int64(size)) + sum := make([]byte, bench.Size()) + for i := 0; i < b.N; i++ { + bench.Reset() + bench.Write(buf[:size]) + bench.Sum(sum[:0]) + } +} + +func BenchmarkHash8Bytes(b *testing.B) { + benchmarkSize(b, 8) +} + +func BenchmarkHash1K(b *testing.B) { + benchmarkSize(b, 1024) +} + +func BenchmarkHash8K(b *testing.B) { + benchmarkSize(b, 8192) +} diff --git a/vendor/github.com/btcsuite/fastsha256/sha256block.go b/vendor/github.com/btcsuite/fastsha256/sha256block.go new file mode 100644 index 0000000000000000000000000000000000000000..ff0c3faa3281f6c02d3dac04c242887f511f247b --- /dev/null +++ b/vendor/github.com/btcsuite/fastsha256/sha256block.go @@ -0,0 +1,128 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine !386,!amd64 + +// SHA256 block step. +// In its own file so that a faster assembly or C version +// can be substituted easily. + +package fastsha256 + +var _K = []uint32{ + 0x428a2f98, + 0x71374491, + 0xb5c0fbcf, + 0xe9b5dba5, + 0x3956c25b, + 0x59f111f1, + 0x923f82a4, + 0xab1c5ed5, + 0xd807aa98, + 0x12835b01, + 0x243185be, + 0x550c7dc3, + 0x72be5d74, + 0x80deb1fe, + 0x9bdc06a7, + 0xc19bf174, + 0xe49b69c1, + 0xefbe4786, + 0x0fc19dc6, + 0x240ca1cc, + 0x2de92c6f, + 0x4a7484aa, + 0x5cb0a9dc, + 0x76f988da, + 0x983e5152, + 0xa831c66d, + 0xb00327c8, + 0xbf597fc7, + 0xc6e00bf3, + 0xd5a79147, + 0x06ca6351, + 0x14292967, + 0x27b70a85, + 0x2e1b2138, + 0x4d2c6dfc, + 0x53380d13, + 0x650a7354, + 0x766a0abb, + 0x81c2c92e, + 0x92722c85, + 0xa2bfe8a1, + 0xa81a664b, + 0xc24b8b70, + 0xc76c51a3, + 0xd192e819, + 0xd6990624, + 0xf40e3585, + 0x106aa070, + 0x19a4c116, + 0x1e376c08, + 0x2748774c, + 0x34b0bcb5, + 0x391c0cb3, + 0x4ed8aa4a, + 0x5b9cca4f, + 0x682e6ff3, + 0x748f82ee, + 0x78a5636f, + 0x84c87814, + 0x8cc70208, + 0x90befffa, + 0xa4506ceb, + 0xbef9a3f7, + 0xc67178f2, +} + +func block(dig *digest, p []byte) { + var w [64]uint32 + h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] + for len(p) >= chunk { + // Can interlace the computation of w with the + // rounds below if needed for speed. + for i := 0; i < 16; i++ { + j := i * 4 + w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3]) + } + for i := 16; i < 64; i++ { + v1 := w[i-2] + t1 := (v1>>17 | v1<<(32-17)) ^ (v1>>19 | v1<<(32-19)) ^ (v1 >> 10) + v2 := w[i-15] + t2 := (v2>>7 | v2<<(32-7)) ^ (v2>>18 | v2<<(32-18)) ^ (v2 >> 3) + w[i] = t1 + w[i-7] + t2 + w[i-16] + } + + a, b, c, d, e, f, g, h := h0, h1, h2, h3, h4, h5, h6, h7 + + for i := 0; i < 64; i++ { + t1 := h + ((e>>6 | e<<(32-6)) ^ (e>>11 | e<<(32-11)) ^ (e>>25 | e<<(32-25))) + ((e & f) ^ (^e & g)) + _K[i] + w[i] + + t2 := ((a>>2 | a<<(32-2)) ^ (a>>13 | a<<(32-13)) ^ (a>>22 | a<<(32-22))) + ((a & b) ^ (a & c) ^ (b & c)) + + h = g + g = f + f = e + e = d + t1 + d = c + c = b + b = a + a = t1 + t2 + } + + h0 += a + h1 += b + h2 += c + h3 += d + h4 += e + h5 += f + h6 += g + h7 += h + + p = p[chunk:] + } + + dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7 +} diff --git a/vendor/github.com/btcsuite/fastsha256/sha256block_386.s b/vendor/github.com/btcsuite/fastsha256/sha256block_386.s new file mode 100644 index 0000000000000000000000000000000000000000..c34d9be91e4a75147e93365bddc85f1cfafd977c --- /dev/null +++ b/vendor/github.com/btcsuite/fastsha256/sha256block_386.s @@ -0,0 +1,285 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine + +// SHA256 block routine. See sha256block.go for Go equivalent. +// +// The algorithm is detailed in FIPS 180-4: +// +// http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf +// +// Wt = Mt; for 0 <= t <= 15 +// Wt = SIGMA1(Wt-2) + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63 +// +// a = H0 +// b = H1 +// c = H2 +// d = H3 +// e = H4 +// f = H5 +// g = H6 +// h = H7 +// +// for t = 0 to 63 { +// T1 = h + BIGSIGMA1(e) + Ch(e,f,g) + Kt + Wt +// T2 = BIGSIGMA0(a) + Maj(a,b,c) +// h = g +// g = f +// f = e +// e = d + T1 +// d = c +// c = b +// b = a +// a = T1 + T2 +// } +// +// H0 = a + H0 +// H1 = b + H1 +// H2 = c + H2 +// H3 = d + H3 +// H4 = e + H4 +// H5 = f + H5 +// H6 = g + H6 +// H7 = h + H7 + +// Wt = Mt; for 0 <= t <= 15 +#define MSGSCHEDULE0(index) \ + MOVL (index*4)(SI), AX; \ + BSWAPL AX; \ + MOVL AX, (index*4)(BP) + +// Wt = SIGMA1(Wt-2) + Wt-7 + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63 +// SIGMA0(x) = ROTR(7,x) XOR ROTR(18,x) XOR SHR(3,x) +// SIGMA1(x) = ROTR(17,x) XOR ROTR(19,x) XOR SHR(10,x) +#define MSGSCHEDULE1(index) \ + MOVL ((index-2)*4)(BP), AX; \ + MOVL AX, CX; \ + RORL $17, AX; \ + MOVL CX, DX; \ + RORL $19, CX; \ + SHRL $10, DX; \ + MOVL ((index-15)*4)(BP), BX; \ + XORL CX, AX; \ + MOVL BX, CX; \ + XORL DX, AX; \ + RORL $7, BX; \ + MOVL CX, DX; \ + SHRL $3, DX; \ + RORL $18, CX; \ + ADDL ((index-7)*4)(BP), AX; \ + XORL CX, BX; \ + XORL DX, BX; \ + ADDL ((index-16)*4)(BP), BX; \ + ADDL BX, AX; \ + MOVL AX, ((index)*4)(BP) + +// Calculate T1 in AX - uses AX, BX, CX and DX registers. +// Wt is passed in AX. +// T1 = h + BIGSIGMA1(e) + Ch(e, f, g) + Kt + Wt +// BIGSIGMA1(x) = ROTR(6,x) XOR ROTR(11,x) XOR ROTR(25,x) +// Ch(x, y, z) = (x AND y) XOR (NOT x AND z) +#define SHA256T1(const, e, f, g, h) \ + MOVL (h*4)(DI), BX; \ + ADDL AX, BX; \ + MOVL (e*4)(DI), AX; \ + ADDL $const, BX; \ + MOVL (e*4)(DI), CX; \ + RORL $6, AX; \ + MOVL (e*4)(DI), DX; \ + RORL $11, CX; \ + XORL CX, AX; \ + MOVL (e*4)(DI), CX; \ + RORL $25, DX; \ + ANDL (f*4)(DI), CX; \ + XORL AX, DX; \ + MOVL (e*4)(DI), AX; \ + NOTL AX; \ + ADDL DX, BX; \ + ANDL (g*4)(DI), AX; \ + XORL CX, AX; \ + ADDL BX, AX + +// Calculate T2 in BX - uses AX, BX, CX and DX registers. +// T2 = BIGSIGMA0(a) + Maj(a, b, c) +// BIGSIGMA0(x) = ROTR(2,x) XOR ROTR(13,x) XOR ROTR(22,x) +// Maj(x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z) +#define SHA256T2(a, b, c) \ + MOVL (a*4)(DI), AX; \ + MOVL (c*4)(DI), BX; \ + RORL $2, AX; \ + MOVL (a*4)(DI), DX; \ + ANDL (b*4)(DI), BX; \ + RORL $13, DX; \ + MOVL (a*4)(DI), CX; \ + ANDL (c*4)(DI), CX; \ + XORL DX, AX; \ + XORL CX, BX; \ + MOVL (a*4)(DI), DX; \ + MOVL (b*4)(DI), CX; \ + RORL $22, DX; \ + ANDL (a*4)(DI), CX; \ + XORL CX, BX; \ + XORL DX, AX; \ + ADDL AX, BX + +// Calculate T1 and T2, then e = d + T1 and a = T1 + T2. +// The values for e and a are stored in d and h, ready for rotation. +#define SHA256ROUND(index, const, a, b, c, d, e, f, g, h) \ + SHA256T1(const, e, f, g, h); \ + MOVL AX, 292(SP); \ + SHA256T2(a, b, c); \ + MOVL 292(SP), AX; \ + ADDL AX, BX; \ + ADDL AX, (d*4)(DI); \ + MOVL BX, (h*4)(DI) + +#define SHA256ROUND0(index, const, a, b, c, d, e, f, g, h) \ + MSGSCHEDULE0(index); \ + SHA256ROUND(index, const, a, b, c, d, e, f, g, h) + +#define SHA256ROUND1(index, const, a, b, c, d, e, f, g, h) \ + MSGSCHEDULE1(index); \ + SHA256ROUND(index, const, a, b, c, d, e, f, g, h) + +TEXT ·block(SB),0,$296-12 + MOVL p_base+4(FP), SI + MOVL p_len+8(FP), DX + SHRL $6, DX + SHLL $6, DX + + LEAL (SI)(DX*1), DI + MOVL DI, 288(SP) + CMPL SI, DI + JEQ end + + LEAL 256(SP), DI // variables + + MOVL dig+0(FP), BP + MOVL (0*4)(BP), AX // a = H0 + MOVL AX, (0*4)(DI) + MOVL (1*4)(BP), BX // b = H1 + MOVL BX, (1*4)(DI) + MOVL (2*4)(BP), CX // c = H2 + MOVL CX, (2*4)(DI) + MOVL (3*4)(BP), DX // d = H3 + MOVL DX, (3*4)(DI) + MOVL (4*4)(BP), AX // e = H4 + MOVL AX, (4*4)(DI) + MOVL (5*4)(BP), BX // f = H5 + MOVL BX, (5*4)(DI) + MOVL (6*4)(BP), CX // g = H6 + MOVL CX, (6*4)(DI) + MOVL (7*4)(BP), DX // h = H7 + MOVL DX, (7*4)(DI) + +loop: + MOVL SP, BP // message schedule + + SHA256ROUND0(0, 0x428a2f98, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND0(1, 0x71374491, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND0(2, 0xb5c0fbcf, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND0(3, 0xe9b5dba5, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND0(4, 0x3956c25b, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND0(5, 0x59f111f1, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND0(6, 0x923f82a4, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND0(7, 0xab1c5ed5, 1, 2, 3, 4, 5, 6, 7, 0) + SHA256ROUND0(8, 0xd807aa98, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND0(9, 0x12835b01, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND0(10, 0x243185be, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND0(11, 0x550c7dc3, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND0(12, 0x72be5d74, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND0(13, 0x80deb1fe, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND0(14, 0x9bdc06a7, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND0(15, 0xc19bf174, 1, 2, 3, 4, 5, 6, 7, 0) + + SHA256ROUND1(16, 0xe49b69c1, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND1(17, 0xefbe4786, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND1(18, 0x0fc19dc6, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND1(19, 0x240ca1cc, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND1(20, 0x2de92c6f, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND1(21, 0x4a7484aa, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND1(22, 0x5cb0a9dc, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND1(23, 0x76f988da, 1, 2, 3, 4, 5, 6, 7, 0) + SHA256ROUND1(24, 0x983e5152, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND1(25, 0xa831c66d, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND1(26, 0xb00327c8, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND1(27, 0xbf597fc7, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND1(28, 0xc6e00bf3, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND1(29, 0xd5a79147, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND1(30, 0x06ca6351, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND1(31, 0x14292967, 1, 2, 3, 4, 5, 6, 7, 0) + SHA256ROUND1(32, 0x27b70a85, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND1(33, 0x2e1b2138, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND1(34, 0x4d2c6dfc, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND1(35, 0x53380d13, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND1(36, 0x650a7354, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND1(37, 0x766a0abb, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND1(38, 0x81c2c92e, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND1(39, 0x92722c85, 1, 2, 3, 4, 5, 6, 7, 0) + SHA256ROUND1(40, 0xa2bfe8a1, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND1(41, 0xa81a664b, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND1(42, 0xc24b8b70, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND1(43, 0xc76c51a3, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND1(44, 0xd192e819, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND1(45, 0xd6990624, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND1(46, 0xf40e3585, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND1(47, 0x106aa070, 1, 2, 3, 4, 5, 6, 7, 0) + SHA256ROUND1(48, 0x19a4c116, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND1(49, 0x1e376c08, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND1(50, 0x2748774c, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND1(51, 0x34b0bcb5, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND1(52, 0x391c0cb3, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND1(53, 0x4ed8aa4a, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND1(54, 0x5b9cca4f, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND1(55, 0x682e6ff3, 1, 2, 3, 4, 5, 6, 7, 0) + SHA256ROUND1(56, 0x748f82ee, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND1(57, 0x78a5636f, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND1(58, 0x84c87814, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND1(59, 0x8cc70208, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND1(60, 0x90befffa, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND1(61, 0xa4506ceb, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND1(62, 0xbef9a3f7, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND1(63, 0xc67178f2, 1, 2, 3, 4, 5, 6, 7, 0) + + MOVL dig+0(FP), BP + MOVL (0*4)(BP), AX // H0 = a + H0 + ADDL (0*4)(DI), AX + MOVL AX, (0*4)(DI) + MOVL AX, (0*4)(BP) + MOVL (1*4)(BP), BX // H1 = b + H1 + ADDL (1*4)(DI), BX + MOVL BX, (1*4)(DI) + MOVL BX, (1*4)(BP) + MOVL (2*4)(BP), CX // H2 = c + H2 + ADDL (2*4)(DI), CX + MOVL CX, (2*4)(DI) + MOVL CX, (2*4)(BP) + MOVL (3*4)(BP), DX // H3 = d + H3 + ADDL (3*4)(DI), DX + MOVL DX, (3*4)(DI) + MOVL DX, (3*4)(BP) + MOVL (4*4)(BP), AX // H4 = e + H4 + ADDL (4*4)(DI), AX + MOVL AX, (4*4)(DI) + MOVL AX, (4*4)(BP) + MOVL (5*4)(BP), BX // H5 = f + H5 + ADDL (5*4)(DI), BX + MOVL BX, (5*4)(DI) + MOVL BX, (5*4)(BP) + MOVL (6*4)(BP), CX // H6 = g + H6 + ADDL (6*4)(DI), CX + MOVL CX, (6*4)(DI) + MOVL CX, (6*4)(BP) + MOVL (7*4)(BP), DX // H7 = h + H7 + ADDL (7*4)(DI), DX + MOVL DX, (7*4)(DI) + MOVL DX, (7*4)(BP) + + ADDL $64, SI + CMPL SI, 288(SP) + JB loop + +end: + RET diff --git a/vendor/github.com/btcsuite/fastsha256/sha256block_amd64.s b/vendor/github.com/btcsuite/fastsha256/sha256block_amd64.s new file mode 100644 index 0000000000000000000000000000000000000000..8e18b0648d2f95f581cda24ffd7340facbaf4f2c --- /dev/null +++ b/vendor/github.com/btcsuite/fastsha256/sha256block_amd64.s @@ -0,0 +1,260 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine + +//#include "../../../cmd/ld/textflag.h" +// just use the #define for now since this isn't in the main repo yet. +#define NOSPLIT 4 + +// SHA256 block routine. See sha256block.go for Go equivalent. +// +// The algorithm is detailed in FIPS 180-4: +// +// http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf +// +// Wt = Mt; for 0 <= t <= 15 +// Wt = SIGMA1(Wt-2) + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63 +// +// a = H0 +// b = H1 +// c = H2 +// d = H3 +// e = H4 +// f = H5 +// g = H6 +// h = H7 +// +// for t = 0 to 63 { +// T1 = h + BIGSIGMA1(e) + Ch(e,f,g) + Kt + Wt +// T2 = BIGSIGMA0(a) + Maj(a,b,c) +// h = g +// g = f +// f = e +// e = d + T1 +// d = c +// c = b +// b = a +// a = T1 + T2 +// } +// +// H0 = a + H0 +// H1 = b + H1 +// H2 = c + H2 +// H3 = d + H3 +// H4 = e + H4 +// H5 = f + H5 +// H6 = g + H6 +// H7 = h + H7 + +// Wt = Mt; for 0 <= t <= 15 +#define MSGSCHEDULE0(index) \ + MOVL (index*4)(SI), AX; \ + BSWAPL AX; \ + MOVL AX, (index*4)(BP) + +// Wt = SIGMA1(Wt-2) + Wt-7 + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63 +// SIGMA0(x) = ROTR(7,x) XOR ROTR(18,x) XOR SHR(3,x) +// SIGMA1(x) = ROTR(17,x) XOR ROTR(19,x) XOR SHR(10,x) +#define MSGSCHEDULE1(index) \ + MOVL ((index-2)*4)(BP), AX; \ + MOVL AX, CX; \ + RORL $17, AX; \ + MOVL CX, DX; \ + RORL $19, CX; \ + SHRL $10, DX; \ + MOVL ((index-15)*4)(BP), BX; \ + XORL CX, AX; \ + MOVL BX, CX; \ + XORL DX, AX; \ + RORL $7, BX; \ + MOVL CX, DX; \ + SHRL $3, DX; \ + RORL $18, CX; \ + ADDL ((index-7)*4)(BP), AX; \ + XORL CX, BX; \ + XORL DX, BX; \ + ADDL ((index-16)*4)(BP), BX; \ + ADDL BX, AX; \ + MOVL AX, ((index)*4)(BP) + +// Calculate T1 in AX - uses AX, CX and DX registers. +// h is also used as an accumulator. Wt is passed in AX. +// T1 = h + BIGSIGMA1(e) + Ch(e, f, g) + Kt + Wt +// BIGSIGMA1(x) = ROTR(6,x) XOR ROTR(11,x) XOR ROTR(25,x) +// Ch(x, y, z) = (x AND y) XOR (NOT x AND z) +#define SHA256T1(const, e, f, g, h) \ + ADDL AX, h; \ + MOVL e, AX; \ + ADDL $const, h; \ + MOVL e, CX; \ + RORL $6, AX; \ + MOVL e, DX; \ + RORL $11, CX; \ + XORL CX, AX; \ + MOVL e, CX; \ + RORL $25, DX; \ + ANDL f, CX; \ + XORL AX, DX; \ + MOVL e, AX; \ + NOTL AX; \ + ADDL DX, h; \ + ANDL g, AX; \ + XORL CX, AX; \ + ADDL h, AX + +// Calculate T2 in BX - uses BX, CX, DX and DI registers. +// T2 = BIGSIGMA0(a) + Maj(a, b, c) +// BIGSIGMA0(x) = ROTR(2,x) XOR ROTR(13,x) XOR ROTR(22,x) +// Maj(x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z) +#define SHA256T2(a, b, c) \ + MOVL a, DI; \ + MOVL c, BX; \ + RORL $2, DI; \ + MOVL a, DX; \ + ANDL b, BX; \ + RORL $13, DX; \ + MOVL a, CX; \ + ANDL c, CX; \ + XORL DX, DI; \ + XORL CX, BX; \ + MOVL a, DX; \ + MOVL b, CX; \ + RORL $22, DX; \ + ANDL a, CX; \ + XORL CX, BX; \ + XORL DX, DI; \ + ADDL DI, BX + +// Calculate T1 and T2, then e = d + T1 and a = T1 + T2. +// The values for e and a are stored in d and h, ready for rotation. +#define SHA256ROUND(index, const, a, b, c, d, e, f, g, h) \ + SHA256T1(const, e, f, g, h); \ + SHA256T2(a, b, c); \ + MOVL BX, h; \ + ADDL AX, d; \ + ADDL AX, h + +#define SHA256ROUND0(index, const, a, b, c, d, e, f, g, h) \ + MSGSCHEDULE0(index); \ + SHA256ROUND(index, const, a, b, c, d, e, f, g, h) + +#define SHA256ROUND1(index, const, a, b, c, d, e, f, g, h) \ + MSGSCHEDULE1(index); \ + SHA256ROUND(index, const, a, b, c, d, e, f, g, h) + +TEXT ·block(SB),0,$264-32 + MOVQ p_base+8(FP), SI + MOVQ p_len+16(FP), DX + SHRQ $6, DX + SHLQ $6, DX + + LEAQ (SI)(DX*1), DI + MOVQ DI, 256(SP) + CMPQ SI, DI + JEQ end + + MOVQ dig+0(FP), BP + MOVL (0*4)(BP), R8 // a = H0 + MOVL (1*4)(BP), R9 // b = H1 + MOVL (2*4)(BP), R10 // c = H2 + MOVL (3*4)(BP), R11 // d = H3 + MOVL (4*4)(BP), R12 // e = H4 + MOVL (5*4)(BP), R13 // f = H5 + MOVL (6*4)(BP), R14 // g = H6 + MOVL (7*4)(BP), R15 // h = H7 + +loop: + MOVQ SP, BP // message schedule + + SHA256ROUND0(0, 0x428a2f98, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND0(1, 0x71374491, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND0(2, 0xb5c0fbcf, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND0(3, 0xe9b5dba5, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND0(4, 0x3956c25b, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND0(5, 0x59f111f1, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND0(6, 0x923f82a4, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND0(7, 0xab1c5ed5, R9, R10, R11, R12, R13, R14, R15, R8) + SHA256ROUND0(8, 0xd807aa98, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND0(9, 0x12835b01, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND0(10, 0x243185be, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND0(11, 0x550c7dc3, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND0(12, 0x72be5d74, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND0(13, 0x80deb1fe, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND0(14, 0x9bdc06a7, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND0(15, 0xc19bf174, R9, R10, R11, R12, R13, R14, R15, R8) + + SHA256ROUND1(16, 0xe49b69c1, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND1(17, 0xefbe4786, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND1(18, 0x0fc19dc6, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND1(19, 0x240ca1cc, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND1(20, 0x2de92c6f, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND1(21, 0x4a7484aa, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND1(22, 0x5cb0a9dc, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND1(23, 0x76f988da, R9, R10, R11, R12, R13, R14, R15, R8) + SHA256ROUND1(24, 0x983e5152, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND1(25, 0xa831c66d, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND1(26, 0xb00327c8, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND1(27, 0xbf597fc7, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND1(28, 0xc6e00bf3, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND1(29, 0xd5a79147, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND1(30, 0x06ca6351, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND1(31, 0x14292967, R9, R10, R11, R12, R13, R14, R15, R8) + SHA256ROUND1(32, 0x27b70a85, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND1(33, 0x2e1b2138, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND1(34, 0x4d2c6dfc, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND1(35, 0x53380d13, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND1(36, 0x650a7354, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND1(37, 0x766a0abb, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND1(38, 0x81c2c92e, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND1(39, 0x92722c85, R9, R10, R11, R12, R13, R14, R15, R8) + SHA256ROUND1(40, 0xa2bfe8a1, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND1(41, 0xa81a664b, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND1(42, 0xc24b8b70, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND1(43, 0xc76c51a3, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND1(44, 0xd192e819, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND1(45, 0xd6990624, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND1(46, 0xf40e3585, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND1(47, 0x106aa070, R9, R10, R11, R12, R13, R14, R15, R8) + SHA256ROUND1(48, 0x19a4c116, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND1(49, 0x1e376c08, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND1(50, 0x2748774c, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND1(51, 0x34b0bcb5, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND1(52, 0x391c0cb3, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND1(53, 0x4ed8aa4a, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND1(54, 0x5b9cca4f, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND1(55, 0x682e6ff3, R9, R10, R11, R12, R13, R14, R15, R8) + SHA256ROUND1(56, 0x748f82ee, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND1(57, 0x78a5636f, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND1(58, 0x84c87814, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND1(59, 0x8cc70208, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND1(60, 0x90befffa, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND1(61, 0xa4506ceb, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND1(62, 0xbef9a3f7, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND1(63, 0xc67178f2, R9, R10, R11, R12, R13, R14, R15, R8) + + MOVQ dig+0(FP), BP + ADDL (0*4)(BP), R8 // H0 = a + H0 + MOVL R8, (0*4)(BP) + ADDL (1*4)(BP), R9 // H1 = b + H1 + MOVL R9, (1*4)(BP) + ADDL (2*4)(BP), R10 // H2 = c + H2 + MOVL R10, (2*4)(BP) + ADDL (3*4)(BP), R11 // H3 = d + H3 + MOVL R11, (3*4)(BP) + ADDL (4*4)(BP), R12 // H4 = e + H4 + MOVL R12, (4*4)(BP) + ADDL (5*4)(BP), R13 // H5 = f + H5 + MOVL R13, (5*4)(BP) + ADDL (6*4)(BP), R14 // H6 = g + H6 + MOVL R14, (6*4)(BP) + ADDL (7*4)(BP), R15 // H7 = h + H7 + MOVL R15, (7*4)(BP) + + ADDQ $64, SI + CMPQ SI, 256(SP) + JB loop + +end: + RET diff --git a/vendor/github.com/btcsuite/fastsha256/sha256block_decl.go b/vendor/github.com/btcsuite/fastsha256/sha256block_decl.go new file mode 100644 index 0000000000000000000000000000000000000000..389add77e0b76e70270e05f715aa59afaad07456 --- /dev/null +++ b/vendor/github.com/btcsuite/fastsha256/sha256block_decl.go @@ -0,0 +1,11 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build 386,!appengine amd64,!appengine + +package fastsha256 + +//go:noescape + +func block(dig *digest, p []byte) diff --git a/vendor/github.com/gin-gonic/gin/README.md b/vendor/github.com/gin-gonic/gin/README.md index 47ebbed71908f3197d847f86cfd86ff4972497bc..8023dc5a9f3564c872239864f6f1e65e64c16c64 100644 --- a/vendor/github.com/gin-gonic/gin/README.md +++ b/vendor/github.com/gin-gonic/gin/README.md @@ -3,6 +3,7 @@ <img align="right" src="https://raw.githubusercontent.com/gin-gonic/gin/master/logo.jpg"> [](https://travis-ci.org/gin-gonic/gin) [](https://coveralls.io/r/gin-gonic/gin?branch=master) +[](https://goreportcard.com/report/github.com/gin-gonic/gin) [](https://godoc.org/github.com/gin-gonic/gin) [](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -73,7 +74,7 @@ BenchmarkZeus_GithubAll | 2000 | 944234 | 300688 | 2648 (3): Heap Memory (B/op) (4): Average Allocations per Repetition (allocs/op) -##Gin v1. stable +## Gin v1. stable - [x] Zero allocation router. - [x] Still the fastest http router and framework. From routing to writing. @@ -83,6 +84,7 @@ BenchmarkZeus_GithubAll | 2000 | 944234 | 300688 | 2648 ## Start using it + 1. Download and install it: ```sh @@ -101,7 +103,7 @@ BenchmarkZeus_GithubAll | 2000 | 944234 | 300688 | 2648 import "net/http" ``` -##API Examples +## API Examples #### Using GET, POST, PUT, PATCH, DELETE and OPTIONS @@ -411,7 +413,7 @@ $ curl -v --form user=user --form password=password http://localhost:8080/login ``` -#### XML and JSON rendering +#### XML, JSON and YAML rendering ```go func main() { @@ -441,6 +443,10 @@ func main() { c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) }) + r.GET("/someYAML", func(c *gin.Context) { + c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) + }) + // Listen and server on 0.0.0.0:8080 r.Run(":8080") } @@ -707,3 +713,10 @@ endless.ListenAndServe(":4242", router) An alternative to endless: * [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully. + +## Example + +Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework. + +* [drone](https://github.com/drone/drone): Drone is a Continuous Delivery platform built on Docker, written in Go +* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go. diff --git a/vendor/github.com/gin-gonic/gin/benchmarks_test.go b/vendor/github.com/gin-gonic/gin/benchmarks_test.go index 8a1c91a96bdaf96093355a3fa35bb1616d813e5d..ebe9804c3f570ee1507c041e9b292f6ae65c211b 100644 --- a/vendor/github.com/gin-gonic/gin/benchmarks_test.go +++ b/vendor/github.com/gin-gonic/gin/benchmarks_test.go @@ -3,6 +3,7 @@ package gin import ( "html/template" "net/http" + "os" "testing" ) @@ -36,7 +37,7 @@ func BenchmarkManyHandlers(B *testing.B) { } func Benchmark5Params(B *testing.B) { - DefaultWriter = newMockWriter() + DefaultWriter = os.Stdout router := New() router.Use(func(c *Context) {}) router.GET("/param/:param1/:params2/:param3/:param4/:param5", func(c *Context) {}) diff --git a/vendor/github.com/gin-gonic/gin/binding/protobuf.go b/vendor/github.com/gin-gonic/gin/binding/protobuf.go index d6bef029ed4effe53fef2494f68e202d32960414..9f95622831d57b7eb7467a62bddf12337006cc59 100644 --- a/vendor/github.com/gin-gonic/gin/binding/protobuf.go +++ b/vendor/github.com/gin-gonic/gin/binding/protobuf.go @@ -13,11 +13,11 @@ import ( type protobufBinding struct{} -func (_ protobufBinding) Name() string { +func (protobufBinding) Name() string { return "protobuf" } -func (_ protobufBinding) Bind(req *http.Request, obj interface{}) error { +func (protobufBinding) Bind(req *http.Request, obj interface{}) error { buf, err := ioutil.ReadAll(req.Body) if err != nil { diff --git a/vendor/github.com/gin-gonic/gin/binding/validate_test.go b/vendor/github.com/gin-gonic/gin/binding/validate_test.go index faccaa3f880d14cd8ddd0256322b174ae6891444..cbcb389dec5ca316eda76ae5feca0ca6441bb203 100644 --- a/vendor/github.com/gin-gonic/gin/binding/validate_test.go +++ b/vendor/github.com/gin-gonic/gin/binding/validate_test.go @@ -16,15 +16,15 @@ type testInterface interface { String() string } -type substruct_noValidation struct { - I_String string - I_Int int +type substructNoValidation struct { + IString string + IInt int } -type mapNoValidationSub map[string]substruct_noValidation +type mapNoValidationSub map[string]substructNoValidation -type struct_noValidation_values struct { - substruct_noValidation +type structNoValidationValues struct { + substructNoValidation Boolean bool @@ -46,7 +46,7 @@ type struct_noValidation_values struct { Date time.Time - Struct substruct_noValidation + Struct substructNoValidation InlinedStruct struct { String []string Integer int @@ -54,8 +54,8 @@ type struct_noValidation_values struct { IntSlice []int IntPointerSlice []*int - StructPointerSlice []*substruct_noValidation - StructSlice []substruct_noValidation + StructPointerSlice []*substructNoValidation + StructSlice []substructNoValidation InterfaceSlice []testInterface UniversalInterface interface{} @@ -65,9 +65,9 @@ type struct_noValidation_values struct { StructMap mapNoValidationSub } -func createNoValidation_values() struct_noValidation_values { +func createNoValidationValues() structNoValidationValues { integer := 1 - s := struct_noValidation_values{ + s := structNoValidationValues{ Boolean: true, Uinteger: 1 << 29, Integer: -10000, @@ -84,33 +84,33 @@ func createNoValidation_values() struct_noValidation_values { String: "text", Date: time.Time{}, CustomInterface: &bytes.Buffer{}, - Struct: substruct_noValidation{}, + Struct: substructNoValidation{}, IntSlice: []int{-3, -2, 1, 0, 1, 2, 3}, IntPointerSlice: []*int{&integer}, - StructSlice: []substruct_noValidation{}, + StructSlice: []substructNoValidation{}, UniversalInterface: 1.2, FloatMap: map[string]float32{ "foo": 1.23, "bar": 232.323, }, StructMap: mapNoValidationSub{ - "foo": substruct_noValidation{}, - "bar": substruct_noValidation{}, + "foo": substructNoValidation{}, + "bar": substructNoValidation{}, }, // StructPointerSlice []noValidationSub // InterfaceSlice []testInterface } s.InlinedStruct.Integer = 1000 s.InlinedStruct.String = []string{"first", "second"} - s.I_String = "substring" - s.I_Int = 987654 + s.IString = "substring" + s.IInt = 987654 return s } func TestValidateNoValidationValues(t *testing.T) { - origin := createNoValidation_values() - test := createNoValidation_values() - empty := struct_noValidation_values{} + origin := createNoValidationValues() + test := createNoValidationValues() + empty := structNoValidationValues{} assert.Nil(t, validate(test)) assert.Nil(t, validate(&test)) @@ -120,8 +120,8 @@ func TestValidateNoValidationValues(t *testing.T) { assert.Equal(t, origin, test) } -type struct_noValidation_pointer struct { - substruct_noValidation +type structNoValidationPointer struct { + substructNoValidation Boolean bool @@ -143,12 +143,12 @@ type struct_noValidation_pointer struct { Date *time.Time - Struct *substruct_noValidation + Struct *substructNoValidation IntSlice *[]int IntPointerSlice *[]*int - StructPointerSlice *[]*substruct_noValidation - StructSlice *[]substruct_noValidation + StructPointerSlice *[]*substructNoValidation + StructSlice *[]substructNoValidation InterfaceSlice *[]testInterface FloatMap *map[string]float32 @@ -158,7 +158,7 @@ type struct_noValidation_pointer struct { func TestValidateNoValidationPointers(t *testing.T) { //origin := createNoValidation_values() //test := createNoValidation_values() - empty := struct_noValidation_pointer{} + empty := structNoValidationPointer{} //assert.Nil(t, validate(test)) //assert.Nil(t, validate(&test)) diff --git a/vendor/github.com/gin-gonic/gin/context.go b/vendor/github.com/gin-gonic/gin/context.go index b043d1b416ce48e96a39e0b207f89d92d78f4c0c..5d3b6a4e38e11f90409d35b3f8541302abcb74c7 100644 --- a/vendor/github.com/gin-gonic/gin/context.go +++ b/vendor/github.com/gin-gonic/gin/context.go @@ -69,7 +69,7 @@ func (c *Context) reset() { // Copy returns a copy of the current context that can be safely used outside the request's scope. // This have to be used then the context has to be passed to a goroutine. func (c *Context) Copy() *Context { - var cp Context = *c + var cp = *c cp.writermem.ResponseWriter = nil cp.Writer = &cp.writermem cp.index = abortIndex @@ -115,6 +115,7 @@ func (c *Context) Abort() { // For example, a failed attempt to authentificate a request could use: context.AbortWithStatus(401). func (c *Context) AbortWithStatus(code int) { c.Status(code) + c.Writer.WriteHeaderNow() c.Abort() } @@ -171,7 +172,7 @@ func (c *Context) Get(key string) (value interface{}, exists bool) { return } -// Returns the value for the given key if it exists, otherwise it panics. +// MustGet returns the value for the given key if it exists, otherwise it panics. func (c *Context) MustGet(key string) interface{} { if value, exists := c.Get(key); exists { return value @@ -243,7 +244,7 @@ func (c *Context) PostForm(key string) string { return value } -// PostForm returns the specified key from a POST urlencoded form or multipart form +// DefaultPostForm returns the specified key from a POST urlencoded form or multipart form // when it exists, otherwise it returns the specified defaultValue string. // See: PostForm() and GetPostForm() for further information. func (c *Context) DefaultPostForm(key, defaultValue string) string { @@ -426,6 +427,11 @@ func (c *Context) XML(code int, obj interface{}) { c.Render(code, render.XML{Data: obj}) } +// YAML serializes the given struct as YAML into the response body. +func (c *Context) YAML(code int, obj interface{}) { + c.Render(code, render.YAML{Data: obj}) +} + // String writes the given string into the response body. func (c *Context) String(code int, format string, values ...interface{}) { c.Status(code) diff --git a/vendor/github.com/gin-gonic/gin/context_test.go b/vendor/github.com/gin-gonic/gin/context_test.go index 322c4829140af114b3d58f45153a56e91d8b47ab..97d4957c6d95ce20b21be0d508effe208cd5e16e 100644 --- a/vendor/github.com/gin-gonic/gin/context_test.go +++ b/vendor/github.com/gin-gonic/gin/context_test.go @@ -262,14 +262,14 @@ func TestContextPostFormMultipart(t *testing.T) { Bar string `form:"bar"` BarAsInt int `form:"bar"` Array []string `form:"array"` - Id string `form:"id"` + ID string `form:"id"` } assert.NoError(t, c.Bind(&obj)) assert.Equal(t, obj.Foo, "bar") assert.Equal(t, obj.Bar, "10") assert.Equal(t, obj.BarAsInt, 10) assert.Equal(t, obj.Array, []string{"first", "second"}) - assert.Equal(t, obj.Id, "") + assert.Equal(t, obj.ID, "") value, ok := c.GetQuery("foo") assert.False(t, ok) @@ -433,6 +433,17 @@ func TestContextRenderFile(t *testing.T) { assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") } +// TestContextRenderYAML tests that the response is serialized as YAML +// and Content-Type is set to application/x-yaml +func TestContextRenderYAML(t *testing.T) { + c, w, _ := CreateTestContext() + c.YAML(201, H{"foo": "bar"}) + + assert.Equal(t, w.Code, 201) + assert.Equal(t, w.Body.String(), "foo: bar\n") + assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/x-yaml; charset=utf-8") +} + func TestContextHeaders(t *testing.T) { c, _, _ := CreateTestContext() c.Header("Content-Type", "text/plain") @@ -545,7 +556,6 @@ func TestContextAbortWithStatus(t *testing.T) { c, w, _ := CreateTestContext() c.index = 4 c.AbortWithStatus(401) - c.Writer.WriteHeaderNow() assert.Equal(t, c.index, abortIndex) assert.Equal(t, c.Writer.Status(), 401) @@ -596,7 +606,6 @@ func TestContextTypedError(t *testing.T) { func TestContextAbortWithError(t *testing.T) { c, w, _ := CreateTestContext() c.AbortWithError(401, errors.New("bad input")).SetMeta("some input") - c.Writer.WriteHeaderNow() assert.Equal(t, w.Code, 401) assert.Equal(t, c.index, abortIndex) diff --git a/vendor/github.com/gin-gonic/gin/errors.go b/vendor/github.com/gin-gonic/gin/errors.go index bced19aa0283c5bcfdb6d6c75b4980cdfe3a3aaa..7716bfaea0edc8463eaa656d4f87719c8d527870 100644 --- a/vendor/github.com/gin-gonic/gin/errors.go +++ b/vendor/github.com/gin-gonic/gin/errors.go @@ -66,7 +66,7 @@ func (msg *Error) JSON() interface{} { return json } -// Implements the json.Marshaller interface +// MarshalJSON implements the json.Marshaller interface func (msg *Error) MarshalJSON() ([]byte, error) { return json.Marshal(msg.JSON()) } @@ -89,7 +89,7 @@ func (a errorMsgs) ByType(typ ErrorType) errorMsgs { if typ == ErrorTypeAny { return a } - var result errorMsgs = nil + var result errorMsgs for _, msg := range a { if msg.IsType(typ) { result = append(result, msg) diff --git a/vendor/github.com/gin-gonic/gin/examples/realtime-advanced/stats.go b/vendor/github.com/gin-gonic/gin/examples/realtime-advanced/stats.go index c36ecc781d52b488d697021ab117c55d97c2f201..554ab86a52c7960ac7fe8be16d5ef6c44507fced 100644 --- a/vendor/github.com/gin-gonic/gin/examples/realtime-advanced/stats.go +++ b/vendor/github.com/gin-gonic/gin/examples/realtime-advanced/stats.go @@ -16,9 +16,9 @@ var savedStats map[string]uint64 func statsWorker() { c := time.Tick(1 * time.Second) - var lastMallocs uint64 = 0 - var lastFrees uint64 = 0 - for _ = range c { + var lastMallocs uint64 + var lastFrees uint64 + for range c { var stats runtime.MemStats runtime.ReadMemStats(&stats) diff --git a/vendor/github.com/gin-gonic/gin/gin.go b/vendor/github.com/gin-gonic/gin/gin.go index fb1df9cd060c769265832736e14bbfb1d4f89b41..60f4ab292aa2e33018b2c50287764463ffa44039 100644 --- a/vendor/github.com/gin-gonic/gin/gin.go +++ b/vendor/github.com/gin-gonic/gin/gin.go @@ -14,7 +14,7 @@ import ( "github.com/gin-gonic/gin/render" ) -// Framework's version +// Version is Framework's version const Version = "v1.0rc2" var default404Body = []byte("404 page not found") @@ -147,19 +147,19 @@ func (engine *Engine) SetHTMLTemplate(templ *template.Template) { engine.HTMLRender = render.HTMLProduction{Template: templ} } -// Adds handlers for NoRoute. It return a 404 code by default. +// NoRoute adds handlers for NoRoute. It return a 404 code by default. func (engine *Engine) NoRoute(handlers ...HandlerFunc) { engine.noRoute = handlers engine.rebuild404Handlers() } -// Sets the handlers called when... TODO +// NoMethod sets the handlers called when... TODO func (engine *Engine) NoMethod(handlers ...HandlerFunc) { engine.noMethod = handlers engine.rebuild405Handlers() } -// Attachs a global middleware to the router. ie. the middleware attached though Use() will be +// Use attachs a global middleware to the router. ie. the middleware attached though Use() will be // included in the handlers chain for every single request. Even 404, 405, static files... // For example, this is the right place for a logger or error management middleware. func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { diff --git a/vendor/github.com/gin-gonic/gin/ginS/gins.go b/vendor/github.com/gin-gonic/gin/ginS/gins.go index 71744702c5c718b393b24f7c7f0815eca9cb4f58..d40d1c3a31df353692c520f8723c6b212b2451a7 100644 --- a/vendor/github.com/gin-gonic/gin/ginS/gins.go +++ b/vendor/github.com/gin-gonic/gin/ginS/gins.go @@ -34,17 +34,17 @@ func SetHTMLTemplate(templ *template.Template) { engine().SetHTMLTemplate(templ) } -// Adds handlers for NoRoute. It return a 404 code by default. +// NoRoute adds handlers for NoRoute. It return a 404 code by default. func NoRoute(handlers ...HandlerFunc) { engine().NoRoute(handlers...) } -// Sets the handlers called when... TODO +// NoMethod sets the handlers called when... TODO func NoMethod(handlers ...HandlerFunc) { engine().NoMethod(handlers...) } -// Creates a new router group. You should add all the routes that have common middlwares or the same path prefix. +// Group creates a new router group. You should add all the routes that have common middlwares or the same path prefix. // For example, all the routes that use a common middlware for authorization could be grouped. func Group(relativePath string, handlers ...HandlerFunc) *RouterGroup { return engine().Group(relativePath, handlers...) @@ -111,28 +111,28 @@ func StaticFS(relativePath string, fs http.FileSystem) IRoutes { return engine().StaticFS(relativePath, fs) } -// Attachs a global middleware to the router. ie. the middlewares attached though Use() will be +// Use attachs a global middleware to the router. ie. the middlewares attached though Use() will be // included in the handlers chain for every single request. Even 404, 405, static files... // For example, this is the right place for a logger or error management middleware. func Use(middlewares ...HandlerFunc) IRoutes { return engine().Use(middlewares...) } -// The router is attached to a http.Server and starts listening and serving HTTP requests. +// Run : The router is attached to a http.Server and starts listening and serving HTTP requests. // It is a shortcut for http.ListenAndServe(addr, router) // Note: this method will block the calling goroutine undefinitelly unless an error happens. func Run(addr ...string) (err error) { return engine().Run(addr...) } -// The router is attached to a http.Server and starts listening and serving HTTPS requests. +// RunTLS : The router is attached to a http.Server and starts listening and serving HTTPS requests. // It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router) // Note: this method will block the calling goroutine undefinitelly unless an error happens. func RunTLS(addr string, certFile string, keyFile string) (err error) { return engine().RunTLS(addr, certFile, keyFile) } -// The router is attached to a http.Server and starts listening and serving HTTP requests +// RunUnix : The router is attached to a http.Server and starts listening and serving HTTP requests // through the specified unix socket (ie. a file) // Note: this method will block the calling goroutine undefinitelly unless an error happens. func RunUnix(file string) (err error) { diff --git a/vendor/github.com/gin-gonic/gin/gin_test.go b/vendor/github.com/gin-gonic/gin/gin_test.go index af95ab84d34cac2e5c9076d823eae31311e34dc5..cc24bc9243081e5c9099fae8777785e54553a91b 100644 --- a/vendor/github.com/gin-gonic/gin/gin_test.go +++ b/vendor/github.com/gin-gonic/gin/gin_test.go @@ -201,13 +201,13 @@ func compareFunc(t *testing.T, a, b interface{}) { func TestListOfRoutes(t *testing.T) { router := New() - router.GET("/favicon.ico", handler_test1) - router.GET("/", handler_test1) + router.GET("/favicon.ico", handlerTest1) + router.GET("/", handlerTest1) group := router.Group("/users") { - group.GET("/", handler_test2) - group.GET("/:id", handler_test1) - group.POST("/:id", handler_test2) + group.GET("/", handlerTest2) + group.GET("/:id", handlerTest1) + group.POST("/:id", handlerTest2) } router.Static("/static", ".") @@ -217,27 +217,27 @@ func TestListOfRoutes(t *testing.T) { assertRoutePresent(t, list, RouteInfo{ Method: "GET", Path: "/favicon.ico", - Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handler_test1$", + Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$", }) assertRoutePresent(t, list, RouteInfo{ Method: "GET", Path: "/", - Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handler_test1$", + Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$", }) assertRoutePresent(t, list, RouteInfo{ Method: "GET", Path: "/users/", - Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handler_test2$", + Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest2$", }) assertRoutePresent(t, list, RouteInfo{ Method: "GET", Path: "/users/:id", - Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handler_test1$", + Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$", }) assertRoutePresent(t, list, RouteInfo{ Method: "POST", Path: "/users/:id", - Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handler_test2$", + Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest2$", }) } @@ -251,5 +251,5 @@ func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo) t.Errorf("route not found: %v", wantRoute) } -func handler_test1(c *Context) {} -func handler_test2(c *Context) {} +func handlerTest1(c *Context) {} +func handlerTest2(c *Context) {} diff --git a/vendor/github.com/gin-gonic/gin/githubapi_test.go b/vendor/github.com/gin-gonic/gin/githubapi_test.go index 2227fa6ae8fcf61d919a02873f095bc58048d168..a08c264d777d4816a40ade653d440c3334703dd2 100644 --- a/vendor/github.com/gin-gonic/gin/githubapi_test.go +++ b/vendor/github.com/gin-gonic/gin/githubapi_test.go @@ -10,6 +10,7 @@ import ( "math/rand" "net/http" "net/http/httptest" + "os" "testing" "github.com/stretchr/testify/assert" @@ -298,7 +299,7 @@ func githubConfigRouter(router *Engine) { } func TestGithubAPI(t *testing.T) { - DefaultWriter = newMockWriter() + DefaultWriter = os.Stdout router := Default() githubConfigRouter(router) @@ -341,7 +342,7 @@ func exampleFromPath(path string) (string, Params) { if start >= 0 { value := fmt.Sprint(rand.Intn(100000)) params = append(params, Param{ - Key: path[start:len(path)], + Key: path[start:], Value: value, }) output.WriteString(value) @@ -357,7 +358,7 @@ func BenchmarkGithub(b *testing.B) { } func BenchmarkParallelGithub(b *testing.B) { - DefaultWriter = newMockWriter() + DefaultWriter = os.Stdout router := New() githubConfigRouter(router) @@ -373,7 +374,7 @@ func BenchmarkParallelGithub(b *testing.B) { } func BenchmarkParallelGithubDefault(b *testing.B) { - DefaultWriter = newMockWriter() + DefaultWriter = os.Stdout router := Default() githubConfigRouter(router) diff --git a/vendor/github.com/gin-gonic/gin/test_helpers.go b/vendor/github.com/gin-gonic/gin/helpers_test.go similarity index 100% rename from vendor/github.com/gin-gonic/gin/test_helpers.go rename to vendor/github.com/gin-gonic/gin/helpers_test.go diff --git a/vendor/github.com/gin-gonic/gin/logger.go b/vendor/github.com/gin-gonic/gin/logger.go index c5d4c3e24d1882dd2746fcba4292e43c6190b8d9..d56bc628915b7d79612a1764b176b8db3faf7787 100644 --- a/vendor/github.com/gin-gonic/gin/logger.go +++ b/vendor/github.com/gin-gonic/gin/logger.go @@ -28,23 +28,20 @@ func ErrorLogger() HandlerFunc { func ErrorLoggerT(typ ErrorType) HandlerFunc { return func(c *Context) { c.Next() - // avoid writting if we already wrote into the response body - if !c.Writer.Written() { - errors := c.Errors.ByType(typ) - if len(errors) > 0 { - c.JSON(-1, errors) - } + errors := c.Errors.ByType(typ) + if len(errors) > 0 { + c.JSON(-1, errors) } } } -// Instances a Logger middleware that will write the logs to gin.DefaultWriter +// Logger instances a Logger middleware that will write the logs to gin.DefaultWriter // By default gin.DefaultWriter = os.Stdout func Logger() HandlerFunc { return LoggerWithWriter(DefaultWriter) } -// Instance a Logger middleware with the specified writter buffer. +// LoggerWithWriter instance a Logger middleware with the specified writter buffer. // Example: os.Stdout, a file opened in write mode, a socket... func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc { var skip map[string]struct{} diff --git a/vendor/github.com/gin-gonic/gin/logger_test.go b/vendor/github.com/gin-gonic/gin/logger_test.go index c1471fe96baf1a2f955c453a250aa0d6b385802a..2ad1f474b3cc22a675d9ceab61c7dd959fdaafcf 100644 --- a/vendor/github.com/gin-gonic/gin/logger_test.go +++ b/vendor/github.com/gin-gonic/gin/logger_test.go @@ -116,7 +116,7 @@ func TestErrorLogger(t *testing.T) { w = performRequest(router, "GET", "/print") assert.Equal(t, w.Code, 500) - assert.Equal(t, w.Body.String(), "hola!") + assert.Equal(t, w.Body.String(), "hola!{\"error\":\"this is an error\"}\n") } func TestSkippingPaths(t *testing.T) { diff --git a/vendor/github.com/gin-gonic/gin/mode.go b/vendor/github.com/gin-gonic/gin/mode.go index bf9e995bf11f337c11873579d8e00c73023803a1..f44b071d36e8906c851b47761585410c357fe21f 100644 --- a/vendor/github.com/gin-gonic/gin/mode.go +++ b/vendor/github.com/gin-gonic/gin/mode.go @@ -34,8 +34,8 @@ const ( var DefaultWriter io.Writer = os.Stdout var DefaultErrorWriter io.Writer = os.Stderr -var ginMode int = debugCode -var modeName string = DebugMode +var ginMode = debugCode +var modeName = DebugMode func init() { mode := os.Getenv(ENV_GIN_MODE) diff --git a/vendor/github.com/gin-gonic/gin/recovery_test.go b/vendor/github.com/gin-gonic/gin/recovery_test.go index 94c71a11f727e3aa6845ee86ad5577eb41f3131b..4545ba3caaf0a9ac0f8e9a98cfd6cbc0892eb7a7 100644 --- a/vendor/github.com/gin-gonic/gin/recovery_test.go +++ b/vendor/github.com/gin-gonic/gin/recovery_test.go @@ -39,5 +39,5 @@ func TestPanicWithAbort(t *testing.T) { // RUN w := performRequest(router, "GET", "/recovery") // TEST - assert.Equal(t, w.Code, 500) // NOT SURE + assert.Equal(t, w.Code, 400) } diff --git a/vendor/github.com/gin-gonic/gin/render/html.go b/vendor/github.com/gin-gonic/gin/render/html.go index 01f6bf2207fad7c1fb4c9c82c428d7f5db6f0903..8bfb23ac8e1f3fbc94c02fbc6b4d6dbf56a937c5 100644 --- a/vendor/github.com/gin-gonic/gin/render/html.go +++ b/vendor/github.com/gin-gonic/gin/render/html.go @@ -61,7 +61,6 @@ func (r HTML) Render(w http.ResponseWriter) error { writeContentType(w, htmlContentType) if len(r.Name) == 0 { return r.Template.Execute(w, r.Data) - } else { - return r.Template.ExecuteTemplate(w, r.Name, r.Data) } + return r.Template.ExecuteTemplate(w, r.Name, r.Data) } diff --git a/vendor/github.com/gin-gonic/gin/render/render.go b/vendor/github.com/gin-gonic/gin/render/render.go index 994fcd7c7f6cad764d2e0baf8f19707acdbb8543..3808666acf0fca79d1e8e4558e7fb009c5d5dc84 100644 --- a/vendor/github.com/gin-gonic/gin/render/render.go +++ b/vendor/github.com/gin-gonic/gin/render/render.go @@ -20,6 +20,7 @@ var ( _ Render = HTML{} _ HTMLRender = HTMLDebug{} _ HTMLRender = HTMLProduction{} + _ Render = YAML{} ) func writeContentType(w http.ResponseWriter, value []string) { diff --git a/vendor/github.com/gin-gonic/gin/render/yaml.go b/vendor/github.com/gin-gonic/gin/render/yaml.go new file mode 100644 index 0000000000000000000000000000000000000000..46937d888460a36047824cfca39039e3663e0d7f --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/render/yaml.go @@ -0,0 +1,29 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import ( + "net/http" + + "gopkg.in/yaml.v2" +) + +type YAML struct { + Data interface{} +} + +var yamlContentType = []string{"application/x-yaml; charset=utf-8"} + +func (r YAML) Render(w http.ResponseWriter) error { + writeContentType(w, yamlContentType) + + bytes, err := yaml.Marshal(r.Data) + if err != nil { + return err + } + + w.Write(bytes) + return nil +} diff --git a/vendor/github.com/gin-gonic/gin/tree.go b/vendor/github.com/gin-gonic/gin/tree.go index 4f2082ee629d31761a7e5c8da3f204356042dae8..4f1da271f7d785cbcf96d04b0b429e436541c40e 100644 --- a/vendor/github.com/gin-gonic/gin/tree.go +++ b/vendor/github.com/gin-gonic/gin/tree.go @@ -20,7 +20,7 @@ type Param struct { // It is therefore safe to read values by the index. type Params []Param -// ByName returns the value of the first Param which key matches the given name. +// Get returns the value of the first Param which key matches the given name. // If no matching Param is found, an empty string is returned. func (ps Params) Get(name string) (string, bool) { for _, entry := range ps { @@ -31,6 +31,8 @@ func (ps Params) Get(name string) (string, bool) { return "", false } +// ByName returns the value of the first Param which key matches the given name. +// If no matching Param is found, an empty string is returned. func (ps Params) ByName(name string) (va string) { va, _ = ps.Get(name) return diff --git a/vendor/github.com/gin-gonic/gin/tree_test.go b/vendor/github.com/gin-gonic/gin/tree_test.go index 04dacdc5971a57fb2961b1f64e5099441b90e8b5..ed21783c7c2aa30d1ccfea46e48a17953f4df632 100644 --- a/vendor/github.com/gin-gonic/gin/tree_test.go +++ b/vendor/github.com/gin-gonic/gin/tree_test.go @@ -21,7 +21,7 @@ func printChildren(n *node, prefix string) { } } -// Used as a workaround since we can't compare functions or their adresses +// Used as a workaround since we can't compare functions or their addressses var fakeHandlerValue string func fakeHandler(val string) HandlersChain { diff --git a/vendor/github.com/gin-gonic/gin/utils.go b/vendor/github.com/gin-gonic/gin/utils.go index 2814791fb7e3e3e79ebb00550086d0c424c74b2b..18064fb5340857005ccfdb3b68d5d47676b66183 100644 --- a/vendor/github.com/gin-gonic/gin/utils.go +++ b/vendor/github.com/gin-gonic/gin/utils.go @@ -47,7 +47,7 @@ func WrapH(h http.Handler) HandlerFunc { type H map[string]interface{} -// Allows type H to be used with xml.Marshal +// MarshalXML allows type H to be used with xml.Marshal func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error { start.Name = xml.Name{ Space: "", @@ -143,10 +143,9 @@ func resolveAddress(addr []string) string { if port := os.Getenv("PORT"); len(port) > 0 { debugPrint("Environment variable PORT=\"%s\"", port) return ":" + port - } else { - debugPrint("Environment variable PORT is undefined. Using port :8080 by default") - return ":8080" } + debugPrint("Environment variable PORT is undefined. Using port :8080 by default") + return ":8080" case 1: return addr[0] default: diff --git a/vendor/github.com/golang/snappy/cmd/snappytool/main.cpp b/vendor/github.com/golang/snappy/cmd/snappytool/main.cpp index db28a899e261e4f64fb0fecda03ac50425b25fd8..fc31f51739aaaef0fb332c81e751a70bccea840d 100644 --- a/vendor/github.com/golang/snappy/cmd/snappytool/main.cpp +++ b/vendor/github.com/golang/snappy/cmd/snappytool/main.cpp @@ -1,6 +1,9 @@ /* To build the snappytool binary: g++ main.cpp /usr/lib/libsnappy.a -o snappytool +or, if you have built the C++ snappy library from source: +g++ main.cpp /path/to/your/snappy/.libs/libsnappy.a -o snappytool +after running "make" from your snappy checkout directory. */ #include <errno.h> diff --git a/vendor/github.com/golang/snappy/decode_amd64.go b/vendor/github.com/golang/snappy/decode_amd64.go index cbcd4647673fb0e28e671d85572bde17d0e1c2e1..fcd192b849eda1c391d5460b24ce4e263f3a6b90 100644 --- a/vendor/github.com/golang/snappy/decode_amd64.go +++ b/vendor/github.com/golang/snappy/decode_amd64.go @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !appengine // +build gc +// +build !noasm package snappy diff --git a/vendor/github.com/golang/snappy/decode_amd64.s b/vendor/github.com/golang/snappy/decode_amd64.s index 099d096209727d9af27b421648db8d17e19c153b..030cafcc8c04ab3e8030faafe8b6565ae2b7467d 100644 --- a/vendor/github.com/golang/snappy/decode_amd64.s +++ b/vendor/github.com/golang/snappy/decode_amd64.s @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !appengine // +build gc +// +build !noasm #include "textflag.h" diff --git a/vendor/github.com/golang/snappy/decode_other.go b/vendor/github.com/golang/snappy/decode_other.go index b557136be7f9fca962ade59f47d050533c25416b..f305b6f369cfd0f8d237b6e46a77a56f3cc604d9 100644 --- a/vendor/github.com/golang/snappy/decode_other.go +++ b/vendor/github.com/golang/snappy/decode_other.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !amd64 !gc +// +build !amd64 appengine !gc noasm package snappy diff --git a/vendor/github.com/golang/snappy/encode.go b/vendor/github.com/golang/snappy/encode.go index 0fc1cc9c04448b7a4baf06d5a8dd4f00f93acd2d..5759357280d4c36db2076282853dc5275018a215 100644 --- a/vendor/github.com/golang/snappy/encode.go +++ b/vendor/github.com/golang/snappy/encode.go @@ -10,10 +10,6 @@ import ( "io" ) -// maxOffset limits how far copy back-references can go, the same as the C++ -// code. -const maxOffset = 1 << 15 - func load32(b []byte, i int) uint32 { b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line. return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 @@ -178,13 +174,20 @@ func hash(u, shift uint32) uint32 { // minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize func encodeBlock(dst, src []byte) (d int) { // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. - const maxTableSize = 1 << 14 + // The table element type is uint16, as s < sLimit and sLimit < len(src) + // and len(src) <= maxBlockSize and maxBlockSize == 65536. + const ( + maxTableSize = 1 << 14 + // tableMask is redundant, but helps the compiler eliminate bounds + // checks. + tableMask = maxTableSize - 1 + ) shift, tableSize := uint32(32-8), 1<<8 for tableSize < maxTableSize && tableSize < len(src) { shift-- tableSize *= 2 } - var table [maxTableSize]int32 + var table [maxTableSize]uint16 // sLimit is when to stop looking for offset/length copies. The inputMargin // lets us use a fast path for emitLiteral in the main loop, while we are @@ -204,12 +207,13 @@ func encodeBlock(dst, src []byte) (d int) { // // Heuristic match skipping: If 32 bytes are scanned with no matches // found, start looking only at every other byte. If 32 more bytes are - // scanned, look at every third byte, etc.. When a match is found, - // immediately go back to looking at every byte. This is a small loss - // (~5% performance, ~0.1% density) for compressible data due to more - // bookkeeping, but for non-compressible data (such as JPEG) it's a - // huge win since the compressor quickly "realizes" the data is - // incompressible and doesn't bother looking for matches everywhere. + // scanned (or skipped), look at every third byte, etc.. When a match + // is found, immediately go back to looking at every byte. This is a + // small loss (~5% performance, ~0.1% density) for compressible data + // due to more bookkeeping, but for non-compressible data (such as + // JPEG) it's a huge win since the compressor quickly "realizes" the + // data is incompressible and doesn't bother looking for matches + // everywhere. // // The "skip" variable keeps track of how many bytes there are since // the last match; dividing it by 32 (ie. right-shifting by five) gives @@ -220,13 +224,14 @@ func encodeBlock(dst, src []byte) (d int) { candidate := 0 for { s = nextS - nextS = s + skip>>5 - skip++ + bytesBetweenHashLookups := skip >> 5 + nextS = s + bytesBetweenHashLookups + skip += bytesBetweenHashLookups if nextS > sLimit { goto emitRemainder } - candidate = int(table[nextHash]) - table[nextHash] = int32(s) + candidate = int(table[nextHash&tableMask]) + table[nextHash&tableMask] = uint16(s) nextHash = hash(load32(src, nextS), shift) if load32(src, s) == load32(src, candidate) { break @@ -267,10 +272,10 @@ func encodeBlock(dst, src []byte) (d int) { // three load32 calls. x := load64(src, s-1) prevHash := hash(uint32(x>>0), shift) - table[prevHash] = int32(s - 1) + table[prevHash&tableMask] = uint16(s - 1) currHash := hash(uint32(x>>8), shift) - candidate = int(table[currHash]) - table[currHash] = int32(s) + candidate = int(table[currHash&tableMask]) + table[currHash&tableMask] = uint16(s) if uint32(x>>8) != load32(src, candidate) { nextHash = hash(uint32(x>>16), shift) s++ diff --git a/vendor/github.com/golang/snappy/snappy_test.go b/vendor/github.com/golang/snappy/snappy_test.go index 714e2a287d4a0a6a7d55b6a3a0c64e6e19ed0a0f..c045f2279624e4c40f1c3640b52a168f40444363 100644 --- a/vendor/github.com/golang/snappy/snappy_test.go +++ b/vendor/github.com/golang/snappy/snappy_test.go @@ -459,8 +459,13 @@ func TestDecodeLengthOffset(t *testing.T) { } } +const ( + goldenText = "testdata/Mark.Twain-Tom.Sawyer.txt" + goldenCompressed = goldenText + ".rawsnappy" +) + func TestDecodeGoldenInput(t *testing.T) { - src, err := ioutil.ReadFile("testdata/pi.txt.rawsnappy") + src, err := ioutil.ReadFile(goldenCompressed) if err != nil { t.Fatalf("ReadFile: %v", err) } @@ -468,7 +473,7 @@ func TestDecodeGoldenInput(t *testing.T) { if err != nil { t.Fatalf("Decode: %v", err) } - want, err := ioutil.ReadFile("testdata/pi.txt") + want, err := ioutil.ReadFile(goldenText) if err != nil { t.Fatalf("ReadFile: %v", err) } @@ -478,12 +483,12 @@ func TestDecodeGoldenInput(t *testing.T) { } func TestEncodeGoldenInput(t *testing.T) { - src, err := ioutil.ReadFile("testdata/pi.txt") + src, err := ioutil.ReadFile(goldenText) if err != nil { t.Fatalf("ReadFile: %v", err) } got := Encode(nil, src) - want, err := ioutil.ReadFile("testdata/pi.txt.rawsnappy") + want, err := ioutil.ReadFile(goldenCompressed) if err != nil { t.Fatalf("ReadFile: %v", err) } @@ -532,6 +537,7 @@ func TestSameEncodingAsCppLongFiles(t *testing.T) { if msg := skipTestSameEncodingAsCpp(); msg != "" { t.Skip(msg) } + failed := false for i, tf := range testFiles { if err := downloadBenchmarkFiles(t, tf.filename); err != nil { t.Fatalf("failed to download testdata: %s", err) @@ -542,8 +548,14 @@ func TestSameEncodingAsCppLongFiles(t *testing.T) { } if err := runTestSameEncodingAsCpp(data); err != nil { t.Errorf("i=%d: %v", i, err) + failed = true } } + if failed { + t.Errorf("was the snappytool program built against the C++ snappy library version " + + "d53de187 or later, commited on 2016-04-05? See " + + "https://github.com/google/snappy/commit/d53de18799418e113e44444252a39b12a0e4e0cc") + } } // TestSlowForwardCopyOverrun tests the "expand the pattern" algorithm @@ -595,7 +607,7 @@ func TestSlowForwardCopyOverrun(t *testing.T) { // incompressible and the second half is very compressible. The encoded form's // length should be closer to 50% of the original length than 100%. func TestEncodeNoiseThenRepeats(t *testing.T) { - for _, origLen := range []int{32 * 1024, 256 * 1024, 2048 * 1024} { + for _, origLen := range []int{256 * 1024, 2048 * 1024} { src := make([]byte, origLen) rng := rand.New(rand.NewSource(1)) firstHalf, secondHalf := src[:origLen/2], src[origLen/2:] diff --git a/vendor/github.com/golang/snappy/testdata/Mark.Twain-Tom.Sawyer.txt b/vendor/github.com/golang/snappy/testdata/Mark.Twain-Tom.Sawyer.txt new file mode 100644 index 0000000000000000000000000000000000000000..86a18750bfcf8f2360158c72ef8c49dbf0d3d6c3 --- /dev/null +++ b/vendor/github.com/golang/snappy/testdata/Mark.Twain-Tom.Sawyer.txt @@ -0,0 +1,396 @@ +Produced by David Widger. The previous edition was updated by Jose +Menendez. + + + + + + THE ADVENTURES OF TOM SAWYER + BY + MARK TWAIN + (Samuel Langhorne Clemens) + + + + + P R E F A C E + +MOST of the adventures recorded in this book really occurred; one or +two were experiences of my own, the rest those of boys who were +schoolmates of mine. Huck Finn is drawn from life; Tom Sawyer also, but +not from an individual--he is a combination of the characteristics of +three boys whom I knew, and therefore belongs to the composite order of +architecture. + +The odd superstitions touched upon were all prevalent among children +and slaves in the West at the period of this story--that is to say, +thirty or forty years ago. + +Although my book is intended mainly for the entertainment of boys and +girls, I hope it will not be shunned by men and women on that account, +for part of my plan has been to try to pleasantly remind adults of what +they once were themselves, and of how they felt and thought and talked, +and what queer enterprises they sometimes engaged in. + + THE AUTHOR. + +HARTFORD, 1876. + + + + T O M S A W Y E R + + + +CHAPTER I + +"TOM!" + +No answer. + +"TOM!" + +No answer. + +"What's gone with that boy, I wonder? You TOM!" + +No answer. + +The old lady pulled her spectacles down and looked over them about the +room; then she put them up and looked out under them. She seldom or +never looked THROUGH them for so small a thing as a boy; they were her +state pair, the pride of her heart, and were built for "style," not +service--she could have seen through a pair of stove-lids just as well. +She looked perplexed for a moment, and then said, not fiercely, but +still loud enough for the furniture to hear: + +"Well, I lay if I get hold of you I'll--" + +She did not finish, for by this time she was bending down and punching +under the bed with the broom, and so she needed breath to punctuate the +punches with. She resurrected nothing but the cat. + +"I never did see the beat of that boy!" + +She went to the open door and stood in it and looked out among the +tomato vines and "jimpson" weeds that constituted the garden. No Tom. +So she lifted up her voice at an angle calculated for distance and +shouted: + +"Y-o-u-u TOM!" + +There was a slight noise behind her and she turned just in time to +seize a small boy by the slack of his roundabout and arrest his flight. + +"There! I might 'a' thought of that closet. What you been doing in +there?" + +"Nothing." + +"Nothing! Look at your hands. And look at your mouth. What IS that +truck?" + +"I don't know, aunt." + +"Well, I know. It's jam--that's what it is. Forty times I've said if +you didn't let that jam alone I'd skin you. Hand me that switch." + +The switch hovered in the air--the peril was desperate-- + +"My! Look behind you, aunt!" + +The old lady whirled round, and snatched her skirts out of danger. The +lad fled on the instant, scrambled up the high board-fence, and +disappeared over it. + +His aunt Polly stood surprised a moment, and then broke into a gentle +laugh. + +"Hang the boy, can't I never learn anything? Ain't he played me tricks +enough like that for me to be looking out for him by this time? But old +fools is the biggest fools there is. Can't learn an old dog new tricks, +as the saying is. But my goodness, he never plays them alike, two days, +and how is a body to know what's coming? He 'pears to know just how +long he can torment me before I get my dander up, and he knows if he +can make out to put me off for a minute or make me laugh, it's all down +again and I can't hit him a lick. I ain't doing my duty by that boy, +and that's the Lord's truth, goodness knows. Spare the rod and spile +the child, as the Good Book says. I'm a laying up sin and suffering for +us both, I know. He's full of the Old Scratch, but laws-a-me! he's my +own dead sister's boy, poor thing, and I ain't got the heart to lash +him, somehow. Every time I let him off, my conscience does hurt me so, +and every time I hit him my old heart most breaks. Well-a-well, man +that is born of woman is of few days and full of trouble, as the +Scripture says, and I reckon it's so. He'll play hookey this evening, * +and [* Southwestern for "afternoon"] I'll just be obleeged to make him +work, to-morrow, to punish him. It's mighty hard to make him work +Saturdays, when all the boys is having holiday, but he hates work more +than he hates anything else, and I've GOT to do some of my duty by him, +or I'll be the ruination of the child." + +Tom did play hookey, and he had a very good time. He got back home +barely in season to help Jim, the small colored boy, saw next-day's +wood and split the kindlings before supper--at least he was there in +time to tell his adventures to Jim while Jim did three-fourths of the +work. Tom's younger brother (or rather half-brother) Sid was already +through with his part of the work (picking up chips), for he was a +quiet boy, and had no adventurous, troublesome ways. + +While Tom was eating his supper, and stealing sugar as opportunity +offered, Aunt Polly asked him questions that were full of guile, and +very deep--for she wanted to trap him into damaging revealments. Like +many other simple-hearted souls, it was her pet vanity to believe she +was endowed with a talent for dark and mysterious diplomacy, and she +loved to contemplate her most transparent devices as marvels of low +cunning. Said she: + +"Tom, it was middling warm in school, warn't it?" + +"Yes'm." + +"Powerful warm, warn't it?" + +"Yes'm." + +"Didn't you want to go in a-swimming, Tom?" + +A bit of a scare shot through Tom--a touch of uncomfortable suspicion. +He searched Aunt Polly's face, but it told him nothing. So he said: + +"No'm--well, not very much." + +The old lady reached out her hand and felt Tom's shirt, and said: + +"But you ain't too warm now, though." And it flattered her to reflect +that she had discovered that the shirt was dry without anybody knowing +that that was what she had in her mind. But in spite of her, Tom knew +where the wind lay, now. So he forestalled what might be the next move: + +"Some of us pumped on our heads--mine's damp yet. See?" + +Aunt Polly was vexed to think she had overlooked that bit of +circumstantial evidence, and missed a trick. Then she had a new +inspiration: + +"Tom, you didn't have to undo your shirt collar where I sewed it, to +pump on your head, did you? Unbutton your jacket!" + +The trouble vanished out of Tom's face. He opened his jacket. His +shirt collar was securely sewed. + +"Bother! Well, go 'long with you. I'd made sure you'd played hookey +and been a-swimming. But I forgive ye, Tom. I reckon you're a kind of a +singed cat, as the saying is--better'n you look. THIS time." + +She was half sorry her sagacity had miscarried, and half glad that Tom +had stumbled into obedient conduct for once. + +But Sidney said: + +"Well, now, if I didn't think you sewed his collar with white thread, +but it's black." + +"Why, I did sew it with white! Tom!" + +But Tom did not wait for the rest. As he went out at the door he said: + +"Siddy, I'll lick you for that." + +In a safe place Tom examined two large needles which were thrust into +the lapels of his jacket, and had thread bound about them--one needle +carried white thread and the other black. He said: + +"She'd never noticed if it hadn't been for Sid. Confound it! sometimes +she sews it with white, and sometimes she sews it with black. I wish to +geeminy she'd stick to one or t'other--I can't keep the run of 'em. But +I bet you I'll lam Sid for that. I'll learn him!" + +He was not the Model Boy of the village. He knew the model boy very +well though--and loathed him. + +Within two minutes, or even less, he had forgotten all his troubles. +Not because his troubles were one whit less heavy and bitter to him +than a man's are to a man, but because a new and powerful interest bore +them down and drove them out of his mind for the time--just as men's +misfortunes are forgotten in the excitement of new enterprises. This +new interest was a valued novelty in whistling, which he had just +acquired from a negro, and he was suffering to practise it undisturbed. +It consisted in a peculiar bird-like turn, a sort of liquid warble, +produced by touching the tongue to the roof of the mouth at short +intervals in the midst of the music--the reader probably remembers how +to do it, if he has ever been a boy. Diligence and attention soon gave +him the knack of it, and he strode down the street with his mouth full +of harmony and his soul full of gratitude. He felt much as an +astronomer feels who has discovered a new planet--no doubt, as far as +strong, deep, unalloyed pleasure is concerned, the advantage was with +the boy, not the astronomer. + +The summer evenings were long. It was not dark, yet. Presently Tom +checked his whistle. A stranger was before him--a boy a shade larger +than himself. A new-comer of any age or either sex was an impressive +curiosity in the poor little shabby village of St. Petersburg. This boy +was well dressed, too--well dressed on a week-day. This was simply +astounding. His cap was a dainty thing, his close-buttoned blue cloth +roundabout was new and natty, and so were his pantaloons. He had shoes +on--and it was only Friday. He even wore a necktie, a bright bit of +ribbon. He had a citified air about him that ate into Tom's vitals. The +more Tom stared at the splendid marvel, the higher he turned up his +nose at his finery and the shabbier and shabbier his own outfit seemed +to him to grow. Neither boy spoke. If one moved, the other moved--but +only sidewise, in a circle; they kept face to face and eye to eye all +the time. Finally Tom said: + +"I can lick you!" + +"I'd like to see you try it." + +"Well, I can do it." + +"No you can't, either." + +"Yes I can." + +"No you can't." + +"I can." + +"You can't." + +"Can!" + +"Can't!" + +An uncomfortable pause. Then Tom said: + +"What's your name?" + +"'Tisn't any of your business, maybe." + +"Well I 'low I'll MAKE it my business." + +"Well why don't you?" + +"If you say much, I will." + +"Much--much--MUCH. There now." + +"Oh, you think you're mighty smart, DON'T you? I could lick you with +one hand tied behind me, if I wanted to." + +"Well why don't you DO it? You SAY you can do it." + +"Well I WILL, if you fool with me." + +"Oh yes--I've seen whole families in the same fix." + +"Smarty! You think you're SOME, now, DON'T you? Oh, what a hat!" + +"You can lump that hat if you don't like it. I dare you to knock it +off--and anybody that'll take a dare will suck eggs." + +"You're a liar!" + +"You're another." + +"You're a fighting liar and dasn't take it up." + +"Aw--take a walk!" + +"Say--if you give me much more of your sass I'll take and bounce a +rock off'n your head." + +"Oh, of COURSE you will." + +"Well I WILL." + +"Well why don't you DO it then? What do you keep SAYING you will for? +Why don't you DO it? It's because you're afraid." + +"I AIN'T afraid." + +"You are." + +"I ain't." + +"You are." + +Another pause, and more eying and sidling around each other. Presently +they were shoulder to shoulder. Tom said: + +"Get away from here!" + +"Go away yourself!" + +"I won't." + +"I won't either." + +So they stood, each with a foot placed at an angle as a brace, and +both shoving with might and main, and glowering at each other with +hate. But neither could get an advantage. After struggling till both +were hot and flushed, each relaxed his strain with watchful caution, +and Tom said: + +"You're a coward and a pup. I'll tell my big brother on you, and he +can thrash you with his little finger, and I'll make him do it, too." + +"What do I care for your big brother? I've got a brother that's bigger +than he is--and what's more, he can throw him over that fence, too." +[Both brothers were imaginary.] + +"That's a lie." + +"YOUR saying so don't make it so." + +Tom drew a line in the dust with his big toe, and said: + +"I dare you to step over that, and I'll lick you till you can't stand +up. Anybody that'll take a dare will steal sheep." + +The new boy stepped over promptly, and said: + +"Now you said you'd do it, now let's see you do it." + +"Don't you crowd me now; you better look out." + +"Well, you SAID you'd do it--why don't you do it?" + +"By jingo! for two cents I WILL do it." + +The new boy took two broad coppers out of his pocket and held them out +with derision. Tom struck them to the ground. In an instant both boys +were rolling and tumbling in the dirt, gripped together like cats; and +for the space of a minute they tugged and tore at each other's hair and +clothes, punched and scratched each other's nose, and covered +themselves with dust and glory. Presently the confusion took form, and +through the fog of battle Tom appeared, seated astride the new boy, and +pounding him with his fists. "Holler 'nuff!" said he. + +The boy only struggled to free himself. He was crying--mainly from rage. + +"Holler 'nuff!"--and the pounding went on. + +At last the stranger got out a smothered "'Nuff!" and Tom let him up +and said: + +"Now that'll learn you. Better look out who you're fooling with next +time." + +The new boy went off brushing the dust from his clothes, sobbing, +snuffling, and occasionally looking back and shaking his head and +threatening what he would do to Tom the "next time he caught him out." +To which Tom responded with jeers, and started off in high feather, and +as soon as his back was turned the new boy snatched up a stone, threw +it and hit him between the shoulders and then turned tail and ran like +an antelope. Tom chased the traitor home, and thus found out where he +lived. He then held a position at the gate for some time, daring the +enemy to come outside, but the enemy only made faces at him through the +window and declined. At last the enemy's mother appeared, and called +Tom a bad, vicious, vulgar child, and ordered him away. So he went +away; but he said he "'lowed" to "lay" for that boy. + +He got home pretty late that night, and when he climbed cautiously in +at the window, he uncovered an ambuscade, in the person of his aunt; +and when she saw the state his clothes were in her resolution to turn +his Saturday holiday into captivity at hard labor became adamantine in +its firmness. diff --git a/vendor/github.com/golang/snappy/testdata/Mark.Twain-Tom.Sawyer.txt.rawsnappy b/vendor/github.com/golang/snappy/testdata/Mark.Twain-Tom.Sawyer.txt.rawsnappy new file mode 100644 index 0000000000000000000000000000000000000000..9c56d985888e48a9967e187523f0e3326330aeca Binary files /dev/null and b/vendor/github.com/golang/snappy/testdata/Mark.Twain-Tom.Sawyer.txt.rawsnappy differ diff --git a/vendor/github.com/golang/snappy/testdata/pi.txt b/vendor/github.com/golang/snappy/testdata/pi.txt deleted file mode 100644 index 563af418dd22e742f68bece7b47be9f6a456bb94..0000000000000000000000000000000000000000 --- a/vendor/github.com/golang/snappy/testdata/pi.txt +++ /dev/null @@ -1 +0,0 @@ -3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912983367336244065664308602139494639522473719070217986094370277053921717629317675238467481846766940513200056812714526356082778577134275778960917363717872146844090122495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859502445945534690830264252230825334468503526193118817101000313783875288658753320838142061717766914730359825349042875546873115956286388235378759375195778185778053217122680661300192787661119590921642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151557485724245415069595082953311686172785588907509838175463746493931925506040092770167113900984882401285836160356370766010471018194295559619894676783744944825537977472684710404753464620804668425906949129331367702898915210475216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992458631503028618297455570674983850549458858692699569092721079750930295532116534498720275596023648066549911988183479775356636980742654252786255181841757467289097777279380008164706001614524919217321721477235014144197356854816136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179049460165346680498862723279178608578438382796797668145410095388378636095068006422512520511739298489608412848862694560424196528502221066118630674427862203919494504712371378696095636437191728746776465757396241389086583264599581339047802759009946576407895126946839835259570982582262052248940772671947826848260147699090264013639443745530506820349625245174939965143142980919065925093722169646151570985838741059788595977297549893016175392846813826868386894277415599185592524595395943104997252468084598727364469584865383673622262609912460805124388439045124413654976278079771569143599770012961608944169486855584840635342207222582848864815845602850601684273945226746767889525213852254995466672782398645659611635488623057745649803559363456817432411251507606947945109659609402522887971089314566913686722874894056010150330861792868092087476091782493858900971490967598526136554978189312978482168299894872265880485756401427047755513237964145152374623436454285844479526586782105114135473573952311342716610213596953623144295248493718711014576540359027993440374200731057853906219838744780847848968332144571386875194350643021845319104848100537061468067491927819119793995206141966342875444064374512371819217999839101591956181467514269123974894090718649423196156794520809514655022523160388193014209376213785595663893778708303906979207734672218256259966150142150306803844773454920260541466592520149744285073251866600213243408819071048633173464965145390579626856100550810665879699816357473638405257145910289706414011097120628043903975951567715770042033786993600723055876317635942187312514712053292819182618612586732157919841484882916447060957527069572209175671167229109816909152801735067127485832228718352093539657251210835791513698820914442100675103346711031412671113699086585163983150197016515116851714376576183515565088490998985998238734552833163550764791853589322618548963213293308985706420467525907091548141654985946163718027098199430992448895757128289059232332609729971208443357326548938239119325974636673058360414281388303203824903758985243744170291327656180937734440307074692112019130203303801976211011004492932151608424448596376698389522868478312355265821314495768572624334418930396864262434107732269780280731891544110104468232527162010526522721116603966655730925471105578537634668206531098965269186205647693125705863566201855810072936065987648611791045334885034611365768675324944166803962657978771855608455296541266540853061434443185867697514566140680070023787765913440171274947042056223053899456131407112700040785473326993908145466464588079727082668306343285878569830523580893306575740679545716377525420211495576158140025012622859413021647155097925923099079654737612551765675135751782966645477917450112996148903046399471329621073404375189573596145890193897131117904297828564750320319869151402870808599048010941214722131794764777262241425485454033215718530614228813758504306332175182979866223717215916077166925474873898665494945011465406284336639379003976926567214638530673609657120918076383271664162748888007869256029022847210403172118608204190004229661711963779213375751149595015660496318629472654736425230817703675159067350235072835405670403867435136222247715891504953098444893330963408780769325993978054193414473774418426312986080998886874132604721569516239658645730216315981931951673538129741677294786724229246543668009806769282382806899640048243540370141631496589794092432378969070697794223625082216889573837986230015937764716512289357860158816175578297352334460428151262720373431465319777741603199066554187639792933441952154134189948544473456738316249934191318148092777710386387734317720754565453220777092120190516609628049092636019759882816133231666365286193266863360627356763035447762803504507772355471058595487027908143562401451718062464362679456127531813407833033625423278394497538243720583531147711992606381334677687969597030983391307710987040859133746414428227726346594704745878477872019277152807317679077071572134447306057007334924369311383504931631284042512192565179806941135280131470130478164378851852909285452011658393419656213491434159562586586557055269049652098580338507224264829397285847831630577775606888764462482468579260395352773480304802900587607582510474709164396136267604492562742042083208566119062545433721315359584506877246029016187667952406163425225771954291629919306455377991403734043287526288896399587947572917464263574552540790914513571113694109119393251910760208252026187985318877058429725916778131496990090192116971737278476847268608490033770242429165130050051683233643503895170298939223345172201381280696501178440874519601212285993716231301711444846409038906449544400619869075485160263275052983491874078668088183385102283345085048608250393021332197155184306354550076682829493041377655279397517546139539846833936383047461199665385815384205685338621867252334028308711232827892125077126294632295639898989358211674562701021835646220134967151881909730381198004973407239610368540664319395097901906996395524530054505806855019567302292191393391856803449039820595510022635353619204199474553859381023439554495977837790237421617271117236434354394782218185286240851400666044332588856986705431547069657474585503323233421073015459405165537906866273337995851156257843229882737231989875714159578111963583300594087306812160287649628674460477464915995054973742562690104903778198683593814657412680492564879855614537234786733039046883834363465537949864192705638729317487233208376011230299113679386270894387993620162951541337142489283072201269014754668476535761647737946752004907571555278196536213239264061601363581559074220202031872776052772190055614842555187925303435139844253223415762336106425063904975008656271095359194658975141310348227693062474353632569160781547818115284366795706110861533150445212747392454494542368288606134084148637767009612071512491404302725386076482363414334623518975766452164137679690314950191085759844239198629164219399490723623464684411739403265918404437805133389452574239950829659122850855582157250310712570126683024029295252201187267675622041542051618416348475651699981161410100299607838690929160302884002691041407928862150784245167090870006992821206604183718065355672525325675328612910424877618258297651579598470356222629348600341587229805349896502262917487882027342092222453398562647669149055628425039127577102840279980663658254889264880254566101729670266407655904290994568150652653053718294127033693137851786090407086671149655834343476933857817113864558736781230145876871266034891390956200993936103102916161528813843790990423174733639480457593149314052976347574811935670911013775172100803155902485309066920376719220332290943346768514221447737939375170344366199104033751117354719185504644902636551281622882446257591633303910722538374218214088350865739177150968288747826569959957449066175834413752239709683408005355984917541738188399944697486762655165827658483588453142775687900290951702835297163445621296404352311760066510124120065975585127617858382920419748442360800719304576189323492292796501987518721272675079812554709589045563579212210333466974992356302549478024901141952123828153091140790738602515227429958180724716259166854513331239480494707911915326734302824418604142636395480004480026704962482017928964766975831832713142517029692348896276684403232609275249603579964692565049368183609003238092934595889706953653494060340216654437558900456328822505452556405644824651518754711962184439658253375438856909411303150952617937800297412076651479394259029896959469955657612186561967337862362561252163208628692221032748892186543648022967807057656151446320469279068212073883778142335628236089632080682224680122482611771858963814091839036736722208883215137556003727983940041529700287830766709444745601345564172543709069793961225714298946715435784687886144458123145935719849225284716050492212424701412147805734551050080190869960330276347870810817545011930714122339086639383395294257869050764310063835198343893415961318543475464955697810382930971646514384070070736041123735998434522516105070270562352660127648483084076118301305279320542746286540360367453286510570658748822569815793678976697422057505968344086973502014102067235850200724522563265134105592401902742162484391403599895353945909440704691209140938700126456001623742880210927645793106579229552498872758461012648369998922569596881592056001016552563756785 \ No newline at end of file diff --git a/vendor/github.com/golang/snappy/testdata/pi.txt.rawsnappy b/vendor/github.com/golang/snappy/testdata/pi.txt.rawsnappy deleted file mode 100644 index 3378c72434769050f6bc59d5296c237903a0a389..0000000000000000000000000000000000000000 Binary files a/vendor/github.com/golang/snappy/testdata/pi.txt.rawsnappy and /dev/null differ diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go index d2216e05424a4210b4f36dc32858b62166528bb8..e3abfd5a6bf4c92b979d056713e39bbc4766074f 100644 --- a/vendor/github.com/stretchr/testify/mock/mock.go +++ b/vendor/github.com/stretchr/testify/mock/mock.go @@ -43,6 +43,9 @@ type Call struct { // expectations. 0 means to always return the value. Repeatability int + // Amount of times this call has been called + totalCalls int + // Holds a channel that will be used to block the Return until it either // recieves a message or is closed. nil means it returns immediately. WaitFor <-chan time.Time @@ -305,9 +308,14 @@ func (m *Mock) Called(arguments ...interface{}) Arguments { switch { case call.Repeatability == 1: call.Repeatability = -1 + call.totalCalls++ case call.Repeatability > 1: call.Repeatability-- + call.totalCalls++ + + case call.Repeatability == 0: + call.totalCalls++ } m.mutex.Unlock() } @@ -355,7 +363,7 @@ func (m *Mock) AssertExpectations(t TestingT) bool { // iterate through each expectation expectedCalls := m.expectedCalls() for _, expectedCall := range expectedCalls { - if !m.methodWasCalled(expectedCall.Method, expectedCall.Arguments) { + if !m.methodWasCalled(expectedCall.Method, expectedCall.Arguments) && expectedCall.totalCalls == 0 { somethingMissing = true failedExpectations++ t.Logf("\u274C\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String()) @@ -390,6 +398,7 @@ func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls } // AssertCalled asserts that the method was called. +// It can produce a false result when an arugment is a pointer type and the underyling value changed after calling the mocked method. func (m *Mock) AssertCalled(t TestingT, methodName string, arguments ...interface{}) bool { if !assert.True(t, m.methodWasCalled(methodName, arguments), fmt.Sprintf("The \"%s\" method should have been called with %d argument(s), but was not.", methodName, len(arguments))) { t.Logf("%v", m.expectedCalls()) @@ -399,6 +408,7 @@ func (m *Mock) AssertCalled(t TestingT, methodName string, arguments ...interfac } // AssertNotCalled asserts that the method was not called. +// It can produce a false result when an arugment is a pointer type and the underyling value changed after calling the mocked method. func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...interface{}) bool { if !assert.False(t, m.methodWasCalled(methodName, arguments), fmt.Sprintf("The \"%s\" method was called with %d argument(s), but should NOT have been.", methodName, len(arguments))) { t.Logf("%v", m.expectedCalls()) diff --git a/vendor/github.com/stretchr/testify/mock/mock_test.go b/vendor/github.com/stretchr/testify/mock/mock_test.go index b206faaa42d39be3ed20f37cc4974a62ba97f463..166c32461ceceed66fcd6b9b9518c75bce8d2204 100644 --- a/vendor/github.com/stretchr/testify/mock/mock_test.go +++ b/vendor/github.com/stretchr/testify/mock/mock_test.go @@ -765,6 +765,68 @@ func Test_Mock_AssertExpectations(t *testing.T) { } +func Test_Mock_AssertExpectations_Placeholder_NoArgs(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertExpectations_Placeholder_NoArgs").Return(5, 6, 7).Once() + mockedService.On("Test_Mock_AssertExpectations_Placeholder_NoArgs").Return(7, 6, 5) + + tt := new(testing.T) + assert.False(t, mockedService.AssertExpectations(tt)) + + // make the call now + mockedService.Called() + + // now assert expectations + assert.True(t, mockedService.AssertExpectations(tt)) + +} + +func Test_Mock_AssertExpectations_Placeholder(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertExpectations_Placeholder", 1, 2, 3).Return(5, 6, 7).Once() + mockedService.On("Test_Mock_AssertExpectations_Placeholder", 3, 2, 1).Return(7, 6, 5) + + tt := new(testing.T) + assert.False(t, mockedService.AssertExpectations(tt)) + + // make the call now + mockedService.Called(1, 2, 3) + + // now assert expectations + assert.False(t, mockedService.AssertExpectations(tt)) + + // make call to the second expectation + mockedService.Called(3, 2, 1) + + // now assert expectations again + assert.True(t, mockedService.AssertExpectations(tt)) +} + +func Test_Mock_AssertExpectations_With_Pointers(t *testing.T) { + + var mockedService = new(TestExampleImplementation) + + mockedService.On("Test_Mock_AssertExpectations_With_Pointers", &struct{ Foo int }{1}).Return(1) + mockedService.On("Test_Mock_AssertExpectations_With_Pointers", &struct{ Foo int }{2}).Return(2) + + tt := new(testing.T) + assert.False(t, mockedService.AssertExpectations(tt)) + + s := struct{ Foo int }{1} + // make the calls now + mockedService.Called(&s) + s.Foo = 2 + mockedService.Called(&s) + + // now assert expectations + assert.True(t, mockedService.AssertExpectations(tt)) + +} + func Test_Mock_AssertExpectationsCustomType(t *testing.T) { var mockedService = new(TestExampleImplementation) diff --git a/vendor/github.com/tendermint/go-crypto/priv_key.go b/vendor/github.com/tendermint/go-crypto/priv_key.go index e9b43caa03f4bf0bfe9063e187f24566b6ed6e27..42f932ba149fef088312ce65cf46c82ee2937e8d 100644 --- a/vendor/github.com/tendermint/go-crypto/priv_key.go +++ b/vendor/github.com/tendermint/go-crypto/priv_key.go @@ -1,6 +1,9 @@ package crypto import ( + "bytes" + + secp256k1 "github.com/btcsuite/btcd/btcec" "github.com/tendermint/ed25519" "github.com/tendermint/ed25519/extra25519" . "github.com/tendermint/go-common" @@ -12,17 +15,20 @@ type PrivKey interface { Bytes() []byte Sign(msg []byte) Signature PubKey() PubKey + Equals(PrivKey) bool } // Types of PrivKey implementations const ( - PrivKeyTypeEd25519 = byte(0x01) + PrivKeyTypeEd25519 = byte(0x01) + PrivKeyTypeSecp256k1 = byte(0x02) ) // for wire.readReflect var _ = wire.RegisterInterface( struct{ PrivKey }{}, wire.ConcreteType{PrivKeyEd25519{}, PrivKeyTypeEd25519}, + wire.ConcreteType{PrivKeySecp256k1{}, PrivKeyTypeSecp256k1}, ) func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) { @@ -39,8 +45,8 @@ func (privKey PrivKeyEd25519) Bytes() []byte { return wire.BinaryBytes(struct{ PrivKey }{privKey}) } -func (key PrivKeyEd25519) Sign(msg []byte) Signature { - privKeyBytes := [64]byte(key) +func (privKey PrivKeyEd25519) Sign(msg []byte) Signature { + privKeyBytes := [64]byte(privKey) signatureBytes := ed25519.Sign(&privKeyBytes, msg) return SignatureEd25519(*signatureBytes) } @@ -50,6 +56,14 @@ func (privKey PrivKeyEd25519) PubKey() PubKey { return PubKeyEd25519(*ed25519.MakePublicKey(&privKeyBytes)) } +func (privKey PrivKeyEd25519) Equals(other PrivKey) bool { + if otherEd, ok := other.(PrivKeyEd25519); ok { + return bytes.Equal(privKey[:], otherEd[:]) + } else { + return false + } +} + func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte { keyCurve25519 := new([32]byte) privKeyBytes := [64]byte(privKey) @@ -62,11 +76,11 @@ func (privKey PrivKeyEd25519) String() string { } // Deterministically generates new priv-key bytes from key. -func (key PrivKeyEd25519) Generate(index int) PrivKeyEd25519 { +func (privKey PrivKeyEd25519) Generate(index int) PrivKeyEd25519 { newBytes := wire.BinarySha256(struct { PrivKey [64]byte Index int - }{key, index}) + }{privKey, index}) var newKey [64]byte copy(newKey[:], newBytes) return PrivKeyEd25519(newKey) @@ -88,3 +102,71 @@ func GenPrivKeyEd25519FromSecret(secret []byte) PrivKeyEd25519 { ed25519.MakePublicKey(privKeyBytes) return PrivKeyEd25519(*privKeyBytes) } + +//------------------------------------- + +// Implements PrivKey +type PrivKeySecp256k1 [32]byte + +func (privKey PrivKeySecp256k1) Bytes() []byte { + return wire.BinaryBytes(struct{ PrivKey }{privKey}) +} + +func (privKey PrivKeySecp256k1) Sign(msg []byte) Signature { + priv__, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) + sig__, err := priv__.Sign(Sha256(msg)) + if err != nil { + PanicSanity(err) + } + return SignatureSecp256k1(sig__.Serialize()) +} + +func (privKey PrivKeySecp256k1) PubKey() PubKey { + _, pub__ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) + pub := [65]byte{} + copy(pub[:], pub__.SerializeUncompressed()) + return PubKeySecp256k1(pub) +} + +func (privKey PrivKeySecp256k1) Equals(other PrivKey) bool { + if otherSecp, ok := other.(PrivKeySecp256k1); ok { + return bytes.Equal(privKey[:], otherSecp[:]) + } else { + return false + } +} + +func (privKey PrivKeySecp256k1) String() string { + return Fmt("PrivKeySecp256k1{*****}") +} + +/* +// Deterministically generates new priv-key bytes from key. +func (key PrivKeySecp256k1) Generate(index int) PrivKeySecp256k1 { + newBytes := wire.BinarySha256(struct { + PrivKey [64]byte + Index int + }{key, index}) + var newKey [64]byte + copy(newKey[:], newBytes) + return PrivKeySecp256k1(newKey) +} +*/ + +func GenPrivKeySecp256k1() PrivKeySecp256k1 { + privKeyBytes := [32]byte{} + copy(privKeyBytes[:], CRandBytes(32)) + priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKeyBytes[:]) + copy(privKeyBytes[:], priv.Serialize()) + return PrivKeySecp256k1(privKeyBytes) +} + +// NOTE: secret should be the output of a KDF like bcrypt, +// if it's derived from user input. +func GenPrivKeySecp256k1FromSecret(secret []byte) PrivKeySecp256k1 { + privKey32 := Sha256(secret) // Not Ripemd160 because we want 32 bytes. + priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey32) + privKeyBytes := [32]byte{} + copy(privKeyBytes[:], priv.Serialize()) + return PrivKeySecp256k1(privKeyBytes) +} diff --git a/vendor/github.com/tendermint/go-crypto/pub_key.go b/vendor/github.com/tendermint/go-crypto/pub_key.go index 6da13077f2d9053b4c642aed457fb6a2ac07808e..de56c81224ed57f87a60c655e606275a1ccdbb70 100644 --- a/vendor/github.com/tendermint/go-crypto/pub_key.go +++ b/vendor/github.com/tendermint/go-crypto/pub_key.go @@ -3,6 +3,7 @@ package crypto import ( "bytes" + secp256k1 "github.com/btcsuite/btcd/btcec" "github.com/tendermint/ed25519" "github.com/tendermint/ed25519/extra25519" . "github.com/tendermint/go-common" @@ -21,13 +22,15 @@ type PubKey interface { // Types of PubKey implementations const ( - PubKeyTypeEd25519 = byte(0x01) + PubKeyTypeEd25519 = byte(0x01) + PubKeyTypeSecp256k1 = byte(0x02) ) // for wire.readReflect var _ = wire.RegisterInterface( struct{ PubKey }{}, wire.ConcreteType{PubKeyEd25519{}, PubKeyTypeEd25519}, + wire.ConcreteType{PubKeySecp256k1{}, PubKeyTypeSecp256k1}, ) func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) { @@ -47,7 +50,7 @@ func (pubKey PubKeyEd25519) Address() []byte { PanicCrisis(*err) } // append type byte - encodedPubkey := append([]byte{1}, w.Bytes()...) + encodedPubkey := append([]byte{PubKeyTypeEd25519}, w.Bytes()...) hasher := ripemd160.New() hasher.Write(encodedPubkey) // does not error return hasher.Sum(nil) @@ -57,7 +60,6 @@ func (pubKey PubKeyEd25519) Bytes() []byte { return wire.BinaryBytes(struct{ PubKey }{pubKey}) } -// TODO: Consider returning a reason for failure, or logging a runtime type mismatch. func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool { sig, ok := sig_.(SignatureEd25519) if !ok { @@ -96,3 +98,59 @@ func (pubKey PubKeyEd25519) Equals(other PubKey) bool { return false } } + +//------------------------------------- + +// Implements PubKey +type PubKeySecp256k1 [65]byte + +func (pubKey PubKeySecp256k1) Address() []byte { + w, n, err := new(bytes.Buffer), new(int), new(error) + wire.WriteBinary(pubKey[:], w, n, err) + if *err != nil { + PanicCrisis(*err) + } + // append type byte + encodedPubkey := append([]byte{PubKeyTypeSecp256k1}, w.Bytes()...) + hasher := ripemd160.New() + hasher.Write(encodedPubkey) // does not error + return hasher.Sum(nil) +} + +func (pubKey PubKeySecp256k1) Bytes() []byte { + return wire.BinaryBytes(struct{ PubKey }{pubKey}) +} + +func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig_ Signature) bool { + pub__, err := secp256k1.ParsePubKey(pubKey[:], secp256k1.S256()) + if err != nil { + return false + } + sig, ok := sig_.(SignatureSecp256k1) + if !ok { + return false + } + sig__, err := secp256k1.ParseDERSignature(sig[:], secp256k1.S256()) + if err != nil { + return false + } + return sig__.Verify(Sha256(msg), pub__) +} + +func (pubKey PubKeySecp256k1) String() string { + return Fmt("PubKeySecp256k1{%X}", pubKey[:]) +} + +// Must return the full bytes in hex. +// Used for map keying, etc. +func (pubKey PubKeySecp256k1) KeyString() string { + return Fmt("%X", pubKey[:]) +} + +func (pubKey PubKeySecp256k1) Equals(other PubKey) bool { + if otherSecp, ok := other.(PubKeySecp256k1); ok { + return bytes.Equal(pubKey[:], otherSecp[:]) + } else { + return false + } +} diff --git a/vendor/github.com/tendermint/go-crypto/signature.go b/vendor/github.com/tendermint/go-crypto/signature.go index 8eafab7cf33ec6ed1615ad7e12ed4342857623dc..e4921ccf7af29386010ee006baf4c29876940a7b 100644 --- a/vendor/github.com/tendermint/go-crypto/signature.go +++ b/vendor/github.com/tendermint/go-crypto/signature.go @@ -16,13 +16,15 @@ type Signature interface { // Types of Signature implementations const ( - SignatureTypeEd25519 = byte(0x01) + SignatureTypeEd25519 = byte(0x01) + SignatureTypeSecp256k1 = byte(0x02) ) // for wire.readReflect var _ = wire.RegisterInterface( struct{ Signature }{}, wire.ConcreteType{SignatureEd25519{}, SignatureTypeEd25519}, + wire.ConcreteType{SignatureSecp256k1{}, SignatureTypeSecp256k1}, ) //------------------------------------- @@ -37,3 +39,16 @@ func (sig SignatureEd25519) Bytes() []byte { func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 } func (sig SignatureEd25519) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) } + +//------------------------------------- + +// Implements Signature +type SignatureSecp256k1 []byte + +func (sig SignatureSecp256k1) Bytes() []byte { + return wire.BinaryBytes(struct{ Signature }{sig}) +} + +func (sig SignatureSecp256k1) IsZero() bool { return len(sig) == 0 } + +func (sig SignatureSecp256k1) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) } diff --git a/vendor/github.com/tendermint/go-crypto/signature_test.go b/vendor/github.com/tendermint/go-crypto/signature_test.go index e4e72e48d36efa332dba250cfc56505539abff0d..88c490f39aff109439a61e1d3516d90817578459 100644 --- a/vendor/github.com/tendermint/go-crypto/signature_test.go +++ b/vendor/github.com/tendermint/go-crypto/signature_test.go @@ -8,7 +8,7 @@ import ( "github.com/tendermint/go-wire" ) -func TestSignAndValidate(t *testing.T) { +func TestSignAndValidateEd25519(t *testing.T) { privKey := GenPrivKeyEd25519() pubKey := privKey.PubKey() @@ -32,7 +32,31 @@ func TestSignAndValidate(t *testing.T) { } } -func TestBinaryDecode(t *testing.T) { +func TestSignAndValidateSecp256k1(t *testing.T) { + + privKey := GenPrivKeySecp256k1() + pubKey := privKey.PubKey() + + msg := CRandBytes(128) + sig := privKey.Sign(msg) + t.Logf("msg: %X, sig: %X", msg, sig) + + // Test the signature + if !pubKey.VerifyBytes(msg, sig) { + t.Errorf("Account message signature verification failed") + } + + // Mutate the signature, just one bit. + sigEd := sig.(SignatureSecp256k1) + sigEd[0] ^= byte(0x01) + sig = Signature(sigEd) + + if pubKey.VerifyBytes(msg, sig) { + t.Errorf("Account message signature verification should have failed but passed instead") + } +} + +func TestBinaryDecodeEd25519(t *testing.T) { privKey := GenPrivKeyEd25519() pubKey := privKey.PubKey() @@ -66,3 +90,34 @@ func TestBinaryDecode(t *testing.T) { t.Errorf("Account message signature verification failed") } } + +func TestBinaryDecodeSecp256k1(t *testing.T) { + + privKey := GenPrivKeySecp256k1() + pubKey := privKey.PubKey() + + msg := CRandBytes(128) + sig := privKey.Sign(msg) + t.Logf("msg: %X, sig: %X", msg, sig) + + buf, n, err := new(bytes.Buffer), new(int), new(error) + wire.WriteBinary(struct{ Signature }{sig}, buf, n, err) + if *err != nil { + t.Fatalf("Failed to write Signature: %v", err) + } + + if buf.Bytes()[0] != SignatureTypeSecp256k1 { + t.Fatalf("Unexpected signature type byte") + } + + sigStruct := struct{ Signature }{} + sig2 := wire.ReadBinary(sigStruct, buf, 0, n, err) + if *err != nil { + t.Fatalf("Failed to read Signature: %v", err) + } + + // Test the signature + if !pubKey.VerifyBytes(msg, sig2.(struct{ Signature }).Signature.(SignatureSecp256k1)) { + t.Errorf("Account message signature verification failed") + } +} diff --git a/vendor/github.com/tendermint/tendermint/node/node.go b/vendor/github.com/tendermint/tendermint/node/node.go index db95fe22f86c5784235b9e2816ccb86e180eb14c..5ad3018001c3be11e91de5478166c893fe6c09ba 100644 --- a/vendor/github.com/tendermint/tendermint/node/node.go +++ b/vendor/github.com/tendermint/tendermint/node/node.go @@ -45,7 +45,10 @@ type Node struct { privKey crypto.PrivKeyEd25519 } -func NewNode(privValidator *types.PrivValidator) *Node { +func NewNode(privValidator *types.PrivValidator, getProxyApp func(proxyAddr string, appHash []byte) proxy.AppConn) *Node { + + EnsureDir(config.GetString("db_dir"), 0700) // incase we use memdb, cswal still gets written here + // Get BlockStore blockStoreDB := dbm.GetDB("blockstore") blockStore := bc.NewBlockStore(blockStoreDB) @@ -249,7 +252,7 @@ func makeNodeInfo(sw *p2p.Switch, privKey crypto.PrivKeyEd25519) *p2p.NodeInfo { // Get a connection to the proxyAppConn addr. // Check the current hash, and panic if it doesn't match. -func getProxyApp(addr string, hash []byte) (proxyAppConn proxy.AppConn) { +func GetProxyApp(addr string, hash []byte) (proxyAppConn proxy.AppConn) { // use local app (for testing) switch addr { case "nilapp": @@ -297,7 +300,7 @@ func getState() *sm.State { // Users wishing to use an external signer for their validators // should fork tendermint/tendermint and implement RunNode to -// load their custom priv validator and call NewNode(privVal) +// load their custom priv validator and call NewNode(privVal, getProxyFunc) func RunNode() { // Wait until the genesis doc becomes available genDocFile := config.GetString("genesis_file") @@ -326,7 +329,7 @@ func RunNode() { privValidator := types.LoadOrGenPrivValidator(privValidatorFile) // Create & start node - n := NewNode(privValidator) + n := NewNode(privValidator, GetProxyApp) l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), config.GetBool("skip_upnp")) n.AddListener(l) err := n.Start() @@ -356,6 +359,14 @@ func RunNode() { }) } +func (n *Node) NodeInfo() *p2p.NodeInfo { + return n.sw.NodeInfo() +} + +func (n *Node) DialSeeds(seeds []string) { + n.sw.DialSeeds(seeds) +} + //------------------------------------------------------------------------------ // replay @@ -372,8 +383,8 @@ func newConsensusState() *consensus.ConsensusState { // Create two proxyAppConn connections, // one for the consensus and one for the mempool. proxyAddr := config.GetString("proxy_app") - proxyAppConnMempool := getProxyApp(proxyAddr, state.AppHash) - proxyAppConnConsensus := getProxyApp(proxyAddr, state.AppHash) + proxyAppConnMempool := GetProxyApp(proxyAddr, state.AppHash) + proxyAppConnConsensus := GetProxyApp(proxyAddr, state.AppHash) // add the chainid to the global config config.Set("chain_id", state.ChainID) diff --git a/vendor/github.com/tendermint/tendermint/node/node_test.go b/vendor/github.com/tendermint/tendermint/node/node_test.go index 09801d50f3653e0be500e4537cdbd83b54fc8f43..0f397f6e2bfac1e5f3838de6f45126bd46bf0108 100644 --- a/vendor/github.com/tendermint/tendermint/node/node_test.go +++ b/vendor/github.com/tendermint/tendermint/node/node_test.go @@ -20,7 +20,7 @@ func TestNodeStartStop(t *testing.T) { privValidator := types.LoadOrGenPrivValidator(privValidatorFile) // Create & start node - n := NewNode(privValidator) + n := NewNode(privValidator, GetProxyApp) l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), config.GetBool("skip_upnp")) n.AddListener(l) n.Start() diff --git a/vendor/github.com/tendermint/tendermint/rpc/test/helpers.go b/vendor/github.com/tendermint/tendermint/rpc/test/helpers.go index 421fdbb89bc85a82468ca1f500ee8da49047670d..48ec8ccbdf9bab6aeb85ca587734d5c2a5e8d71e 100644 --- a/vendor/github.com/tendermint/tendermint/rpc/test/helpers.go +++ b/vendor/github.com/tendermint/tendermint/rpc/test/helpers.go @@ -51,7 +51,7 @@ func newNode(ready chan struct{}) { // Create & start node privValidatorFile := config.GetString("priv_validator_file") privValidator := types.LoadOrGenPrivValidator(privValidatorFile) - node = nm.NewNode(privValidator) + node = nm.NewNode(privValidator, nm.GetProxyApp) l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), true) node.AddListener(l) node.Start() diff --git a/vendor/github.com/tendermint/tmsp/README.md b/vendor/github.com/tendermint/tmsp/README.md index 6c35736e516e1cb39935f2979cc6be1f248b0774..260357056e5f0dfdefe7b5688fff47352826cafd 100644 --- a/vendor/github.com/tendermint/tmsp/README.md +++ b/vendor/github.com/tendermint/tmsp/README.md @@ -6,6 +6,10 @@ to manage a blockchain application state, running in another. For more information on TMSP, motivations, and tutorials, please visit [our blog post](http://tendermint.com/posts/tendermint-socket-protocol/). +Other implementations: +* [cpp-tmsp](https://github.com/mdyring/cpp-tmsp) by Martin Dyring-Andersen +* [js-tmsp](https://github.com/tendermint/js-tmsp) + ## Message types TMSP requests/responses are simple Protobuf messages. Check out the [schema file](https://github.com/tendermint/tmsp/blob/master/types/types.proto). diff --git a/vendor/github.com/tendermint/tmsp/types/errors.go b/vendor/github.com/tendermint/tmsp/types/errors.go index 475e92fe632c8acb060878f92ba9e8dac3cb1944..7fa1f0090f549c15885c53196c1bd70bebfaebe9 100644 --- a/vendor/github.com/tendermint/tmsp/types/errors.go +++ b/vendor/github.com/tendermint/tmsp/types/errors.go @@ -15,8 +15,8 @@ var ( ErrBaseInsufficientFees = NewError(CodeType_BaseInsufficientFees, "Error (base) insufficient fees") ErrBaseInsufficientFunds = NewError(CodeType_BaseInsufficientFunds, "Error (base) insufficient funds") ErrBaseInsufficientGasPrice = NewError(CodeType_BaseInsufficientGasPrice, "Error (base) insufficient gas price") - ErrBaseInvalidAddress = NewError(CodeType_BaseInvalidAddress, "Error (base) invalid address") - ErrBaseInvalidAmount = NewError(CodeType_BaseInvalidAmount, "Error (base) invalid amount") + ErrBaseInvalidInput = NewError(CodeType_BaseInvalidInput, "Error (base) invalid input") + ErrBaseInvalidOutput = NewError(CodeType_BaseInvalidOutput, "Error (base) invalid output") ErrBaseInvalidPubKey = NewError(CodeType_BaseInvalidPubKey, "Error (base) invalid pubkey") ErrBaseInvalidSequence = NewError(CodeType_BaseInvalidSequence, "Error (base) invalid sequence") ErrBaseInvalidSignature = NewError(CodeType_BaseInvalidSignature, "Error (base) invalid signature") diff --git a/vendor/github.com/tendermint/tmsp/types/types.pb.go b/vendor/github.com/tendermint/tmsp/types/types.pb.go index 75bf1b79c533459a00ccd79319b747c7d6426a0a..933e62ca066dfbb149e6aa7bc4b5a5a59ddb54cd 100644 --- a/vendor/github.com/tendermint/tmsp/types/types.pb.go +++ b/vendor/github.com/tendermint/tmsp/types/types.pb.go @@ -95,8 +95,8 @@ const ( CodeType_BaseInsufficientFees CodeType = 103 CodeType_BaseInsufficientFunds CodeType = 104 CodeType_BaseInsufficientGasPrice CodeType = 105 - CodeType_BaseInvalidAddress CodeType = 106 - CodeType_BaseInvalidAmount CodeType = 107 + CodeType_BaseInvalidInput CodeType = 106 + CodeType_BaseInvalidOutput CodeType = 107 CodeType_BaseInvalidPubKey CodeType = 108 CodeType_BaseInvalidSequence CodeType = 109 CodeType_BaseInvalidSignature CodeType = 110 @@ -129,8 +129,8 @@ var CodeType_name = map[int32]string{ 103: "BaseInsufficientFees", 104: "BaseInsufficientFunds", 105: "BaseInsufficientGasPrice", - 106: "BaseInvalidAddress", - 107: "BaseInvalidAmount", + 106: "BaseInvalidInput", + 107: "BaseInvalidOutput", 108: "BaseInvalidPubKey", 109: "BaseInvalidSequence", 110: "BaseInvalidSignature", @@ -161,8 +161,8 @@ var CodeType_value = map[string]int32{ "BaseInsufficientFees": 103, "BaseInsufficientFunds": 104, "BaseInsufficientGasPrice": 105, - "BaseInvalidAddress": 106, - "BaseInvalidAmount": 107, + "BaseInvalidInput": 106, + "BaseInvalidOutput": 107, "BaseInvalidPubKey": 108, "BaseInvalidSequence": 109, "BaseInvalidSignature": 110, @@ -247,51 +247,51 @@ func init() { } var fileDescriptor0 = []byte{ - // 722 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x54, 0x4d, 0x53, 0xeb, 0x36, - 0x14, 0xad, 0x13, 0x27, 0x24, 0x37, 0x10, 0x14, 0x91, 0x80, 0xdb, 0xe9, 0x82, 0xa1, 0x33, 0x1d, - 0x86, 0x05, 0xed, 0xd0, 0x55, 0x97, 0x24, 0x0d, 0x4c, 0x86, 0x01, 0x52, 0xf3, 0xb1, 0x37, 0xf6, - 0x4d, 0xac, 0xc6, 0x91, 0x8c, 0x2d, 0x03, 0xe9, 0x1f, 0x63, 0xa6, 0xfb, 0x2e, 0xde, 0xf7, 0xc7, - 0x2f, 0x7a, 0x92, 0x3f, 0x92, 0x90, 0xb7, 0x78, 0x8b, 0xb7, 0xc9, 0xe8, 0x9c, 0x23, 0xdd, 0x7b, - 0xce, 0x95, 0x62, 0x68, 0xc9, 0x59, 0x88, 0xf1, 0x6f, 0xe9, 0xef, 0x61, 0x18, 0x09, 0x29, 0x68, - 0x25, 0x05, 0x7b, 0xcf, 0x06, 0xac, 0xd9, 0x78, 0x9f, 0x60, 0x2c, 0xe9, 0xaf, 0x60, 0x6a, 0xd2, - 0x32, 0x76, 0x8d, 0xfd, 0xe6, 0x11, 0x3d, 0xcc, 0xb6, 0x9f, 0x63, 0x1c, 0x3b, 0x63, 0xbc, 0x56, - 0xc0, 0x4e, 0x75, 0x4a, 0xc1, 0xf4, 0x1c, 0xe9, 0x58, 0x25, 0xb5, 0x6f, 0xdd, 0x4e, 0xd7, 0x94, - 0x40, 0x79, 0x82, 0x33, 0xab, 0xac, 0xa8, 0xba, 0xad, 0x97, 0xb4, 0x0d, 0x95, 0x07, 0x27, 0x48, - 0xd0, 0x32, 0x53, 0x2e, 0x03, 0xf4, 0x77, 0x00, 0xb5, 0x60, 0xea, 0x8c, 0x88, 0x62, 0xab, 0xb2, - 0x5b, 0xde, 0x6f, 0x1c, 0x91, 0xbc, 0xd3, 0x6d, 0x21, 0xd8, 0x4b, 0x7b, 0xe8, 0x36, 0x54, 0x7d, - 0x64, 0x63, 0x5f, 0x5a, 0x55, 0x55, 0xc8, 0xb4, 0x73, 0xb4, 0xf7, 0xbf, 0x01, 0x35, 0x1b, 0xe3, - 0x50, 0xf0, 0x18, 0xbf, 0xcb, 0xfa, 0x2f, 0x60, 0xba, 0xc2, 0xc3, 0xd4, 0x7b, 0xf3, 0x68, 0x33, - 0x3f, 0xdb, 0x53, 0x54, 0x76, 0x50, 0x8b, 0x3a, 0x0d, 0x46, 0x91, 0x88, 0x8a, 0x34, 0x29, 0xd0, - 0xa9, 0x03, 0x31, 0x56, 0x31, 0xd2, 0xd4, 0x6a, 0xb9, 0x92, 0xaf, 0xfa, 0xed, 0x7c, 0x7b, 0x7f, - 0x42, 0x7d, 0x2e, 0xe8, 0xb0, 0x61, 0x72, 0x77, 0xa6, 0x26, 0x69, 0xa4, 0x0e, 0x73, 0xa4, 0xdb, - 0x87, 0xe2, 0x11, 0xa3, 0xd4, 0xb8, 0x69, 0x67, 0xe0, 0xe0, 0x3f, 0x03, 0x1a, 0x4b, 0x19, 0xe9, - 0x26, 0x34, 0x2e, 0x92, 0x20, 0xc8, 0x29, 0xf2, 0x03, 0xad, 0x81, 0xd9, 0x77, 0x7d, 0x41, 0x0c, - 0x5a, 0x87, 0xca, 0x49, 0x90, 0xc4, 0x3e, 0x29, 0x69, 0x72, 0xc0, 0x47, 0x82, 0x94, 0xe9, 0x06, - 0xd4, 0xaf, 0x50, 0x5e, 0x86, 0x92, 0x09, 0x4e, 0x4c, 0x0d, 0xfb, 0x4f, 0x2e, 0x66, 0xb0, 0x42, - 0xd7, 0xa1, 0x76, 0x1c, 0x86, 0xc8, 0xbd, 0xeb, 0x27, 0xd2, 0xa2, 0x0d, 0x58, 0xeb, 0xf9, 0xe8, - 0x4e, 0x14, 0x50, 0x53, 0x84, 0x6a, 0x4f, 0x4c, 0xa7, 0x4c, 0x92, 0x2d, 0x5d, 0xf9, 0xef, 0x04, - 0xa3, 0x19, 0x69, 0xeb, 0x02, 0x03, 0xce, 0x64, 0xcf, 0x77, 0x18, 0x27, 0x1d, 0xda, 0x04, 0xe8, - 0xe2, 0x98, 0xf1, 0x6e, 0x20, 0xdc, 0x09, 0xd9, 0xd6, 0x05, 0xfb, 0xdc, 0xcb, 0xd0, 0xce, 0xc1, - 0x73, 0x05, 0x6a, 0xc5, 0x90, 0x69, 0x15, 0x4a, 0x97, 0x67, 0xca, 0x70, 0x0b, 0x36, 0x06, 0x5c, - 0x62, 0xc4, 0x9d, 0xa0, 0xaf, 0x27, 0xac, 0x9c, 0x2b, 0xaa, 0xcf, 0xd5, 0x1d, 0x30, 0x3e, 0xce, - 0xa8, 0x92, 0x2e, 0xd4, 0x75, 0xbc, 0x0b, 0xc1, 0x5d, 0x54, 0x29, 0x08, 0xac, 0xdf, 0x70, 0x27, - 0x91, 0xbe, 0x88, 0xd8, 0xbf, 0xe8, 0xa9, 0x20, 0x1d, 0x68, 0x0d, 0x78, 0x9c, 0x8c, 0x46, 0xcc, - 0x65, 0xc8, 0xe5, 0x49, 0xc2, 0xbd, 0x58, 0x05, 0xa2, 0xd0, 0xbc, 0xe1, 0x13, 0x2e, 0x1e, 0x79, - 0xfe, 0xe2, 0x49, 0x95, 0x5a, 0xd0, 0xee, 0x3a, 0x31, 0xfe, 0x95, 0x84, 0x01, 0x73, 0x1d, 0x89, - 0xc7, 0x9e, 0x17, 0xa9, 0xf1, 0x11, 0xd4, 0x45, 0xb4, 0xf2, 0xb2, 0xf7, 0xa8, 0x38, 0xf0, 0xa2, - 0x3e, 0x62, 0x4c, 0xc6, 0xf4, 0x47, 0xe8, 0x7c, 0xa5, 0xa4, 0x9d, 0x7d, 0xfa, 0x33, 0x58, 0xab, - 0xd2, 0xa9, 0x13, 0x0f, 0x23, 0xa6, 0x02, 0x30, 0x75, 0xe9, 0x34, 0x53, 0xd3, 0x57, 0x51, 0x38, - 0xf8, 0xa7, 0x70, 0x50, 0xf0, 0x53, 0x91, 0x70, 0x49, 0x26, 0x2b, 0xf4, 0x30, 0x7d, 0x20, 0x24, - 0xa0, 0x3b, 0xb0, 0xb5, 0x44, 0x5f, 0xe9, 0x84, 0x7a, 0x3e, 0xd3, 0x85, 0xe3, 0x4c, 0x60, 0x63, - 0xee, 0xc8, 0x24, 0x42, 0xc2, 0x8b, 0xc6, 0xf9, 0x50, 0x8a, 0xc6, 0xa2, 0xe8, 0x90, 0xf3, 0x79, - 0x87, 0x70, 0x95, 0x0e, 0x12, 0x75, 0xb7, 0xe4, 0x5e, 0xd1, 0xe4, 0x54, 0x3c, 0xe4, 0x6c, 0x9f, - 0x4b, 0x26, 0x67, 0xe4, 0x95, 0xa1, 0x9e, 0xec, 0xe6, 0x82, 0x3e, 0x8d, 0x44, 0x12, 0x92, 0xd7, - 0x86, 0x72, 0x49, 0x17, 0xec, 0x30, 0x12, 0xa1, 0x88, 0x9d, 0x80, 0xbc, 0x31, 0x94, 0x97, 0x96, - 0x12, 0xe6, 0xf7, 0x90, 0x1d, 0x78, 0x5b, 0x1c, 0x98, 0xf3, 0xe7, 0x38, 0xbd, 0xc3, 0x88, 0xbc, - 0x33, 0xd4, 0xb8, 0xdb, 0xcb, 0xc2, 0xbc, 0xd6, 0x7b, 0x23, 0x77, 0x34, 0x97, 0x6e, 0x85, 0x44, - 0xf2, 0xa1, 0xa0, 0xf3, 0x39, 0xe4, 0x85, 0x3e, 0x1a, 0x74, 0x0b, 0x9a, 0x0b, 0x3a, 0xdd, 0xfb, - 0xc9, 0xa0, 0x3f, 0x41, 0xe7, 0x05, 0xa9, 0x5e, 0xc0, 0x50, 0xff, 0xe7, 0xc8, 0x67, 0xe3, 0xae, - 0x9a, 0x7e, 0x41, 0xff, 0xf8, 0x12, 0x00, 0x00, 0xff, 0xff, 0x65, 0x9a, 0x2e, 0x13, 0x56, 0x05, - 0x00, 0x00, + // 729 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x94, 0x4b, 0x4f, 0xeb, 0x46, + 0x14, 0xc7, 0xeb, 0xc4, 0x09, 0xc9, 0x09, 0x84, 0xc9, 0x90, 0x80, 0x5b, 0x75, 0x81, 0xa8, 0x54, + 0x21, 0x16, 0xb4, 0xa2, 0xab, 0x2e, 0x49, 0x1a, 0x50, 0x84, 0x80, 0xd4, 0x3c, 0xf6, 0xc6, 0x3e, + 0x89, 0xa7, 0x71, 0x66, 0x8c, 0x3d, 0x06, 0xd2, 0xef, 0x55, 0x55, 0xba, 0xfb, 0xbb, 0xb8, 0xef, + 0xc7, 0x27, 0xba, 0x33, 0x7e, 0x24, 0x21, 0x77, 0x71, 0x17, 0x77, 0x13, 0xcd, 0xf9, 0xfd, 0x67, + 0xce, 0x39, 0xff, 0x33, 0x13, 0x43, 0x4b, 0xce, 0x42, 0x8c, 0x7f, 0x4b, 0x7f, 0x0f, 0xc3, 0x48, + 0x48, 0x41, 0x2b, 0x69, 0xb0, 0xf7, 0xbf, 0x01, 0x6b, 0x36, 0xde, 0x27, 0x18, 0x4b, 0xfa, 0x2b, + 0x98, 0x1a, 0x5a, 0xc6, 0xae, 0xb1, 0xdf, 0x3c, 0xa2, 0x87, 0xd9, 0xf6, 0x73, 0x8c, 0x63, 0x67, + 0x8c, 0xd7, 0x2a, 0xb0, 0x53, 0x9d, 0x52, 0x30, 0x3d, 0x47, 0x3a, 0x56, 0x49, 0xed, 0x5b, 0xb7, + 0xd3, 0x35, 0x25, 0x50, 0x9e, 0xe0, 0xcc, 0x2a, 0x2b, 0x54, 0xb7, 0xf5, 0x92, 0xb6, 0xa1, 0xf2, + 0xe0, 0x04, 0x09, 0x5a, 0x66, 0xca, 0xb2, 0x80, 0xfe, 0x0e, 0xa0, 0x16, 0x4c, 0x9d, 0x11, 0x51, + 0x6c, 0x55, 0x76, 0xcb, 0xfb, 0x8d, 0x23, 0x92, 0x57, 0xba, 0x2d, 0x04, 0x7b, 0x69, 0x0f, 0xdd, + 0x86, 0xaa, 0x8f, 0x6c, 0xec, 0x4b, 0xab, 0xaa, 0x12, 0x99, 0x76, 0x1e, 0xed, 0xbd, 0x34, 0xa0, + 0x66, 0x63, 0x1c, 0x0a, 0x1e, 0xe3, 0x77, 0xb5, 0xfe, 0x0b, 0x98, 0xae, 0xf0, 0x30, 0xed, 0xbd, + 0x79, 0xb4, 0x99, 0x9f, 0xed, 0x29, 0x94, 0x1d, 0xd4, 0xa2, 0x76, 0x83, 0x51, 0x24, 0xa2, 0xc2, + 0x4d, 0x1a, 0x68, 0xd7, 0x81, 0x18, 0x2b, 0x1b, 0xa9, 0x6b, 0xb5, 0x5c, 0xf1, 0x57, 0xfd, 0xb6, + 0xbf, 0xbd, 0x3f, 0xa1, 0x3e, 0x17, 0xb4, 0xd9, 0x30, 0xb9, 0x3b, 0x53, 0x93, 0x34, 0xd2, 0x0e, + 0xf3, 0x48, 0x97, 0x0f, 0xc5, 0x23, 0x46, 0x69, 0xe3, 0xa6, 0x9d, 0x05, 0x07, 0x2f, 0x0c, 0x68, + 0x2c, 0x79, 0xa4, 0x9b, 0xd0, 0xb8, 0x48, 0x82, 0x20, 0x47, 0xe4, 0x07, 0x5a, 0x03, 0xb3, 0xef, + 0xfa, 0x82, 0x18, 0xb4, 0x0e, 0x95, 0x93, 0x20, 0x89, 0x7d, 0x52, 0xd2, 0x70, 0xc0, 0x47, 0x82, + 0x94, 0xe9, 0x06, 0xd4, 0xaf, 0x50, 0x5e, 0x86, 0x92, 0x09, 0x4e, 0x4c, 0x1d, 0xf6, 0x9f, 0x5c, + 0xcc, 0xc2, 0x0a, 0x5d, 0x87, 0xda, 0x71, 0x18, 0x22, 0xf7, 0xae, 0x9f, 0x48, 0x8b, 0x36, 0x60, + 0xad, 0xe7, 0xa3, 0x3b, 0x51, 0x81, 0x9a, 0x22, 0x54, 0x7b, 0x62, 0x3a, 0x65, 0x92, 0x6c, 0xe9, + 0xcc, 0x7f, 0x27, 0x18, 0xcd, 0x48, 0x5b, 0x27, 0x18, 0x70, 0x26, 0x7b, 0xbe, 0xc3, 0x38, 0xe9, + 0xd0, 0x26, 0x40, 0x17, 0xc7, 0x8c, 0x77, 0x03, 0xe1, 0x4e, 0xc8, 0xb6, 0x4e, 0xd8, 0xe7, 0x5e, + 0x16, 0xed, 0x1c, 0xfc, 0x57, 0x81, 0x5a, 0x31, 0x64, 0x5a, 0x85, 0xd2, 0xe5, 0x99, 0x6a, 0xb8, + 0x05, 0x1b, 0x03, 0x2e, 0x31, 0xe2, 0x4e, 0xd0, 0xd7, 0x13, 0x56, 0x9d, 0x2b, 0xd4, 0xe7, 0xea, + 0x0e, 0x18, 0x1f, 0x67, 0xa8, 0xa4, 0x13, 0x75, 0x1d, 0xef, 0x42, 0x70, 0x17, 0x95, 0x0b, 0x02, + 0xeb, 0x37, 0xdc, 0x49, 0xa4, 0x2f, 0x22, 0xf6, 0x2f, 0x7a, 0xca, 0x48, 0x07, 0x5a, 0x03, 0x1e, + 0x27, 0xa3, 0x11, 0x73, 0x19, 0x72, 0x79, 0x92, 0x70, 0x2f, 0x56, 0x86, 0x28, 0x34, 0x6f, 0xf8, + 0x84, 0x8b, 0x47, 0x9e, 0xbf, 0x78, 0x52, 0xa5, 0x16, 0xb4, 0xbb, 0x4e, 0x8c, 0x7f, 0x25, 0x61, + 0xc0, 0x5c, 0x47, 0xe2, 0xb1, 0xe7, 0x45, 0x6a, 0x7c, 0x04, 0x75, 0x12, 0xad, 0x3c, 0xaf, 0x3d, + 0x2a, 0x0e, 0x3c, 0xcb, 0x8f, 0x18, 0x93, 0x31, 0xfd, 0x11, 0x3a, 0x5f, 0x29, 0x69, 0x65, 0x9f, + 0xfe, 0x0c, 0xd6, 0xaa, 0x74, 0xea, 0xc4, 0xc3, 0x88, 0x29, 0x03, 0x4c, 0x5d, 0x2e, 0xc9, 0xd4, + 0xf4, 0x55, 0x0c, 0x78, 0x98, 0x48, 0xf2, 0x4f, 0x51, 0x3f, 0xa7, 0x97, 0x89, 0xd4, 0x78, 0xb2, + 0x82, 0x87, 0xe9, 0xf3, 0x20, 0x01, 0xdd, 0x81, 0xad, 0x25, 0x7c, 0xa5, 0xfd, 0xe9, 0xe9, 0x4c, + 0x17, 0xfd, 0x66, 0x02, 0x1b, 0x73, 0x47, 0x26, 0x11, 0x12, 0xae, 0xde, 0x1a, 0xd5, 0x4a, 0x3e, + 0x92, 0xc2, 0xb8, 0x28, 0x2a, 0xe4, 0x3c, 0xaf, 0x10, 0xae, 0xe2, 0x20, 0x51, 0x37, 0x4b, 0xee, + 0x15, 0x26, 0xa7, 0xe2, 0x21, 0xa7, 0x7d, 0x2e, 0x99, 0x9c, 0x91, 0x57, 0x86, 0xf2, 0xb4, 0xb9, + 0xc0, 0xa7, 0x91, 0x48, 0x42, 0xf2, 0xda, 0x50, 0x5d, 0xd2, 0x05, 0x1d, 0x46, 0x22, 0x14, 0xb1, + 0x13, 0x90, 0x37, 0x86, 0xea, 0xa5, 0xa5, 0x84, 0xf9, 0x2d, 0x64, 0x07, 0xde, 0x16, 0x07, 0xe6, + 0xfc, 0x1c, 0xa7, 0x77, 0x18, 0x91, 0x77, 0x86, 0x1a, 0x76, 0x7b, 0x59, 0x98, 0xe7, 0x7a, 0x6f, + 0xe4, 0x1d, 0xcd, 0xa5, 0x5b, 0x21, 0x91, 0x7c, 0x28, 0x70, 0x3e, 0x87, 0x3c, 0xd1, 0x47, 0x83, + 0x6e, 0x41, 0x73, 0x81, 0xd3, 0xbd, 0x9f, 0x0c, 0xfa, 0x13, 0x74, 0x9e, 0x41, 0x75, 0xff, 0x43, + 0xfd, 0x8f, 0x23, 0x9f, 0x8d, 0xbb, 0x6a, 0xfa, 0xfd, 0xfc, 0xe3, 0x4b, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xc9, 0xcf, 0x96, 0x2d, 0x54, 0x05, 0x00, 0x00, } diff --git a/vendor/github.com/tendermint/tmsp/types/types.proto b/vendor/github.com/tendermint/tmsp/types/types.proto index 8bead59a6f463f3d147bee43edac22636df86707..37a4fa6a0162c754b6be928d2000450a49f7b125 100644 --- a/vendor/github.com/tendermint/tmsp/types/types.proto +++ b/vendor/github.com/tendermint/tmsp/types/types.proto @@ -43,8 +43,8 @@ enum CodeType { BaseInsufficientFees = 103; BaseInsufficientFunds = 104; BaseInsufficientGasPrice = 105; - BaseInvalidAddress = 106; - BaseInvalidAmount = 107; + BaseInvalidInput = 106; + BaseInvalidOutput = 107; BaseInvalidPubKey = 108; BaseInvalidSequence = 109; BaseInvalidSignature = 110; diff --git a/vendor/golang.org/x/crypto/acme/internal/acme/acme.go b/vendor/golang.org/x/crypto/acme/internal/acme/acme.go new file mode 100644 index 0000000000000000000000000000000000000000..2732999f350585164f6d9b34549c6a72cb776299 --- /dev/null +++ b/vendor/golang.org/x/crypto/acme/internal/acme/acme.go @@ -0,0 +1,473 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package acme provides an ACME client implementation. +// See https://ietf-wg-acme.github.io/acme/ for details. +// +// This package is a work in progress and makes no API stability promises. +package acme + +import ( + "bytes" + "crypto/rsa" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "strconv" + "strings" + "time" + + "golang.org/x/net/context" +) + +// Client is an ACME client. +type Client struct { + // HTTPClient optionally specifies an HTTP client to use + // instead of http.DefaultClient. + HTTPClient *http.Client + + // Key is the account key used to register with a CA + // and sign requests. + Key *rsa.PrivateKey +} + +// Discover performs ACME server discovery using the provided discovery endpoint URL. +func (c *Client) Discover(url string) (*Directory, error) { + res, err := c.httpClient().Get(url) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return nil, responseError(res) + } + var v struct { + Reg string `json:"new-reg"` + Authz string `json:"new-authz"` + Cert string `json:"new-cert"` + Revoke string `json:"revoke-cert"` + Meta struct { + Terms string `json:"terms-of-service"` + Website string `json:"website"` + CAA []string `json:"caa-identities"` + } + } + if json.NewDecoder(res.Body).Decode(&v); err != nil { + return nil, err + } + return &Directory{ + RegURL: v.Reg, + AuthzURL: v.Authz, + CertURL: v.Cert, + RevokeURL: v.Revoke, + Terms: v.Meta.Terms, + Website: v.Meta.Website, + CAA: v.Meta.CAA, + }, nil +} + +// CreateCert requests a new certificate. +// In the case where CA server does not provide the issued certificate in the response, +// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips. +// In such scenario the caller can cancel the polling with ctx. +// +// If the bundle is true, the returned value will also contain CA (the issuer) certificate. +// The url argument is an Directory.CertURL value, typically obtained from c.Discover. +// The csr is a DER encoded certificate signing request. +func (c *Client) CreateCert(ctx context.Context, url string, csr []byte, exp time.Duration, bundle bool) (der [][]byte, certURL string, err error) { + req := struct { + Resource string `json:"resource"` + CSR string `json:"csr"` + NotBefore string `json:"notBefore,omitempty"` + NotAfter string `json:"notAfter,omitempty"` + }{ + Resource: "new-cert", + CSR: base64.RawURLEncoding.EncodeToString(csr), + } + now := timeNow() + req.NotBefore = now.Format(time.RFC3339) + if exp > 0 { + req.NotAfter = now.Add(exp).Format(time.RFC3339) + } + + res, err := c.postJWS(url, req) + if err != nil { + return nil, "", err + } + defer res.Body.Close() + if res.StatusCode != http.StatusCreated { + return nil, "", responseError(res) + } + + curl := res.Header.Get("location") // cert permanent URL + if res.ContentLength == 0 { + // no cert in the body; poll until we get it + cert, err := c.FetchCert(ctx, curl, bundle) + return cert, curl, err + } + // slurp issued cert and ca, if requested + cert, err := responseCert(c.httpClient(), res, bundle) + return cert, curl, err +} + +// FetchCert retrieves already issued certificate from the given url, in DER format. +// It retries the request until the certificate is successfully retrieved, +// context is cancelled by the caller or an error response is received. +// +// The returned value will also contain CA (the issuer) certificate if bundle == true. +// +// http.DefaultClient is used if client argument is nil. +func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) { + for { + res, err := c.httpClient().Get(url) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode == http.StatusOK { + return responseCert(c.httpClient(), res, bundle) + } + if res.StatusCode > 299 { + return nil, responseError(res) + } + d, err := retryAfter(res.Header.Get("retry-after")) + if err != nil { + d = 3 * time.Second + } + select { + case <-time.After(d): + // retry + case <-ctx.Done(): + return nil, ctx.Err() + } + } +} + +// Register creates a new account registration by following the "new-reg" flow. +// It returns registered account. The a argument is not modified. +// +// The url argument is typically an Directory.RegURL obtained from c.Discover. +func (c *Client) Register(url string, a *Account) (*Account, error) { + return c.doReg(url, "new-reg", a) +} + +// GetReg retrieves an existing registration. +// The url argument is an Account.URI, typically obtained from c.Register. +func (c *Client) GetReg(url string) (*Account, error) { + a := &Account{URI: url} + return c.doReg(url, "reg", a) +} + +// UpdateReg updates an existing registration. +// It returns an updated account copy. The provided account is not modified. +// +// The url argument is an Account.URI, usually obtained with c.Register. +func (c *Client) UpdateReg(url string, a *Account) (*Account, error) { + return c.doReg(url, "reg", a) +} + +// Authorize performs the initial step in an authorization flow. +// The caller will then need to choose from and perform a set of returned +// challenges using c.Accept in order to successfully complete authorization. +// +// The url argument is an authz URL, usually obtained with c.Register. +func (c *Client) Authorize(url, domain string) (*Authorization, error) { + type authzID struct { + Type string `json:"type"` + Value string `json:"value"` + } + req := struct { + Resource string `json:"resource"` + Identifier authzID `json:"identifier"` + }{ + Resource: "new-authz", + Identifier: authzID{Type: "dns", Value: domain}, + } + res, err := c.postJWS(url, req) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusCreated { + return nil, responseError(res) + } + + var v wireAuthz + if err := json.NewDecoder(res.Body).Decode(&v); err != nil { + return nil, fmt.Errorf("Decode: %v", err) + } + if v.Status != StatusPending { + return nil, fmt.Errorf("Unexpected status: %s", v.Status) + } + return v.authorization(res.Header.Get("Location")), nil +} + +// GetAuthz retrieves the current status of an authorization flow. +// +// A client typically polls an authz status using this method. +func (c *Client) GetAuthz(url string) (*Authorization, error) { + res, err := c.httpClient().Get(url) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { + return nil, responseError(res) + } + var v wireAuthz + if err := json.NewDecoder(res.Body).Decode(&v); err != nil { + return nil, fmt.Errorf("Decode: %v", err) + } + return v.authorization(url), nil +} + +// GetChallenge retrieves the current status of an challenge. +// +// A client typically polls a challenge status using this method. +func (c *Client) GetChallenge(url string) (*Challenge, error) { + res, err := c.httpClient().Get(url) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { + return nil, responseError(res) + } + v := wireChallenge{URI: url} + if err := json.NewDecoder(res.Body).Decode(&v); err != nil { + return nil, fmt.Errorf("Decode: %v", err) + } + return v.challenge(), nil +} + +// Accept informs the server that the client accepts one of its challenges +// previously obtained with c.Authorize. +// +// The server will then perform the validation asynchronously. +func (c *Client) Accept(chal *Challenge) (*Challenge, error) { + req := struct { + Resource string `json:"resource"` + Type string `json:"type"` + Auth string `json:"keyAuthorization"` + }{ + Resource: "challenge", + Type: chal.Type, + Auth: keyAuth(&c.Key.PublicKey, chal.Token), + } + res, err := c.postJWS(chal.URI, req) + if err != nil { + return nil, err + } + defer res.Body.Close() + // Note: the protocol specifies 200 as the expected response code, but + // letsencrypt seems to be returning 202. + if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { + return nil, responseError(res) + } + + var v wireChallenge + if err := json.NewDecoder(res.Body).Decode(&v); err != nil { + return nil, fmt.Errorf("Decode: %v", err) + } + return v.challenge(), nil +} + +// HTTP01Handler creates a new handler which responds to a http-01 challenge. +// The token argument is a Challenge.Token value. +func (c *Client) HTTP01Handler(token string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !strings.HasSuffix(r.URL.Path, token) { + w.WriteHeader(http.StatusNotFound) + return + } + w.Header().Set("content-type", "text/plain") + w.Write([]byte(keyAuth(&c.Key.PublicKey, token))) + }) +} + +func (c *Client) httpClient() *http.Client { + if c.HTTPClient != nil { + return c.HTTPClient + } + return http.DefaultClient +} + +// postJWS signs body and posts it to the provided url. +// The body argument must be JSON-serializable. +func (c *Client) postJWS(url string, body interface{}) (*http.Response, error) { + nonce, err := fetchNonce(c.httpClient(), url) + if err != nil { + return nil, err + } + b, err := jwsEncodeJSON(body, c.Key, nonce) + if err != nil { + return nil, err + } + req, err := http.NewRequest("POST", url, bytes.NewReader(b)) + if err != nil { + return nil, err + } + return c.httpClient().Do(req) +} + +// doReg sends all types of registration requests. +// The type of request is identified by typ argument, which is a "resource" +// in the ACME spec terms. +// +// A non-nil acct argument indicates whether the intention is to mutate data +// of the Account. Only Contact and Agreement of its fields are used +// in such cases. +// +// The fields of acct will be populate with the server response +// and may be overwritten. +func (c *Client) doReg(url string, typ string, acct *Account) (*Account, error) { + req := struct { + Resource string `json:"resource"` + Contact []string `json:"contact,omitempty"` + Agreement string `json:"agreement,omitempty"` + }{ + Resource: typ, + } + if acct != nil { + req.Contact = acct.Contact + req.Agreement = acct.AgreedTerms + } + res, err := c.postJWS(url, req) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode < 200 || res.StatusCode > 299 { + return nil, responseError(res) + } + + var v struct { + Contact []string + Agreement string + Authorizations string + Certificates string + } + if err := json.NewDecoder(res.Body).Decode(&v); err != nil { + return nil, fmt.Errorf("Decode: %v", err) + } + return &Account{ + URI: res.Header.Get("Location"), + Contact: v.Contact, + AgreedTerms: v.Agreement, + CurrentTerms: linkHeader(res.Header, "terms-of-service"), + Authz: linkHeader(res.Header, "next"), + Authorizations: v.Authorizations, + Certificates: v.Certificates, + }, nil +} + +func responseCert(client *http.Client, res *http.Response, bundle bool) ([][]byte, error) { + b, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, fmt.Errorf("ReadAll: %v", err) + } + cert := [][]byte{b} + if !bundle { + return cert, nil + } + + // append ca cert + up := linkHeader(res.Header, "up") + if up == "" { + return nil, errors.New("rel=up link not found") + } + res, err = client.Get(up) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return nil, responseError(res) + } + b, err = ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + return append(cert, b), nil +} + +// responseError creates an error of Error type from resp. +func responseError(resp *http.Response) error { + // don't care if ReadAll returns an error: + // json.Unmarshal will fail in that case anyway + b, _ := ioutil.ReadAll(resp.Body) + e := struct { + Status int + Type string + Detail string + }{ + Status: resp.StatusCode, + } + if err := json.Unmarshal(b, &e); err != nil { + // this is not a regular error response: + // populate detail with anything we received, + // e.Status will already contain HTTP response code value + e.Detail = string(b) + if e.Detail == "" { + e.Detail = resp.Status + } + } + return &Error{ + StatusCode: e.Status, + ProblemType: e.Type, + Detail: e.Detail, + Header: resp.Header, + } +} + +func fetchNonce(client *http.Client, url string) (string, error) { + resp, err := client.Head(url) + if err != nil { + return "", nil + } + defer resp.Body.Close() + enc := resp.Header.Get("replay-nonce") + if enc == "" { + return "", errors.New("nonce not found") + } + return enc, nil +} + +func linkHeader(h http.Header, rel string) string { + for _, v := range h["Link"] { + parts := strings.Split(v, ";") + for _, p := range parts { + p = strings.TrimSpace(p) + if !strings.HasPrefix(p, "rel=") { + continue + } + if v := strings.Trim(p[4:], `"`); v == rel { + return strings.Trim(parts[0], "<>") + } + } + } + return "" +} + +func retryAfter(v string) (time.Duration, error) { + if i, err := strconv.Atoi(v); err == nil { + return time.Duration(i) * time.Second, nil + } + t, err := http.ParseTime(v) + if err != nil { + return 0, err + } + return t.Sub(timeNow()), nil +} + +// keyAuth generates a key authorization string for a given token. +func keyAuth(pub *rsa.PublicKey, token string) string { + return fmt.Sprintf("%s.%s", token, JWKThumbprint(pub)) +} + +// timeNow is useful for testing for fixed current time. +var timeNow = time.Now diff --git a/vendor/golang.org/x/crypto/acme/internal/acme/acme_test.go b/vendor/golang.org/x/crypto/acme/internal/acme/acme_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f9d17c339db56cee930c1d64f5ab0ee51b42dc50 --- /dev/null +++ b/vendor/golang.org/x/crypto/acme/internal/acme/acme_test.go @@ -0,0 +1,759 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package acme + +import ( + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "math/big" + "net/http" + "net/http/httptest" + "reflect" + "strings" + "testing" + "time" + + "golang.org/x/net/context" +) + +// Decodes a JWS-encoded request and unmarshals the decoded JSON into a provided +// interface. +func decodeJWSRequest(t *testing.T, v interface{}, r *http.Request) { + // Decode request + var req struct{ Payload string } + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + t.Fatal(err) + } + payload, err := base64.RawURLEncoding.DecodeString(req.Payload) + if err != nil { + t.Fatal(err) + } + err = json.Unmarshal(payload, v) + if err != nil { + t.Fatal(err) + } +} + +func TestDiscover(t *testing.T) { + const ( + reg = "https://example.com/acme/new-reg" + authz = "https://example.com/acme/new-authz" + cert = "https://example.com/acme/new-cert" + revoke = "https://example.com/acme/revoke-cert" + ) + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + fmt.Fprintf(w, `{ + "new-reg": %q, + "new-authz": %q, + "new-cert": %q, + "revoke-cert": %q + }`, reg, authz, cert, revoke) + })) + defer ts.Close() + ep, err := (&Client{}).Discover(ts.URL) + if err != nil { + t.Fatal(err) + } + if ep.RegURL != reg { + t.Errorf("RegURL = %q; want %q", ep.RegURL, reg) + } + if ep.AuthzURL != authz { + t.Errorf("authzURL = %q; want %q", ep.AuthzURL, authz) + } + if ep.CertURL != cert { + t.Errorf("certURL = %q; want %q", ep.CertURL, cert) + } + if ep.RevokeURL != revoke { + t.Errorf("revokeURL = %q; want %q", ep.RevokeURL, revoke) + } +} + +func TestRegister(t *testing.T) { + contacts := []string{"mailto:admin@example.com"} + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "HEAD" { + w.Header().Set("replay-nonce", "test-nonce") + return + } + if r.Method != "POST" { + t.Errorf("r.Method = %q; want POST", r.Method) + } + + var j struct { + Resource string + Contact []string + Agreement string + } + decodeJWSRequest(t, &j, r) + + // Test request + if j.Resource != "new-reg" { + t.Errorf("j.Resource = %q; want new-reg", j.Resource) + } + if !reflect.DeepEqual(j.Contact, contacts) { + t.Errorf("j.Contact = %v; want %v", j.Contact, contacts) + } + + w.Header().Set("Location", "https://ca.tld/acme/reg/1") + w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`) + w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`) + w.Header().Add("Link", `<https://ca.tld/acme/terms>;rel="terms-of-service"`) + w.WriteHeader(http.StatusCreated) + b, _ := json.Marshal(contacts) + fmt.Fprintf(w, `{ + "key":%q, + "contact":%s + }`, testKeyThumbprint, b) + })) + defer ts.Close() + + c := Client{Key: testKey} + a := &Account{Contact: contacts} + var err error + if a, err = c.Register(ts.URL, a); err != nil { + t.Fatal(err) + } + if a.URI != "https://ca.tld/acme/reg/1" { + t.Errorf("a.URI = %q; want https://ca.tld/acme/reg/1", a.URI) + } + if a.Authz != "https://ca.tld/acme/new-authz" { + t.Errorf("a.Authz = %q; want https://ca.tld/acme/new-authz", a.Authz) + } + if a.CurrentTerms != "https://ca.tld/acme/terms" { + t.Errorf("a.CurrentTerms = %q; want https://ca.tld/acme/terms", a.CurrentTerms) + } + if !reflect.DeepEqual(a.Contact, contacts) { + t.Errorf("a.Contact = %v; want %v", a.Contact, contacts) + } +} + +func TestUpdateReg(t *testing.T) { + const terms = "https://ca.tld/acme/terms" + contacts := []string{"mailto:admin@example.com"} + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "HEAD" { + w.Header().Set("replay-nonce", "test-nonce") + return + } + if r.Method != "POST" { + t.Errorf("r.Method = %q; want POST", r.Method) + } + + var j struct { + Resource string + Contact []string + Agreement string + } + decodeJWSRequest(t, &j, r) + + // Test request + if j.Resource != "reg" { + t.Errorf("j.Resource = %q; want reg", j.Resource) + } + if j.Agreement != terms { + t.Errorf("j.Agreement = %q; want %q", j.Agreement, terms) + } + if !reflect.DeepEqual(j.Contact, contacts) { + t.Errorf("j.Contact = %v; want %v", j.Contact, contacts) + } + + w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`) + w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`) + w.Header().Add("Link", fmt.Sprintf(`<%s>;rel="terms-of-service"`, terms)) + w.WriteHeader(http.StatusOK) + b, _ := json.Marshal(contacts) + fmt.Fprintf(w, `{ + "key":%q, + "contact":%s, + "agreement":%q + }`, testKeyThumbprint, b, terms) + })) + defer ts.Close() + + c := Client{Key: testKey} + a := &Account{Contact: contacts, AgreedTerms: terms} + var err error + if a, err = c.UpdateReg(ts.URL, a); err != nil { + t.Fatal(err) + } + if a.Authz != "https://ca.tld/acme/new-authz" { + t.Errorf("a.Authz = %q; want https://ca.tld/acme/new-authz", a.Authz) + } + if a.AgreedTerms != terms { + t.Errorf("a.AgreedTerms = %q; want %q", a.AgreedTerms, terms) + } + if a.CurrentTerms != terms { + t.Errorf("a.CurrentTerms = %q; want %q", a.CurrentTerms, terms) + } +} + +func TestGetReg(t *testing.T) { + const terms = "https://ca.tld/acme/terms" + const newTerms = "https://ca.tld/acme/new-terms" + contacts := []string{"mailto:admin@example.com"} + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "HEAD" { + w.Header().Set("replay-nonce", "test-nonce") + return + } + if r.Method != "POST" { + t.Errorf("r.Method = %q; want POST", r.Method) + } + + var j struct { + Resource string + Contact []string + Agreement string + } + decodeJWSRequest(t, &j, r) + + // Test request + if j.Resource != "reg" { + t.Errorf("j.Resource = %q; want reg", j.Resource) + } + if len(j.Contact) != 0 { + t.Errorf("j.Contact = %v", j.Contact) + } + if j.Agreement != "" { + t.Errorf("j.Agreement = %q", j.Agreement) + } + + w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`) + w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`) + w.Header().Add("Link", fmt.Sprintf(`<%s>;rel="terms-of-service"`, newTerms)) + w.WriteHeader(http.StatusOK) + b, _ := json.Marshal(contacts) + fmt.Fprintf(w, `{ + "key":%q, + "contact":%s, + "agreement":%q + }`, testKeyThumbprint, b, terms) + })) + defer ts.Close() + + c := Client{Key: testKey} + a, err := c.GetReg(ts.URL) + if err != nil { + t.Fatal(err) + } + if a.Authz != "https://ca.tld/acme/new-authz" { + t.Errorf("a.AuthzURL = %q; want https://ca.tld/acme/new-authz", a.Authz) + } + if a.AgreedTerms != terms { + t.Errorf("a.AgreedTerms = %q; want %q", a.AgreedTerms, terms) + } + if a.CurrentTerms != newTerms { + t.Errorf("a.CurrentTerms = %q; want %q", a.CurrentTerms, newTerms) + } +} + +func TestAuthorize(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "HEAD" { + w.Header().Set("replay-nonce", "test-nonce") + return + } + if r.Method != "POST" { + t.Errorf("r.Method = %q; want POST", r.Method) + } + + var j struct { + Resource string + Identifier struct { + Type string + Value string + } + } + decodeJWSRequest(t, &j, r) + + // Test request + if j.Resource != "new-authz" { + t.Errorf("j.Resource = %q; want new-authz", j.Resource) + } + if j.Identifier.Type != "dns" { + t.Errorf("j.Identifier.Type = %q; want dns", j.Identifier.Type) + } + if j.Identifier.Value != "example.com" { + t.Errorf("j.Identifier.Value = %q; want example.com", j.Identifier.Value) + } + + w.Header().Set("Location", "https://ca.tld/acme/auth/1") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, `{ + "identifier": {"type":"dns","value":"example.com"}, + "status":"pending", + "challenges":[ + { + "type":"http-01", + "status":"pending", + "uri":"https://ca.tld/acme/challenge/publickey/id1", + "token":"token1" + }, + { + "type":"tls-sni-01", + "status":"pending", + "uri":"https://ca.tld/acme/challenge/publickey/id2", + "token":"token2" + } + ], + "combinations":[[0],[1]]}`) + })) + defer ts.Close() + + cl := Client{Key: testKey} + auth, err := cl.Authorize(ts.URL, "example.com") + if err != nil { + t.Fatal(err) + } + + if auth.URI != "https://ca.tld/acme/auth/1" { + t.Errorf("URI = %q; want https://ca.tld/acme/auth/1", auth.URI) + } + if auth.Status != "pending" { + t.Errorf("Status = %q; want pending", auth.Status) + } + if auth.Identifier.Type != "dns" { + t.Errorf("Identifier.Type = %q; want dns", auth.Identifier.Type) + } + if auth.Identifier.Value != "example.com" { + t.Errorf("Identifier.Value = %q; want example.com", auth.Identifier.Value) + } + + if n := len(auth.Challenges); n != 2 { + t.Fatalf("len(auth.Challenges) = %d; want 2", n) + } + + c := auth.Challenges[0] + if c.Type != "http-01" { + t.Errorf("c.Type = %q; want http-01", c.Type) + } + if c.URI != "https://ca.tld/acme/challenge/publickey/id1" { + t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI) + } + if c.Token != "token1" { + t.Errorf("c.Token = %q; want token1", c.Type) + } + + c = auth.Challenges[1] + if c.Type != "tls-sni-01" { + t.Errorf("c.Type = %q; want tls-sni-01", c.Type) + } + if c.URI != "https://ca.tld/acme/challenge/publickey/id2" { + t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id2", c.URI) + } + if c.Token != "token2" { + t.Errorf("c.Token = %q; want token2", c.Type) + } + + combs := [][]int{{0}, {1}} + if !reflect.DeepEqual(auth.Combinations, combs) { + t.Errorf("auth.Combinations: %+v\nwant: %+v\n", auth.Combinations, combs) + } +} + +func TestPollAuthz(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + t.Errorf("r.Method = %q; want GET", r.Method) + } + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, `{ + "identifier": {"type":"dns","value":"example.com"}, + "status":"pending", + "challenges":[ + { + "type":"http-01", + "status":"pending", + "uri":"https://ca.tld/acme/challenge/publickey/id1", + "token":"token1" + }, + { + "type":"tls-sni-01", + "status":"pending", + "uri":"https://ca.tld/acme/challenge/publickey/id2", + "token":"token2" + } + ], + "combinations":[[0],[1]]}`) + })) + defer ts.Close() + + cl := Client{Key: testKey} + auth, err := cl.GetAuthz(ts.URL) + if err != nil { + t.Fatal(err) + } + + if auth.Status != "pending" { + t.Errorf("Status = %q; want pending", auth.Status) + } + if auth.Identifier.Type != "dns" { + t.Errorf("Identifier.Type = %q; want dns", auth.Identifier.Type) + } + if auth.Identifier.Value != "example.com" { + t.Errorf("Identifier.Value = %q; want example.com", auth.Identifier.Value) + } + + if n := len(auth.Challenges); n != 2 { + t.Fatalf("len(set.Challenges) = %d; want 2", n) + } + + c := auth.Challenges[0] + if c.Type != "http-01" { + t.Errorf("c.Type = %q; want http-01", c.Type) + } + if c.URI != "https://ca.tld/acme/challenge/publickey/id1" { + t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI) + } + if c.Token != "token1" { + t.Errorf("c.Token = %q; want token1", c.Type) + } + + c = auth.Challenges[1] + if c.Type != "tls-sni-01" { + t.Errorf("c.Type = %q; want tls-sni-01", c.Type) + } + if c.URI != "https://ca.tld/acme/challenge/publickey/id2" { + t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id2", c.URI) + } + if c.Token != "token2" { + t.Errorf("c.Token = %q; want token2", c.Type) + } + + combs := [][]int{{0}, {1}} + if !reflect.DeepEqual(auth.Combinations, combs) { + t.Errorf("auth.Combinations: %+v\nwant: %+v\n", auth.Combinations, combs) + } +} + +func TestPollChallenge(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + t.Errorf("r.Method = %q; want GET", r.Method) + } + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, `{ + "type":"http-01", + "status":"pending", + "uri":"https://ca.tld/acme/challenge/publickey/id1", + "token":"token1"}`) + })) + defer ts.Close() + + cl := Client{Key: testKey} + chall, err := cl.GetChallenge(ts.URL) + if err != nil { + t.Fatal(err) + } + + if chall.Status != "pending" { + t.Errorf("Status = %q; want pending", chall.Status) + } + if chall.Type != "http-01" { + t.Errorf("c.Type = %q; want http-01", chall.Type) + } + if chall.URI != "https://ca.tld/acme/challenge/publickey/id1" { + t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", chall.URI) + } + if chall.Token != "token1" { + t.Errorf("c.Token = %q; want token1", chall.Type) + } +} + +func TestAcceptChallenge(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "HEAD" { + w.Header().Set("replay-nonce", "test-nonce") + return + } + if r.Method != "POST" { + t.Errorf("r.Method = %q; want POST", r.Method) + } + + var j struct { + Resource string + Type string + Auth string `json:"keyAuthorization"` + } + decodeJWSRequest(t, &j, r) + + // Test request + if j.Resource != "challenge" { + t.Errorf(`resource = %q; want "challenge"`, j.Resource) + } + if j.Type != "http-01" { + t.Errorf(`type = %q; want "http-01"`, j.Type) + } + keyAuth := "token1." + testKeyThumbprint + if j.Auth != keyAuth { + t.Errorf(`keyAuthorization = %q; want %q`, j.Auth, keyAuth) + } + + // Respond to request + w.WriteHeader(http.StatusAccepted) + fmt.Fprintf(w, `{ + "type":"http-01", + "status":"pending", + "uri":"https://ca.tld/acme/challenge/publickey/id1", + "token":"token1", + "keyAuthorization":%q + }`, keyAuth) + })) + defer ts.Close() + + cl := Client{Key: testKey} + c, err := cl.Accept(&Challenge{ + URI: ts.URL, + Token: "token1", + Type: "http-01", + }) + if err != nil { + t.Fatal(err) + } + + if c.Type != "http-01" { + t.Errorf("c.Type = %q; want http-01", c.Type) + } + if c.URI != "https://ca.tld/acme/challenge/publickey/id1" { + t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI) + } + if c.Token != "token1" { + t.Errorf("c.Token = %q; want token1", c.Type) + } +} + +func TestNewCert(t *testing.T) { + notBefore := time.Now() + notAfter := notBefore.AddDate(0, 2, 0) + timeNow = func() time.Time { return notBefore } + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "HEAD" { + w.Header().Set("replay-nonce", "test-nonce") + return + } + if r.Method != "POST" { + t.Errorf("r.Method = %q; want POST", r.Method) + } + + var j struct { + Resource string `json:"resource"` + CSR string `json:"csr"` + NotBefore string `json:"notBefore,omitempty"` + NotAfter string `json:"notAfter,omitempty"` + } + decodeJWSRequest(t, &j, r) + + // Test request + if j.Resource != "new-cert" { + t.Errorf(`resource = %q; want "new-cert"`, j.Resource) + } + if j.NotBefore != notBefore.Format(time.RFC3339) { + t.Errorf(`notBefore = %q; wanted %q`, j.NotBefore, notBefore.Format(time.RFC3339)) + } + if j.NotAfter != notAfter.Format(time.RFC3339) { + t.Errorf(`notAfter = %q; wanted %q`, j.NotAfter, notAfter.Format(time.RFC3339)) + } + + // Respond to request + template := x509.Certificate{ + SerialNumber: big.NewInt(int64(1)), + Subject: pkix.Name{ + Organization: []string{"goacme"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + sampleCert, err := x509.CreateCertificate(rand.Reader, &template, &template, &testKey.PublicKey, testKey) + if err != nil { + t.Fatalf("Error creating certificate: %v", err) + } + + w.Header().Set("Location", "https://ca.tld/acme/cert/1") + w.WriteHeader(http.StatusCreated) + w.Write(sampleCert) + })) + defer ts.Close() + + csr := x509.CertificateRequest{ + Version: 0, + Subject: pkix.Name{ + CommonName: "example.com", + Organization: []string{"goacme"}, + }, + } + csrb, err := x509.CreateCertificateRequest(rand.Reader, &csr, testKey) + if err != nil { + t.Fatal(err) + } + + c := Client{Key: testKey} + cert, certURL, err := c.CreateCert(context.Background(), ts.URL, csrb, notAfter.Sub(notBefore), false) + if err != nil { + t.Fatal(err) + } + if cert == nil { + t.Errorf("cert is nil") + } + if certURL != "https://ca.tld/acme/cert/1" { + t.Errorf("certURL = %q; want https://ca.tld/acme/cert/1", certURL) + } +} + +func TestFetchCert(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte{1}) + })) + defer ts.Close() + res, err := (&Client{}).FetchCert(context.Background(), ts.URL, false) + if err != nil { + t.Fatalf("FetchCert: %v", err) + } + cert := [][]byte{{1}} + if !reflect.DeepEqual(res, cert) { + t.Errorf("res = %v; want %v", res, cert) + } +} + +func TestFetchCertRetry(t *testing.T) { + var count int + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if count < 1 { + w.Header().Set("retry-after", "0") + w.WriteHeader(http.StatusAccepted) + count++ + return + } + w.Write([]byte{1}) + })) + defer ts.Close() + res, err := (&Client{}).FetchCert(context.Background(), ts.URL, false) + if err != nil { + t.Fatalf("FetchCert: %v", err) + } + cert := [][]byte{{1}} + if !reflect.DeepEqual(res, cert) { + t.Errorf("res = %v; want %v", res, cert) + } +} + +func TestFetchCertCancel(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("retry-after", "0") + w.WriteHeader(http.StatusAccepted) + })) + defer ts.Close() + ctx, cancel := context.WithCancel(context.Background()) + done := make(chan struct{}) + var err error + go func() { + _, err = (&Client{}).FetchCert(ctx, ts.URL, false) + close(done) + }() + cancel() + <-done + if err != context.Canceled { + t.Errorf("err = %v; want %v", err, context.Canceled) + } +} + +func TestFetchNonce(t *testing.T) { + tests := []struct { + code int + nonce string + }{ + {http.StatusOK, "nonce1"}, + {http.StatusBadRequest, "nonce2"}, + {http.StatusOK, ""}, + } + var i int + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "HEAD" { + t.Errorf("%d: r.Method = %q; want HEAD", i, r.Method) + } + w.Header().Set("replay-nonce", tests[i].nonce) + w.WriteHeader(tests[i].code) + })) + defer ts.Close() + for ; i < len(tests); i++ { + test := tests[i] + n, err := fetchNonce(http.DefaultClient, ts.URL) + if n != test.nonce { + t.Errorf("%d: n=%q; want %q", i, n, test.nonce) + } + switch { + case err == nil && test.nonce == "": + t.Errorf("%d: n=%q, err=%v; want non-nil error", i, n, err) + case err != nil && test.nonce != "": + t.Errorf("%d: n=%q, err=%v; want %q", i, n, err, test.nonce) + } + } +} + +func TestLinkHeader(t *testing.T) { + h := http.Header{"Link": { + `<https://example.com/acme/new-authz>;rel="next"`, + `<https://example.com/acme/recover-reg>; rel=recover`, + `<https://example.com/acme/terms>; foo=bar; rel="terms-of-service"`, + }} + tests := []struct{ in, out string }{ + {"next", "https://example.com/acme/new-authz"}, + {"recover", "https://example.com/acme/recover-reg"}, + {"terms-of-service", "https://example.com/acme/terms"}, + {"empty", ""}, + } + for i, test := range tests { + if v := linkHeader(h, test.in); v != test.out { + t.Errorf("%d: parseLinkHeader(%q): %q; want %q", i, test.in, v, test.out) + } + } +} + +func TestErrorResponse(t *testing.T) { + s := `{ + "status": 400, + "type": "urn:acme:error:xxx", + "detail": "text" + }` + res := &http.Response{ + StatusCode: 400, + Status: "400 Bad Request", + Body: ioutil.NopCloser(strings.NewReader(s)), + Header: http.Header{"X-Foo": {"bar"}}, + } + err := responseError(res) + v, ok := err.(*Error) + if !ok { + t.Fatalf("err = %+v (%T); want *Error type", err, err) + } + if v.StatusCode != 400 { + t.Errorf("v.StatusCode = %v; want 400", v.StatusCode) + } + if v.ProblemType != "urn:acme:error:xxx" { + t.Errorf("v.ProblemType = %q; want urn:acme:error:xxx", v.ProblemType) + } + if v.Detail != "text" { + t.Errorf("v.Detail = %q; want text", v.Detail) + } + if !reflect.DeepEqual(v.Header, res.Header) { + t.Errorf("v.Header = %+v; want %+v", v.Header, res.Header) + } +} diff --git a/vendor/golang.org/x/crypto/acme/internal/acme/jws.go b/vendor/golang.org/x/crypto/acme/internal/acme/jws.go new file mode 100644 index 0000000000000000000000000000000000000000..c27752977b63180755f2c5dceac3b1065c177b0d --- /dev/null +++ b/vendor/golang.org/x/crypto/acme/internal/acme/jws.go @@ -0,0 +1,67 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package acme + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "fmt" + "math/big" +) + +// jwsEncodeJSON signs claimset using provided key and a nonce. +// The result is serialized in JSON format. +// See https://tools.ietf.org/html/rfc7515#section-7. +func jwsEncodeJSON(claimset interface{}, key *rsa.PrivateKey, nonce string) ([]byte, error) { + jwk := jwkEncode(&key.PublicKey) + phead := fmt.Sprintf(`{"alg":"RS256","jwk":%s,"nonce":%q}`, jwk, nonce) + phead = base64.RawURLEncoding.EncodeToString([]byte(phead)) + cs, err := json.Marshal(claimset) + if err != nil { + return nil, err + } + payload := base64.RawURLEncoding.EncodeToString(cs) + h := sha256.New() + h.Write([]byte(phead + "." + payload)) + sig, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil)) + if err != nil { + return nil, err + } + enc := struct { + Protected string `json:"protected"` + Payload string `json:"payload"` + Sig string `json:"signature"` + }{ + Protected: phead, + Payload: payload, + Sig: base64.RawURLEncoding.EncodeToString(sig), + } + return json.Marshal(&enc) +} + +// jwkEncode encodes public part of an RSA key into a JWK. +// The result is also suitable for creating a JWK thumbprint. +func jwkEncode(pub *rsa.PublicKey) string { + n := pub.N + e := big.NewInt(int64(pub.E)) + // fields order is important + // see https://tools.ietf.org/html/rfc7638#section-3.3 for details + return fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`, + base64.RawURLEncoding.EncodeToString(e.Bytes()), + base64.RawURLEncoding.EncodeToString(n.Bytes()), + ) +} + +// JWKThumbprint creates a JWK thumbprint out of pub +// as specified in https://tools.ietf.org/html/rfc7638. +func JWKThumbprint(pub *rsa.PublicKey) string { + jwk := jwkEncode(pub) + b := sha256.Sum256([]byte(jwk)) + return base64.RawURLEncoding.EncodeToString(b[:]) +} diff --git a/vendor/golang.org/x/crypto/acme/internal/acme/jws_test.go b/vendor/golang.org/x/crypto/acme/internal/acme/jws_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1477ccbeed751c611a53b43e1452efdaac718763 --- /dev/null +++ b/vendor/golang.org/x/crypto/acme/internal/acme/jws_test.go @@ -0,0 +1,139 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package acme + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "math/big" + "testing" +) + +const testKeyPEM = ` +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA4xgZ3eRPkwoRvy7qeRUbmMDe0V+xH9eWLdu0iheeLlrmD2mq +WXfP9IeSKApbn34g8TuAS9g5zhq8ELQ3kmjr+KV86GAMgI6VAcGlq3QrzpTCf/30 +Ab7+zawrfRaFONa1HwEzPY1KHnGVkxJc85gNkwYI9SY2RHXtvln3zs5wITNrdosq +EXeaIkVYBEhbhNu54pp3kxo6TuWLi9e6pXeWetEwmlBwtWZlPoib2j3TxLBksKZf +oyFyek380mHgJAumQ/I2fjj98/97mk3ihOY4AgVdCDj1z/GCoZkG5Rq7nbCGyosy +KWyDX00Zs+nNqVhoLeIvXC4nnWdJMZ6rogxyQQIDAQABAoIBACIEZTOI1Kao9nmV +9IeIsuaR1Y61b9neOF/MLmIVIZu+AAJFCMB4Iw11FV6sFodwpEyeZhx2WkpWVN+H +r19eGiLX3zsL0DOdqBJoSIHDWCCMxgnYJ6nvS0nRxX3qVrBp8R2g12Ub+gNPbmFm +ecf/eeERIVxfifd9VsyRu34eDEvcmKFuLYbElFcPh62xE3x12UZvV/sN7gXbawpP +G+w255vbE5MoaKdnnO83cTFlcHvhn24M/78qP7Te5OAeelr1R89kYxQLpuGe4fbS +zc6E3ym5Td6urDetGGrSY1Eu10/8sMusX+KNWkm+RsBRbkyKq72ks/qKpOxOa+c6 +9gm+Y8ECgYEA/iNUyg1ubRdH11p82l8KHtFC1DPE0V1gSZsX29TpM5jS4qv46K+s +8Ym1zmrORM8x+cynfPx1VQZQ34EYeCMIX212ryJ+zDATl4NE0I4muMvSiH9vx6Xc +7FmhNnaYzPsBL5Tm9nmtQuP09YEn8poiOJFiDs/4olnD5ogA5O4THGkCgYEA5MIL +qWYBUuqbEWLRtMruUtpASclrBqNNsJEsMGbeqBJmoMxdHeSZckbLOrqm7GlMyNRJ +Ne/5uWRGSzaMYuGmwsPpERzqEvYFnSrpjW5YtXZ+JtxFXNVfm9Z1gLLgvGpOUCIU +RbpoDckDe1vgUuk3y5+DjZihs+rqIJ45XzXTzBkCgYBWuf3segruJZy5rEKhTv+o +JqeUvRn0jNYYKFpLBeyTVBrbie6GkbUGNIWbrK05pC+c3K9nosvzuRUOQQL1tJbd +4gA3oiD9U4bMFNr+BRTHyZ7OQBcIXdz3t1qhuHVKtnngIAN1p25uPlbRFUNpshnt +jgeVoHlsBhApcs5DUc+pyQKBgDzeHPg/+g4z+nrPznjKnktRY1W+0El93kgi+J0Q +YiJacxBKEGTJ1MKBb8X6sDurcRDm22wMpGfd9I5Cv2v4GsUsF7HD/cx5xdih+G73 +c4clNj/k0Ff5Nm1izPUno4C+0IOl7br39IPmfpSuR6wH/h6iHQDqIeybjxyKvT1G +N0rRAoGBAKGD+4ZI/E1MoJ5CXB8cDDMHagbE3cq/DtmYzE2v1DFpQYu5I4PCm5c7 +EQeIP6dZtv8IMgtGIb91QX9pXvP0aznzQKwYIA8nZgoENCPfiMTPiEDT9e/0lObO +9XWsXpbSTsRPj0sv1rB+UzBJ0PgjK4q2zOF0sNo7b1+6nlM3BWPx +-----END RSA PRIVATE KEY---- +` + +// This thumbprint is for the testKey defined above. +const testKeyThumbprint = "6nicxzh6WETQlrvdchkz-U3e3DOQZ4heJKU63rfqMqQ" + +var testKey *rsa.PrivateKey + +func init() { + d, _ := pem.Decode([]byte(testKeyPEM)) + if d == nil { + panic("no block found in testKeyPEM") + } + var err error + testKey, err = x509.ParsePKCS1PrivateKey(d.Bytes) + if err != nil { + panic(err.Error()) + } +} + +func TestJWSEncodeJSON(t *testing.T) { + claims := struct{ Msg string }{"Hello JWS"} + // JWS signed with testKey and "nonce" as the nonce value + // JSON-serialized JWS fields are split for easier testing + const ( + // {"alg":"RS256","jwk":{"e":"AQAB","kty":"RSA","n":"..."},"nonce":"nonce"} + protected = "eyJhbGciOiJSUzI1NiIsImp3ayI6eyJlIjoiQVFBQiIsImt0eSI6" + + "IlJTQSIsIm4iOiI0eGdaM2VSUGt3b1J2eTdxZVJVYm1NRGUwVi14" + + "SDllV0xkdTBpaGVlTGxybUQybXFXWGZQOUllU0tBcGJuMzRnOFR1" + + "QVM5ZzV6aHE4RUxRM2ttanItS1Y4NkdBTWdJNlZBY0dscTNRcnpw" + + "VENmXzMwQWI3LXphd3JmUmFGT05hMUh3RXpQWTFLSG5HVmt4SmM4" + + "NWdOa3dZSTlTWTJSSFh0dmxuM3pzNXdJVE5yZG9zcUVYZWFJa1ZZ" + + "QkVoYmhOdTU0cHAza3hvNlR1V0xpOWU2cFhlV2V0RXdtbEJ3dFda" + + "bFBvaWIyajNUeExCa3NLWmZveUZ5ZWszODBtSGdKQXVtUV9JMmZq" + + "ajk4Xzk3bWszaWhPWTRBZ1ZkQ0RqMXpfR0NvWmtHNVJxN25iQ0d5" + + "b3N5S1d5RFgwMFpzLW5OcVZob0xlSXZYQzRubldkSk1aNnJvZ3h5" + + "UVEifSwibm9uY2UiOiJub25jZSJ9" + // {"Msg":"Hello JWS"} + payload = "eyJNc2ciOiJIZWxsbyBKV1MifQ" + signature = "eAGUikStX_UxyiFhxSLMyuyBcIB80GeBkFROCpap2sW3EmkU_ggF" + + "knaQzxrTfItICSAXsCLIquZ5BbrSWA_4vdEYrwWtdUj7NqFKjHRa" + + "zpLHcoR7r1rEHvkoP1xj49lS5fc3Wjjq8JUhffkhGbWZ8ZVkgPdC" + + "4tMBWiQDoth-x8jELP_3LYOB_ScUXi2mETBawLgOT2K8rA0Vbbmx" + + "hWNlOWuUf-8hL5YX4IOEwsS8JK_TrTq5Zc9My0zHJmaieqDV0UlP" + + "k0onFjPFkGm7MrPSgd0MqRG-4vSAg2O4hDo7rKv4n8POjjXlNQvM" + + "9IPLr8qZ7usYBKhEGwX3yq_eicAwBw" + ) + + b, err := jwsEncodeJSON(claims, testKey, "nonce") + if err != nil { + t.Fatal(err) + } + var jws struct{ Protected, Payload, Signature string } + if err := json.Unmarshal(b, &jws); err != nil { + t.Fatal(err) + } + if jws.Protected != protected { + t.Errorf("protected:\n%s\nwant:\n%s", jws.Protected, protected) + } + if jws.Payload != payload { + t.Errorf("payload:\n%s\nwant:\n%s", jws.Payload, payload) + } + if jws.Signature != signature { + t.Errorf("signature:\n%s\nwant:\n%s", jws.Signature, signature) + } +} + +func TestJWKThumbprint(t *testing.T) { + // Key example from RFC 7638 + const base64N = "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAt" + + "VT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn6" + + "4tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FD" + + "W2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n9" + + "1CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINH" + + "aQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw" + const base64E = "AQAB" + const expected = "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs" + + bytes, err := base64.RawURLEncoding.DecodeString(base64N) + if err != nil { + t.Fatalf("Error parsing example key N: %v", err) + } + n := new(big.Int).SetBytes(bytes) + + bytes, err = base64.RawURLEncoding.DecodeString(base64E) + if err != nil { + t.Fatalf("Error parsing example key E: %v", err) + } + e := new(big.Int).SetBytes(bytes) + + pub := &rsa.PublicKey{N: n, E: int(e.Uint64())} + th := JWKThumbprint(pub) + if th != expected { + t.Errorf("th = %q; want %q", th, expected) + } +} diff --git a/vendor/golang.org/x/crypto/acme/internal/acme/types.go b/vendor/golang.org/x/crypto/acme/internal/acme/types.go new file mode 100644 index 0000000000000000000000000000000000000000..e64dc118c0604f1d89f9cfa5cf097e3d157794d0 --- /dev/null +++ b/vendor/golang.org/x/crypto/acme/internal/acme/types.go @@ -0,0 +1,181 @@ +package acme + +import ( + "fmt" + "net/http" +) + +// ACME server response statuses used to describe Authorization and Challenge states. +const ( + StatusUnknown = "unknown" + StatusPending = "pending" + StatusProcessing = "processing" + StatusValid = "valid" + StatusInvalid = "invalid" + StatusRevoked = "revoked" +) + +// Account is a user account. It is associated with a private key. +type Account struct { + // URI is the account unique ID, which is also a URL used to retrieve + // account data from the CA. + URI string + + // Contact is a slice of contact info used during registration. + Contact []string + + // The terms user has agreed to. + // Zero value indicates that the user hasn't agreed yet. + AgreedTerms string + + // Actual terms of a CA. + CurrentTerms string + + // Authz is the authorization URL used to initiate a new authz flow. + Authz string + + // Authorizations is a URI from which a list of authorizations + // granted to this account can be fetched via a GET request. + Authorizations string + + // Certificates is a URI from which a list of certificates + // issued for this account can be fetched via a GET request. + Certificates string +} + +// Directory is ACME server discovery data. +type Directory struct { + // RegURL is an account endpoint URL, allowing for creating new + // and modifying existing accounts. + RegURL string + + // AuthzURL is used to initiate Identifier Authorization flow. + AuthzURL string + + // CertURL is a new certificate issuance endpoint URL. + CertURL string + + // RevokeURL is used to initiate a certificate revocation flow. + RevokeURL string + + // Term is a URI identifying the current terms of service. + Terms string + + // Website is an HTTP or HTTPS URL locating a website + // providing more information about the ACME server. + Website string + + // CAA consists of lowercase hostname elements, which the ACME server + // recognises as referring to itself for the purposes of CAA record validation + // as defined in RFC6844. + CAA []string +} + +// Challenge encodes a returned CA challenge. +type Challenge struct { + // Type is the challenge type, e.g. "http-01", "tls-sni-02", "dns-01". + Type string + + // URI is where a challenge response can be posted to. + URI string + + // Token is a random value that uniquely identifies the challenge. + Token string + + // Status identifies the status of this challenge. + Status string +} + +// Authorization encodes an authorization response. +type Authorization struct { + // URI uniquely identifies a authorization. + URI string + + // Status identifies the status of an authorization. + Status string + + // Identifier is what the account is authorized to represent. + Identifier AuthzID + + // Challenges that the client needs to fulfill in order to prove possession + // of the identifier (for pending authorizations). + // For final authorizations, the challenges that were used. + Challenges []*Challenge + + // A collection of sets of challenges, each of which would be sufficient + // to prove possession of the identifier. + // Clients must complete a set of challenges that covers at least one set. + // Challenges are identified by their indices in the challenges array. + // If this field is empty, the client needs to complete all challenges. + Combinations [][]int +} + +// AuthzID is an identifier that an account is authorized to represent. +type AuthzID struct { + Type string // The type of identifier, e.g. "dns". + Value string // The identifier itself, e.g. "example.org". +} + +// Error is an ACME error, defined in Problem Details for HTTP APIs doc +// http://tools.ietf.org/html/draft-ietf-appsawg-http-problem. +type Error struct { + // StatusCode is The HTTP status code generated by the origin server. + StatusCode int + // ProblemType is a URI reference that identifies the problem type, + // typically in a "urn:acme:error:xxx" form. + ProblemType string + // Detail is a human-readable explanation specific to this occurrence of the problem. + Detail string + // Header is the original server error response headers. + Header http.Header +} + +func (e *Error) Error() string { + return fmt.Sprintf("%d %s: %s", e.StatusCode, e.ProblemType, e.Detail) +} + +// wireAuthz is ACME JSON representation of Authorization objects. +type wireAuthz struct { + Status string + Challenges []wireChallenge + Combinations [][]int + Identifier struct { + Type string + Value string + } +} + +func (z *wireAuthz) authorization(uri string) *Authorization { + a := &Authorization{ + URI: uri, + Status: z.Status, + Identifier: AuthzID{Type: z.Identifier.Type, Value: z.Identifier.Value}, + Combinations: z.Combinations, // shallow copy + Challenges: make([]*Challenge, len(z.Challenges)), + } + for i, v := range z.Challenges { + a.Challenges[i] = v.challenge() + } + return a +} + +// wireChallenge is ACME JSON challenge representation. +type wireChallenge struct { + URI string `json:"uri"` + Type string + Token string + Status string +} + +func (c *wireChallenge) challenge() *Challenge { + v := &Challenge{ + URI: c.URI, + Type: c.Type, + Token: c.Token, + Status: c.Status, + } + if v.Status == "" { + v.Status = StatusPending + } + return v +} diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go index bc6f47a97418d20c97757e1b6b9fd3d38f29ee72..e0f1a4d584f381f639efa1788f117a716a465dec 100644 --- a/vendor/golang.org/x/crypto/ssh/client.go +++ b/vendor/golang.org/x/crypto/ssh/client.go @@ -97,7 +97,7 @@ func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) e c.transport = newClientTransport( newTransport(c.sshConn.conn, config.Rand, true /* is client */), c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr()) - if err := c.transport.requestKeyChange(); err != nil { + if err := c.transport.requestInitialKeyChange(); err != nil { return err } diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go index 1c54f758781ee08c3e6a4179a0207b4b1e18c9d4..08abd66a92ab42e82b9f94e26fdea09121d4d263 100644 --- a/vendor/golang.org/x/crypto/ssh/handshake.go +++ b/vendor/golang.org/x/crypto/ssh/handshake.go @@ -29,25 +29,6 @@ type keyingTransport interface { // direction will be effected if a msgNewKeys message is sent // or received. prepareKeyChange(*algorithms, *kexResult) error - - // getSessionID returns the session ID. prepareKeyChange must - // have been called once. - getSessionID() []byte -} - -// rekeyingTransport is the interface of handshakeTransport that we -// (internally) expose to ClientConn and ServerConn. -type rekeyingTransport interface { - packetConn - - // requestKeyChange asks the remote side to change keys. All - // writes are blocked until the key change succeeds, which is - // signaled by reading a msgNewKeys. - requestKeyChange() error - - // getSessionID returns the session ID. This is only valid - // after the first key change has completed. - getSessionID() []byte } // handshakeTransport implements rekeying on top of a keyingTransport @@ -86,6 +67,9 @@ type handshakeTransport struct { sentInitMsg *kexInitMsg writtenSinceKex uint64 writeError error + + // The session ID or nil if first kex did not complete yet. + sessionID []byte } func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport { @@ -122,7 +106,7 @@ func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byt } func (t *handshakeTransport) getSessionID() []byte { - return t.conn.getSessionID() + return t.sessionID } func (t *handshakeTransport) id() string { @@ -183,9 +167,9 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) { if p[0] != msgKexInit { return p, nil } - err = t.enterKeyExchange(p) t.mu.Lock() + err = t.enterKeyExchangeLocked(p) if err != nil { // drop connection t.conn.Close() @@ -211,25 +195,39 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) { return []byte{msgNewKeys}, nil } +// keyChangeCategory describes whether a key exchange is the first on a +// connection, or a subsequent one. +type keyChangeCategory bool + +const ( + firstKeyExchange keyChangeCategory = true + subsequentKeyExchange keyChangeCategory = false +) + // sendKexInit sends a key change message, and returns the message // that was sent. After initiating the key change, all writes will be // blocked until the change is done, and a failed key change will // close the underlying transport. This function is safe for // concurrent use by multiple goroutines. -func (t *handshakeTransport) sendKexInit() (*kexInitMsg, []byte, error) { +func (t *handshakeTransport) sendKexInit(isFirst keyChangeCategory) (*kexInitMsg, []byte, error) { t.mu.Lock() defer t.mu.Unlock() - return t.sendKexInitLocked() + return t.sendKexInitLocked(isFirst) +} + +func (t *handshakeTransport) requestInitialKeyChange() error { + _, _, err := t.sendKexInit(firstKeyExchange) + return err } func (t *handshakeTransport) requestKeyChange() error { - _, _, err := t.sendKexInit() + _, _, err := t.sendKexInit(subsequentKeyExchange) return err } // sendKexInitLocked sends a key change message. t.mu must be locked // while this happens. -func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) { +func (t *handshakeTransport) sendKexInitLocked(isFirst keyChangeCategory) (*kexInitMsg, []byte, error) { // kexInits may be sent either in response to the other side, // or because our side wants to initiate a key change, so we // may have already sent a kexInit. In that case, don't send a @@ -237,6 +235,14 @@ func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) { if t.sentInitMsg != nil { return t.sentInitMsg, t.sentInitPacket, nil } + + // If this is the initial key change, but we already have a sessionID, + // then do nothing because the key exchange has already completed + // asynchronously. + if isFirst && t.sessionID != nil { + return nil, nil, nil + } + msg := &kexInitMsg{ KexAlgos: t.config.KeyExchanges, CiphersClientServer: t.config.Ciphers, @@ -276,7 +282,7 @@ func (t *handshakeTransport) writePacket(p []byte) error { defer t.mu.Unlock() if t.writtenSinceKex > t.config.RekeyThreshold { - t.sendKexInitLocked() + t.sendKexInitLocked(subsequentKeyExchange) } for t.sentInitMsg != nil && t.writeError == nil { t.cond.Wait() @@ -300,12 +306,12 @@ func (t *handshakeTransport) Close() error { return t.conn.Close() } -// enterKeyExchange runs the key exchange. -func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { +// enterKeyExchange runs the key exchange. t.mu must be held while running this. +func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) error { if debugHandshake { log.Printf("%s entered key exchange", t.id()) } - myInit, myInitPacket, err := t.sendKexInit() + myInit, myInitPacket, err := t.sendKexInitLocked(subsequentKeyExchange) if err != nil { return err } @@ -362,6 +368,11 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { return err } + if t.sessionID == nil { + t.sessionID = result.H + result.SessionID = result.H + } + t.conn.prepareKeyChange(algs, result) if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { return err @@ -371,6 +382,7 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { } else if packet[0] != msgNewKeys { return unexpectedMessageError(msgNewKeys, packet[0]) } + return nil } diff --git a/vendor/golang.org/x/crypto/ssh/handshake_test.go b/vendor/golang.org/x/crypto/ssh/handshake_test.go index bd7fe7752aad7b9a0fdaeca780ef0f6d14d00ae4..ef97d572805761257e0b93d1eaa9b622e40bca22 100644 --- a/vendor/golang.org/x/crypto/ssh/handshake_test.go +++ b/vendor/golang.org/x/crypto/ssh/handshake_test.go @@ -104,7 +104,7 @@ func TestHandshakeBasic(t *testing.T) { } if i == 5 { // halfway through, we request a key change. - _, _, err := trC.sendKexInit() + _, _, err := trC.sendKexInit(subsequentKeyExchange) if err != nil { t.Fatalf("sendKexInit: %v", err) } @@ -161,7 +161,7 @@ func TestHandshakeError(t *testing.T) { } // Now request a key change. - _, _, err = trC.sendKexInit() + _, _, err = trC.sendKexInit(subsequentKeyExchange) if err != nil { t.Errorf("sendKexInit: %v", err) } @@ -202,7 +202,7 @@ func TestHandshakeTwice(t *testing.T) { } // Now request a key change. - _, _, err = trC.sendKexInit() + _, _, err = trC.sendKexInit(subsequentKeyExchange) if err != nil { t.Errorf("sendKexInit: %v", err) } @@ -215,7 +215,7 @@ func TestHandshakeTwice(t *testing.T) { } // 2nd key change. - _, _, err = trC.sendKexInit() + _, _, err = trC.sendKexInit(subsequentKeyExchange) if err != nil { t.Errorf("sendKexInit: %v", err) } @@ -430,7 +430,7 @@ func TestDisconnect(t *testing.T) { trC.writePacket([]byte{msgRequestSuccess, 0, 0}) errMsg := &disconnectMsg{ - Reason: 42, + Reason: 42, Message: "such is life", } trC.writePacket(Marshal(errMsg)) @@ -441,7 +441,7 @@ func TestDisconnect(t *testing.T) { t.Fatalf("readPacket 1: %v", err) } if packet[0] != msgRequestSuccess { - t.Errorf("got packet %v, want packet type %d", packet, msgRequestSuccess) + t.Errorf("got packet %v, want packet type %d", packet, msgRequestSuccess) } _, err = trS.readPacket() diff --git a/vendor/golang.org/x/crypto/ssh/kex.go b/vendor/golang.org/x/crypto/ssh/kex.go index 3ec603c0a1597e19a3e267790326275a46e48374..9285ee31deec3a3efc8f96b3568fcc4f7d96a1d9 100644 --- a/vendor/golang.org/x/crypto/ssh/kex.go +++ b/vendor/golang.org/x/crypto/ssh/kex.go @@ -46,7 +46,7 @@ type kexResult struct { Hash crypto.Hash // The session ID, which is the first H computed. This is used - // to signal data inside transport. + // to derive key material inside the transport. SessionID []byte } diff --git a/vendor/golang.org/x/crypto/ssh/messages.go b/vendor/golang.org/x/crypto/ssh/messages.go index 247694b795b43b396f829d4fef2c9f50c2018ffb..9e2530ae7e6315acacd884b063bd126f4f2e9929 100644 --- a/vendor/golang.org/x/crypto/ssh/messages.go +++ b/vendor/golang.org/x/crypto/ssh/messages.go @@ -124,6 +124,10 @@ type userAuthRequestMsg struct { Payload []byte `ssh:"rest"` } +// Used for debug printouts of packets. +type userAuthSuccessMsg struct { +} + // See RFC 4252, section 5.1 const msgUserAuthFailure = 51 @@ -158,6 +162,13 @@ type channelOpenMsg struct { const msgChannelExtendedData = 95 const msgChannelData = 94 +// Used for debug print outs of packets. +type channelDataMsg struct { + PeersId uint32 `sshtype:"94"` + Length uint32 + Rest []byte `ssh:"rest"` +} + // See RFC 4254, section 5.1. const msgChannelOpenConfirm = 91 @@ -687,6 +698,8 @@ func decode(packet []byte) (interface{}, error) { msg = new(kexDHReplyMsg) case msgUserAuthRequest: msg = new(userAuthRequestMsg) + case msgUserAuthSuccess: + return new(userAuthSuccessMsg), nil case msgUserAuthFailure: msg = new(userAuthFailureMsg) case msgUserAuthPubKeyOk: @@ -699,6 +712,8 @@ func decode(packet []byte) (interface{}, error) { msg = new(globalRequestFailureMsg) case msgChannelOpen: msg = new(channelOpenMsg) + case msgChannelData: + msg = new(channelDataMsg) case msgChannelOpenConfirm: msg = new(channelOpenConfirmMsg) case msgChannelOpenFailure: diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index 4781eb78050d42f159a36292cf115d7c02a25ac9..d53050197c3c275100c346e4fec2bd89e4ab3d98 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -188,7 +188,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */) s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config) - if err := s.transport.requestKeyChange(); err != nil { + if err := s.transport.requestInitialKeyChange(); err != nil { return nil, err } diff --git a/vendor/golang.org/x/crypto/ssh/transport.go b/vendor/golang.org/x/crypto/ssh/transport.go index 4de98a6a5ad47ddb306752cd3dc33a01b5f48575..bf7dd61fc8263e5ac0530501263b37b2e2646106 100644 --- a/vendor/golang.org/x/crypto/ssh/transport.go +++ b/vendor/golang.org/x/crypto/ssh/transport.go @@ -39,19 +39,6 @@ type transport struct { rand io.Reader io.Closer - - // Initial H used for the session ID. Once assigned this does - // not change, even during subsequent key exchanges. - sessionID []byte -} - -// getSessionID returns the ID of the SSH connection. The return value -// should not be modified. -func (t *transport) getSessionID() []byte { - if t.sessionID == nil { - panic("session ID not set yet") - } - return t.sessionID } // packetCipher represents a combination of SSH encryption/MAC @@ -81,12 +68,6 @@ type connectionState struct { // both directions are triggered by reading and writing a msgNewKey packet // respectively. func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error { - if t.sessionID == nil { - t.sessionID = kexResult.H - } - - kexResult.SessionID = t.sessionID - if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil { return err } else { @@ -119,7 +100,7 @@ func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { case msgNewKeys: select { case cipher := <-s.pendingKeyChange: - s.packetCipher = cipher + s.packetCipher = cipher default: return nil, errors.New("ssh: got bogus newkeys message.") } diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 5182d0532ea9b8cd5b9c27874e4310752038281e..9ca104c957990775ffd1af475c1e9bc1cbafec12 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -872,7 +872,6 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri //sysnb EpollCreate(size int) (fd int, err error) //sysnb EpollCreate1(flag int) (fd int, err error) //sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Exit(code int) = SYS_EXIT_GROUP //sys Faccessat(dirfd int, path string, mode uint32, flags int) (err error) //sys Fallocate(fd int, mode uint32, off int64, len int64) (err error) @@ -907,7 +906,6 @@ func Getpgrp() (pid int) { //sys Mkdirat(dirfd int, path string, mode uint32) (err error) //sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error) //sys Nanosleep(time *Timespec, leftover *Timespec) (err error) -//sys Pause() (err error) //sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT //sysnb prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) = SYS_PRLIMIT64 //sys Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_386.go b/vendor/golang.org/x/sys/unix/syscall_linux_386.go index d5dde1b9467dc23e999264c040bc670543e3c8a2..bea01cb506c35fbbd38f85e4c5b860c61556ec5f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_386.go @@ -91,6 +91,8 @@ func Pipe2(p []int, flags int) (err error) { //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT //sys mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error) +//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) +//sys Pause() (err error) func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) { page := uintptr(offset / 4096) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go b/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go index b7fa9e4f52c25e68483c39c5a9cfa7be48b8113c..721f24b68d3b963cd6f75240867e06ad040badf4 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go @@ -9,6 +9,7 @@ package unix import "syscall" //sys Dup2(oldfd int, newfd int) (err error) +//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64 //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) @@ -25,6 +26,7 @@ import "syscall" //sys Lchown(path string, uid int, gid int) (err error) //sys Listen(s int, n int) (err error) //sys Lstat(path string, stat *Stat_t) (err error) +//sys Pause() (err error) //sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 //sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm.go index 3b4da2061b9d5181ff087fee28c017c026dfb09e..122df649af655508b7eca0c9734a12cd863cc149 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm.go @@ -108,6 +108,8 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { // Vsyscalls on amd64. //sysnb Gettimeofday(tv *Timeval) (err error) +//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) +//sys Pause() (err error) func Time(t *Time_t) (Time_t, error) { var tv Timeval diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go index 9e2e8b72f688dd5beed3c7a43a9ff9fdc7045f9b..d105186803448f7f4318cf3e7e82bcef6f15e1ae 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go @@ -8,6 +8,7 @@ package unix const _SYS_dup = SYS_DUP3 +//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) @@ -154,6 +155,14 @@ func Dup2(oldfd int, newfd int) (err error) { return Dup3(oldfd, newfd, 0) } +func Pause() (err error) { + _, _, e1 := Syscall6(SYS_PPOLL, 0, 0, 0, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + // TODO(dfc): constants that should be in zsysnum_linux_arm64.go, remove // these when the deprecated syscalls that the syscall package relies on // are removed. diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go b/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go index ce7b420c9add233fb34d03aa549d208d7b75fb27..bb15ba3e68a9e6fcc94a6b2821e0dcd3303fb517 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go @@ -14,6 +14,7 @@ package unix // Lookup linux_dirent{,64} in kernel source code for details. const _SYS_getdents = SYS_GETDENTS +//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstatfs(fd int, buf *Statfs_t) (err error) //sys Ftruncate(fd int, length int64) (err error) @@ -24,6 +25,7 @@ const _SYS_getdents = SYS_GETDENTS //sysnb Getuid() (uid int) //sys Lchown(path string, uid int, gid int) (err error) //sys Listen(s int, n int) (err error) +//sys Pause() (err error) //sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 //sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go b/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go index 9560ffa6680a694035043bcc53a9cb1369239375..b156d5242a4111ed25631fbf46bd4ca4f8b5e3d9 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go @@ -7,6 +7,7 @@ package unix +//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Dup2(oldfd int, newfd int) (err error) //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) @@ -22,6 +23,7 @@ package unix //sys Lchown(path string, uid int, gid int) (err error) //sys Listen(s int, n int) (err error) //sys Lstat(path string, stat *Stat_t) (err error) +//sys Pause() (err error) //sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 //sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK diff --git a/vendor/golang.org/x/sys/unix/types_linux.go b/vendor/golang.org/x/sys/unix/types_linux.go index 9a8d2b6f6f14b3243ce442b1adf7ff648df33669..974d28c38b8fc9c0f8c1b2fbac901622075352f7 100644 --- a/vendor/golang.org/x/sys/unix/types_linux.go +++ b/vendor/golang.org/x/sys/unix/types_linux.go @@ -55,6 +55,8 @@ package unix #include <unistd.h> #include <ustat.h> #include <utime.h> +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> #ifdef TCSETS2 // On systems that have "struct termios2" use this as type Termios. @@ -107,7 +109,7 @@ typedef struct user_regs_struct PtraceRegs; // The real epoll_event is a union, and godefs doesn't handle it well. struct my_epoll_event { uint32_t events; -#ifdef __ARM_EABI__ +#if defined(__ARM_EABI__) || defined(__aarch64__) // padding is not specified in linux/eventpoll.h but added to conform to the // alignment requirements of EABI int32_t padFd; diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go index fe1f1dd240e9d42d7a35073b2bcce9b80e622fff..749f3e46e6ebe632cb75738cc262b7fd8bca9982 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go @@ -370,23 +370,6 @@ func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Exit(code int) { Syscall(SYS_EXIT_GROUP, uintptr(code), 0, 0) return @@ -746,16 +729,6 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pause() (err error) { - _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func PivotRoot(newroot string, putold string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(newroot) @@ -1582,6 +1555,33 @@ func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + var _p0 unsafe.Pointer + if len(events) > 0 { + _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pause() (err error) { + _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func getrlimit(resource int, rlim *rlimit32) (err error) { _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go index 510cb1bb16f040b93471044cdc2289412371cb3b..1096aa54436507df21a0867f50c4d4380728d5c2 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go @@ -370,23 +370,6 @@ func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Exit(code int) { Syscall(SYS_EXIT_GROUP, uintptr(code), 0, 0) return @@ -746,16 +729,6 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pause() (err error) { - _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func PivotRoot(newroot string, putold string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(newroot) @@ -1226,6 +1199,23 @@ func Dup2(oldfd int, newfd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + var _p0 unsafe.Pointer + if len(events) > 0 { + _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fadvise(fd int, offset int64, length int64, advice int) (err error) { _, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) if e1 != 0 { @@ -1391,6 +1381,16 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Pause() (err error) { + _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go index 28c720fbc1906d2feb10bc577180e4593d222820..9066e1cb773c251a240757478169126fc1423c26 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go @@ -370,23 +370,6 @@ func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Exit(code int) { Syscall(SYS_EXIT_GROUP, uintptr(code), 0, 0) return @@ -746,16 +729,6 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pause() (err error) { - _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func PivotRoot(newroot string, putold string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(newroot) @@ -1649,6 +1622,33 @@ func Gettimeofday(tv *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + var _p0 unsafe.Pointer + if len(events) > 0 { + _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pause() (err error) { + _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go index 1ac54217adca4c354fa38acbe7e0051f5beaf873..5b916122651386a65eed125dda70f0a741659758 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go @@ -370,23 +370,6 @@ func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Exit(code int) { Syscall(SYS_EXIT_GROUP, uintptr(code), 0, 0) return @@ -746,16 +729,6 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pause() (err error) { - _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func PivotRoot(newroot string, putold string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(newroot) @@ -1216,6 +1189,23 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + var _p0 unsafe.Pointer + if len(events) > 0 { + _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fchown(fd int, uid int, gid int) (err error) { _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go index 724700583d079271917f8acd54efb76e2479fc3a..738c830914cb68bcc30bcc6a416c3937617c21b7 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go @@ -370,23 +370,6 @@ func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Exit(code int) { Syscall(SYS_EXIT_GROUP, uintptr(code), 0, 0) return @@ -746,16 +729,6 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pause() (err error) { - _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func PivotRoot(newroot string, putold string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(newroot) @@ -1216,6 +1189,23 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + var _p0 unsafe.Pointer + if len(events) > 0 { + _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fchown(fd int, uid int, gid int) (err error) { _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { @@ -1314,6 +1304,16 @@ func Listen(s int, n int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Pause() (err error) { + _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go index 1b7fb64ea9e3cdbf5c62651346d68784c6b654cd..2a03578322cd4765f9364e105151d7a5dd0f7787 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go @@ -370,23 +370,6 @@ func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Exit(code int) { Syscall(SYS_EXIT_GROUP, uintptr(code), 0, 0) return @@ -746,16 +729,6 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pause() (err error) { - _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func PivotRoot(newroot string, putold string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(newroot) @@ -1216,6 +1189,23 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + var _p0 unsafe.Pointer + if len(events) > 0 { + _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fchown(fd int, uid int, gid int) (err error) { _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { @@ -1314,6 +1304,16 @@ func Listen(s int, n int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Pause() (err error) { + _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go index 2b4cd7dfba0d32d6551aefed4e56beb2b7d0ba48..844ae592f8429b20d0c31acd1cc3d38f33b78047 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go @@ -370,23 +370,6 @@ func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Exit(code int) { Syscall(SYS_EXIT_GROUP, uintptr(code), 0, 0) return @@ -746,16 +729,6 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pause() (err error) { - _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func PivotRoot(newroot string, putold string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(newroot) @@ -1216,6 +1189,23 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + var _p0 unsafe.Pointer + if len(events) > 0 { + _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Dup2(oldfd int, newfd int) (err error) { _, _, e1 := Syscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0) if e1 != 0 { @@ -1370,6 +1360,16 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Pause() (err error) { + _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go index 7e1708ded84f0f0ccfb8d273fef7e04582b9f7b6..0e86c9d9ed0daf62d25a0cbe78907ec87261c884 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go @@ -370,23 +370,6 @@ func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - var _p0 unsafe.Pointer - if len(events) > 0 { - _p0 = unsafe.Pointer(&events[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Exit(code int) { Syscall(SYS_EXIT_GROUP, uintptr(code), 0, 0) return @@ -746,16 +729,6 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pause() (err error) { - _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func PivotRoot(newroot string, putold string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(newroot) @@ -1216,6 +1189,23 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + var _p0 unsafe.Pointer + if len(events) > 0 { + _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Dup2(oldfd int, newfd int) (err error) { _, _, e1 := Syscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0) if e1 != 0 { @@ -1370,6 +1360,16 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Pause() (err error) { + _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go index 72c7f0910bb75762c0e65663c6dfbd24d36de3ff..fb1257ae020340815ba7b9ab6b1c6f7744111ab8 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go @@ -151,6 +151,15 @@ type Flock_t struct { Pid int32 } +const ( + FADV_NORMAL = 0x0 + FADV_RANDOM = 0x1 + FADV_SEQUENTIAL = 0x2 + FADV_WILLNEED = 0x3 + FADV_DONTNEED = 0x4 + FADV_NOREUSE = 0x5 +) + type RawSockaddrInet4 struct { Family uint16 Port uint16 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go index 70a443f4c12d6245d08023daa99f51f888f8deeb..34edb3685fa29e7660984a470ebf4139870c6312 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go @@ -153,6 +153,15 @@ type Flock_t struct { Pad_cgo_1 [4]byte } +const ( + FADV_NORMAL = 0x0 + FADV_RANDOM = 0x1 + FADV_SEQUENTIAL = 0x2 + FADV_WILLNEED = 0x3 + FADV_DONTNEED = 0x4 + FADV_NOREUSE = 0x5 +) + type RawSockaddrInet4 struct { Family uint16 Port uint16 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go index 7939c4cecddf49a74ddba3773b3dcff2da5b713b..28b7cd43ce73b86a1698c100e75d687576ff0514 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go @@ -580,6 +580,7 @@ type Ustat_t struct { type EpollEvent struct { Events uint32 + PadFd int32 Fd int32 Pad int32 } diff --git a/vendor/gopkg.in/yaml.v2/.travis.yml b/vendor/gopkg.in/yaml.v2/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..004172a2e37f4efe27ad56e6f22ef9ed0cdcb78f --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/.travis.yml @@ -0,0 +1,9 @@ +language: go + +go: + - 1.4 + - 1.5 + - 1.6 + - tip + +go_import_path: gopkg.in/yaml.v2 diff --git a/vendor/gopkg.in/yaml.v2/LICENSE b/vendor/gopkg.in/yaml.v2/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..a68e67f01b0cbbebb44224df30471217ee6ecdd1 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/LICENSE @@ -0,0 +1,188 @@ + +Copyright (c) 2011-2014 - Canonical Inc. + +This software is licensed under the LGPLv3, included below. + +As a special exception to the GNU Lesser General Public License version 3 +("LGPL3"), the copyright holders of this Library give you permission to +convey to a third party a Combined Work that links statically or dynamically +to this Library without providing any Minimal Corresponding Source or +Minimal Application Code as set out in 4d or providing the installation +information set out in section 4e, provided that you comply with the other +provisions of LGPL3 and provided that you meet, for the Application the +terms and conditions of the license(s) which apply to the Application. + +Except as stated in this special exception, the provisions of LGPL3 will +continue to comply in full to this Library. If you modify this Library, you +may apply this exception to your version of this Library, but you are not +obliged to do so. If you do not wish to do so, delete this exception +statement from your version. This exception does not (and cannot) modify any +license terms which apply to the Application, with which you must still +comply. + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/vendor/gopkg.in/yaml.v2/LICENSE.libyaml b/vendor/gopkg.in/yaml.v2/LICENSE.libyaml new file mode 100644 index 0000000000000000000000000000000000000000..8da58fbf6f84a9280f8351ed3c8bfb47c9d787c2 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/LICENSE.libyaml @@ -0,0 +1,31 @@ +The following files were ported to Go from C files of libyaml, and thus +are still covered by their original copyright and license: + + apic.go + emitterc.go + parserc.go + readerc.go + scannerc.go + writerc.go + yamlh.go + yamlprivateh.go + +Copyright (c) 2006 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/gopkg.in/yaml.v2/README.md b/vendor/gopkg.in/yaml.v2/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7b8bd86701e3279d3a924ce3371acb3dcfd35b3b --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/README.md @@ -0,0 +1,131 @@ +# YAML support for the Go language + +Introduction +------------ + +The yaml package enables Go programs to comfortably encode and decode YAML +values. It was developed within [Canonical](https://www.canonical.com) as +part of the [juju](https://juju.ubuntu.com) project, and is based on a +pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) +C library to parse and generate YAML data quickly and reliably. + +Compatibility +------------- + +The yaml package supports most of YAML 1.1 and 1.2, including support for +anchors, tags, map merging, etc. Multi-document unmarshalling is not yet +implemented, and base-60 floats from YAML 1.1 are purposefully not +supported since they're a poor design and are gone in YAML 1.2. + +Installation and usage +---------------------- + +The import path for the package is *gopkg.in/yaml.v2*. + +To install it, run: + + go get gopkg.in/yaml.v2 + +API documentation +----------------- + +If opened in a browser, the import path itself leads to the API documentation: + + * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2) + +API stability +------------- + +The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). + + +License +------- + +The yaml package is licensed under the LGPL with an exception that allows it to be linked statically. Please see the LICENSE file for details. + + +Example +------- + +```Go +package main + +import ( + "fmt" + "log" + + "gopkg.in/yaml.v2" +) + +var data = ` +a: Easy! +b: + c: 2 + d: [3, 4] +` + +type T struct { + A string + B struct { + RenamedC int `yaml:"c"` + D []int `yaml:",flow"` + } +} + +func main() { + t := T{} + + err := yaml.Unmarshal([]byte(data), &t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t:\n%v\n\n", t) + + d, err := yaml.Marshal(&t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t dump:\n%s\n\n", string(d)) + + m := make(map[interface{}]interface{}) + + err = yaml.Unmarshal([]byte(data), &m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m:\n%v\n\n", m) + + d, err = yaml.Marshal(&m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m dump:\n%s\n\n", string(d)) +} +``` + +This example will generate the following output: + +``` +--- t: +{Easy! {2 [3 4]}} + +--- t dump: +a: Easy! +b: + c: 2 + d: [3, 4] + + +--- m: +map[a:Easy! b:map[c:2 d:[3 4]]] + +--- m dump: +a: Easy! +b: + c: 2 + d: + - 3 + - 4 +``` + diff --git a/vendor/gopkg.in/yaml.v2/apic.go b/vendor/gopkg.in/yaml.v2/apic.go new file mode 100644 index 0000000000000000000000000000000000000000..95ec014e8ccfdc818c02cde00ecb617519d910d1 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/apic.go @@ -0,0 +1,742 @@ +package yaml + +import ( + "io" + "os" +) + +func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { + //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) + + // Check if we can move the queue at the beginning of the buffer. + if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { + if parser.tokens_head != len(parser.tokens) { + copy(parser.tokens, parser.tokens[parser.tokens_head:]) + } + parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] + parser.tokens_head = 0 + } + parser.tokens = append(parser.tokens, *token) + if pos < 0 { + return + } + copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) + parser.tokens[parser.tokens_head+pos] = *token +} + +// Create a new parser object. +func yaml_parser_initialize(parser *yaml_parser_t) bool { + *parser = yaml_parser_t{ + raw_buffer: make([]byte, 0, input_raw_buffer_size), + buffer: make([]byte, 0, input_buffer_size), + } + return true +} + +// Destroy a parser object. +func yaml_parser_delete(parser *yaml_parser_t) { + *parser = yaml_parser_t{} +} + +// String read handler. +func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { + if parser.input_pos == len(parser.input) { + return 0, io.EOF + } + n = copy(buffer, parser.input[parser.input_pos:]) + parser.input_pos += n + return n, nil +} + +// File read handler. +func yaml_file_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { + return parser.input_file.Read(buffer) +} + +// Set a string input. +func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { + if parser.read_handler != nil { + panic("must set the input source only once") + } + parser.read_handler = yaml_string_read_handler + parser.input = input + parser.input_pos = 0 +} + +// Set a file input. +func yaml_parser_set_input_file(parser *yaml_parser_t, file *os.File) { + if parser.read_handler != nil { + panic("must set the input source only once") + } + parser.read_handler = yaml_file_read_handler + parser.input_file = file +} + +// Set the source encoding. +func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { + if parser.encoding != yaml_ANY_ENCODING { + panic("must set the encoding only once") + } + parser.encoding = encoding +} + +// Create a new emitter object. +func yaml_emitter_initialize(emitter *yaml_emitter_t) bool { + *emitter = yaml_emitter_t{ + buffer: make([]byte, output_buffer_size), + raw_buffer: make([]byte, 0, output_raw_buffer_size), + states: make([]yaml_emitter_state_t, 0, initial_stack_size), + events: make([]yaml_event_t, 0, initial_queue_size), + } + return true +} + +// Destroy an emitter object. +func yaml_emitter_delete(emitter *yaml_emitter_t) { + *emitter = yaml_emitter_t{} +} + +// String write handler. +func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { + *emitter.output_buffer = append(*emitter.output_buffer, buffer...) + return nil +} + +// File write handler. +func yaml_file_write_handler(emitter *yaml_emitter_t, buffer []byte) error { + _, err := emitter.output_file.Write(buffer) + return err +} + +// Set a string output. +func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { + if emitter.write_handler != nil { + panic("must set the output target only once") + } + emitter.write_handler = yaml_string_write_handler + emitter.output_buffer = output_buffer +} + +// Set a file output. +func yaml_emitter_set_output_file(emitter *yaml_emitter_t, file io.Writer) { + if emitter.write_handler != nil { + panic("must set the output target only once") + } + emitter.write_handler = yaml_file_write_handler + emitter.output_file = file +} + +// Set the output encoding. +func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { + if emitter.encoding != yaml_ANY_ENCODING { + panic("must set the output encoding only once") + } + emitter.encoding = encoding +} + +// Set the canonical output style. +func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { + emitter.canonical = canonical +} + +//// Set the indentation increment. +func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { + if indent < 2 || indent > 9 { + indent = 2 + } + emitter.best_indent = indent +} + +// Set the preferred line width. +func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { + if width < 0 { + width = -1 + } + emitter.best_width = width +} + +// Set if unescaped non-ASCII characters are allowed. +func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { + emitter.unicode = unicode +} + +// Set the preferred line break character. +func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { + emitter.line_break = line_break +} + +///* +// * Destroy a token object. +// */ +// +//YAML_DECLARE(void) +//yaml_token_delete(yaml_token_t *token) +//{ +// assert(token); // Non-NULL token object expected. +// +// switch (token.type) +// { +// case YAML_TAG_DIRECTIVE_TOKEN: +// yaml_free(token.data.tag_directive.handle); +// yaml_free(token.data.tag_directive.prefix); +// break; +// +// case YAML_ALIAS_TOKEN: +// yaml_free(token.data.alias.value); +// break; +// +// case YAML_ANCHOR_TOKEN: +// yaml_free(token.data.anchor.value); +// break; +// +// case YAML_TAG_TOKEN: +// yaml_free(token.data.tag.handle); +// yaml_free(token.data.tag.suffix); +// break; +// +// case YAML_SCALAR_TOKEN: +// yaml_free(token.data.scalar.value); +// break; +// +// default: +// break; +// } +// +// memset(token, 0, sizeof(yaml_token_t)); +//} +// +///* +// * Check if a string is a valid UTF-8 sequence. +// * +// * Check 'reader.c' for more details on UTF-8 encoding. +// */ +// +//static int +//yaml_check_utf8(yaml_char_t *start, size_t length) +//{ +// yaml_char_t *end = start+length; +// yaml_char_t *pointer = start; +// +// while (pointer < end) { +// unsigned char octet; +// unsigned int width; +// unsigned int value; +// size_t k; +// +// octet = pointer[0]; +// width = (octet & 0x80) == 0x00 ? 1 : +// (octet & 0xE0) == 0xC0 ? 2 : +// (octet & 0xF0) == 0xE0 ? 3 : +// (octet & 0xF8) == 0xF0 ? 4 : 0; +// value = (octet & 0x80) == 0x00 ? octet & 0x7F : +// (octet & 0xE0) == 0xC0 ? octet & 0x1F : +// (octet & 0xF0) == 0xE0 ? octet & 0x0F : +// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; +// if (!width) return 0; +// if (pointer+width > end) return 0; +// for (k = 1; k < width; k ++) { +// octet = pointer[k]; +// if ((octet & 0xC0) != 0x80) return 0; +// value = (value << 6) + (octet & 0x3F); +// } +// if (!((width == 1) || +// (width == 2 && value >= 0x80) || +// (width == 3 && value >= 0x800) || +// (width == 4 && value >= 0x10000))) return 0; +// +// pointer += width; +// } +// +// return 1; +//} +// + +// Create STREAM-START. +func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) bool { + *event = yaml_event_t{ + typ: yaml_STREAM_START_EVENT, + encoding: encoding, + } + return true +} + +// Create STREAM-END. +func yaml_stream_end_event_initialize(event *yaml_event_t) bool { + *event = yaml_event_t{ + typ: yaml_STREAM_END_EVENT, + } + return true +} + +// Create DOCUMENT-START. +func yaml_document_start_event_initialize(event *yaml_event_t, version_directive *yaml_version_directive_t, + tag_directives []yaml_tag_directive_t, implicit bool) bool { + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + version_directive: version_directive, + tag_directives: tag_directives, + implicit: implicit, + } + return true +} + +// Create DOCUMENT-END. +func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) bool { + *event = yaml_event_t{ + typ: yaml_DOCUMENT_END_EVENT, + implicit: implicit, + } + return true +} + +///* +// * Create ALIAS. +// */ +// +//YAML_DECLARE(int) +//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t) +//{ +// mark yaml_mark_t = { 0, 0, 0 } +// anchor_copy *yaml_char_t = NULL +// +// assert(event) // Non-NULL event object is expected. +// assert(anchor) // Non-NULL anchor is expected. +// +// if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0 +// +// anchor_copy = yaml_strdup(anchor) +// if (!anchor_copy) +// return 0 +// +// ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark) +// +// return 1 +//} + +// Create SCALAR. +func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + anchor: anchor, + tag: tag, + value: value, + implicit: plain_implicit, + quoted_implicit: quoted_implicit, + style: yaml_style_t(style), + } + return true +} + +// Create SEQUENCE-START. +func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(style), + } + return true +} + +// Create SEQUENCE-END. +func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + } + return true +} + +// Create MAPPING-START. +func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) bool { + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(style), + } + return true +} + +// Create MAPPING-END. +func yaml_mapping_end_event_initialize(event *yaml_event_t) bool { + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + } + return true +} + +// Destroy an event object. +func yaml_event_delete(event *yaml_event_t) { + *event = yaml_event_t{} +} + +///* +// * Create a document object. +// */ +// +//YAML_DECLARE(int) +//yaml_document_initialize(document *yaml_document_t, +// version_directive *yaml_version_directive_t, +// tag_directives_start *yaml_tag_directive_t, +// tag_directives_end *yaml_tag_directive_t, +// start_implicit int, end_implicit int) +//{ +// struct { +// error yaml_error_type_t +// } context +// struct { +// start *yaml_node_t +// end *yaml_node_t +// top *yaml_node_t +// } nodes = { NULL, NULL, NULL } +// version_directive_copy *yaml_version_directive_t = NULL +// struct { +// start *yaml_tag_directive_t +// end *yaml_tag_directive_t +// top *yaml_tag_directive_t +// } tag_directives_copy = { NULL, NULL, NULL } +// value yaml_tag_directive_t = { NULL, NULL } +// mark yaml_mark_t = { 0, 0, 0 } +// +// assert(document) // Non-NULL document object is expected. +// assert((tag_directives_start && tag_directives_end) || +// (tag_directives_start == tag_directives_end)) +// // Valid tag directives are expected. +// +// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error +// +// if (version_directive) { +// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) +// if (!version_directive_copy) goto error +// version_directive_copy.major = version_directive.major +// version_directive_copy.minor = version_directive.minor +// } +// +// if (tag_directives_start != tag_directives_end) { +// tag_directive *yaml_tag_directive_t +// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) +// goto error +// for (tag_directive = tag_directives_start +// tag_directive != tag_directives_end; tag_directive ++) { +// assert(tag_directive.handle) +// assert(tag_directive.prefix) +// if (!yaml_check_utf8(tag_directive.handle, +// strlen((char *)tag_directive.handle))) +// goto error +// if (!yaml_check_utf8(tag_directive.prefix, +// strlen((char *)tag_directive.prefix))) +// goto error +// value.handle = yaml_strdup(tag_directive.handle) +// value.prefix = yaml_strdup(tag_directive.prefix) +// if (!value.handle || !value.prefix) goto error +// if (!PUSH(&context, tag_directives_copy, value)) +// goto error +// value.handle = NULL +// value.prefix = NULL +// } +// } +// +// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, +// tag_directives_copy.start, tag_directives_copy.top, +// start_implicit, end_implicit, mark, mark) +// +// return 1 +// +//error: +// STACK_DEL(&context, nodes) +// yaml_free(version_directive_copy) +// while (!STACK_EMPTY(&context, tag_directives_copy)) { +// value yaml_tag_directive_t = POP(&context, tag_directives_copy) +// yaml_free(value.handle) +// yaml_free(value.prefix) +// } +// STACK_DEL(&context, tag_directives_copy) +// yaml_free(value.handle) +// yaml_free(value.prefix) +// +// return 0 +//} +// +///* +// * Destroy a document object. +// */ +// +//YAML_DECLARE(void) +//yaml_document_delete(document *yaml_document_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// tag_directive *yaml_tag_directive_t +// +// context.error = YAML_NO_ERROR // Eliminate a compliler warning. +// +// assert(document) // Non-NULL document object is expected. +// +// while (!STACK_EMPTY(&context, document.nodes)) { +// node yaml_node_t = POP(&context, document.nodes) +// yaml_free(node.tag) +// switch (node.type) { +// case YAML_SCALAR_NODE: +// yaml_free(node.data.scalar.value) +// break +// case YAML_SEQUENCE_NODE: +// STACK_DEL(&context, node.data.sequence.items) +// break +// case YAML_MAPPING_NODE: +// STACK_DEL(&context, node.data.mapping.pairs) +// break +// default: +// assert(0) // Should not happen. +// } +// } +// STACK_DEL(&context, document.nodes) +// +// yaml_free(document.version_directive) +// for (tag_directive = document.tag_directives.start +// tag_directive != document.tag_directives.end +// tag_directive++) { +// yaml_free(tag_directive.handle) +// yaml_free(tag_directive.prefix) +// } +// yaml_free(document.tag_directives.start) +// +// memset(document, 0, sizeof(yaml_document_t)) +//} +// +///** +// * Get a document node. +// */ +// +//YAML_DECLARE(yaml_node_t *) +//yaml_document_get_node(document *yaml_document_t, index int) +//{ +// assert(document) // Non-NULL document object is expected. +// +// if (index > 0 && document.nodes.start + index <= document.nodes.top) { +// return document.nodes.start + index - 1 +// } +// return NULL +//} +// +///** +// * Get the root object. +// */ +// +//YAML_DECLARE(yaml_node_t *) +//yaml_document_get_root_node(document *yaml_document_t) +//{ +// assert(document) // Non-NULL document object is expected. +// +// if (document.nodes.top != document.nodes.start) { +// return document.nodes.start +// } +// return NULL +//} +// +///* +// * Add a scalar node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_scalar(document *yaml_document_t, +// tag *yaml_char_t, value *yaml_char_t, length int, +// style yaml_scalar_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// value_copy *yaml_char_t = NULL +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// assert(value) // Non-NULL value is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (length < 0) { +// length = strlen((char *)value) +// } +// +// if (!yaml_check_utf8(value, length)) goto error +// value_copy = yaml_malloc(length+1) +// if (!value_copy) goto error +// memcpy(value_copy, value, length) +// value_copy[length] = '\0' +// +// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// yaml_free(tag_copy) +// yaml_free(value_copy) +// +// return 0 +//} +// +///* +// * Add a sequence node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_sequence(document *yaml_document_t, +// tag *yaml_char_t, style yaml_sequence_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// struct { +// start *yaml_node_item_t +// end *yaml_node_item_t +// top *yaml_node_item_t +// } items = { NULL, NULL, NULL } +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error +// +// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, +// style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// STACK_DEL(&context, items) +// yaml_free(tag_copy) +// +// return 0 +//} +// +///* +// * Add a mapping node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_mapping(document *yaml_document_t, +// tag *yaml_char_t, style yaml_mapping_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// struct { +// start *yaml_node_pair_t +// end *yaml_node_pair_t +// top *yaml_node_pair_t +// } pairs = { NULL, NULL, NULL } +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error +// +// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, +// style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// STACK_DEL(&context, pairs) +// yaml_free(tag_copy) +// +// return 0 +//} +// +///* +// * Append an item to a sequence node. +// */ +// +//YAML_DECLARE(int) +//yaml_document_append_sequence_item(document *yaml_document_t, +// sequence int, item int) +//{ +// struct { +// error yaml_error_type_t +// } context +// +// assert(document) // Non-NULL document is required. +// assert(sequence > 0 +// && document.nodes.start + sequence <= document.nodes.top) +// // Valid sequence id is required. +// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) +// // A sequence node is required. +// assert(item > 0 && document.nodes.start + item <= document.nodes.top) +// // Valid item id is required. +// +// if (!PUSH(&context, +// document.nodes.start[sequence-1].data.sequence.items, item)) +// return 0 +// +// return 1 +//} +// +///* +// * Append a pair of a key and a value to a mapping node. +// */ +// +//YAML_DECLARE(int) +//yaml_document_append_mapping_pair(document *yaml_document_t, +// mapping int, key int, value int) +//{ +// struct { +// error yaml_error_type_t +// } context +// +// pair yaml_node_pair_t +// +// assert(document) // Non-NULL document is required. +// assert(mapping > 0 +// && document.nodes.start + mapping <= document.nodes.top) +// // Valid mapping id is required. +// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) +// // A mapping node is required. +// assert(key > 0 && document.nodes.start + key <= document.nodes.top) +// // Valid key id is required. +// assert(value > 0 && document.nodes.start + value <= document.nodes.top) +// // Valid value id is required. +// +// pair.key = key +// pair.value = value +// +// if (!PUSH(&context, +// document.nodes.start[mapping-1].data.mapping.pairs, pair)) +// return 0 +// +// return 1 +//} +// +// diff --git a/vendor/gopkg.in/yaml.v2/decode.go b/vendor/gopkg.in/yaml.v2/decode.go new file mode 100644 index 0000000000000000000000000000000000000000..085cddc44beb962ede8bcf965cd99ab3110916cd --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/decode.go @@ -0,0 +1,683 @@ +package yaml + +import ( + "encoding" + "encoding/base64" + "fmt" + "math" + "reflect" + "strconv" + "time" +) + +const ( + documentNode = 1 << iota + mappingNode + sequenceNode + scalarNode + aliasNode +) + +type node struct { + kind int + line, column int + tag string + value string + implicit bool + children []*node + anchors map[string]*node +} + +// ---------------------------------------------------------------------------- +// Parser, produces a node tree out of a libyaml event stream. + +type parser struct { + parser yaml_parser_t + event yaml_event_t + doc *node +} + +func newParser(b []byte) *parser { + p := parser{} + if !yaml_parser_initialize(&p.parser) { + panic("failed to initialize YAML emitter") + } + + if len(b) == 0 { + b = []byte{'\n'} + } + + yaml_parser_set_input_string(&p.parser, b) + + p.skip() + if p.event.typ != yaml_STREAM_START_EVENT { + panic("expected stream start event, got " + strconv.Itoa(int(p.event.typ))) + } + p.skip() + return &p +} + +func (p *parser) destroy() { + if p.event.typ != yaml_NO_EVENT { + yaml_event_delete(&p.event) + } + yaml_parser_delete(&p.parser) +} + +func (p *parser) skip() { + if p.event.typ != yaml_NO_EVENT { + if p.event.typ == yaml_STREAM_END_EVENT { + failf("attempted to go past the end of stream; corrupted value?") + } + yaml_event_delete(&p.event) + } + if !yaml_parser_parse(&p.parser, &p.event) { + p.fail() + } +} + +func (p *parser) fail() { + var where string + var line int + if p.parser.problem_mark.line != 0 { + line = p.parser.problem_mark.line + } else if p.parser.context_mark.line != 0 { + line = p.parser.context_mark.line + } + if line != 0 { + where = "line " + strconv.Itoa(line) + ": " + } + var msg string + if len(p.parser.problem) > 0 { + msg = p.parser.problem + } else { + msg = "unknown problem parsing YAML content" + } + failf("%s%s", where, msg) +} + +func (p *parser) anchor(n *node, anchor []byte) { + if anchor != nil { + p.doc.anchors[string(anchor)] = n + } +} + +func (p *parser) parse() *node { + switch p.event.typ { + case yaml_SCALAR_EVENT: + return p.scalar() + case yaml_ALIAS_EVENT: + return p.alias() + case yaml_MAPPING_START_EVENT: + return p.mapping() + case yaml_SEQUENCE_START_EVENT: + return p.sequence() + case yaml_DOCUMENT_START_EVENT: + return p.document() + case yaml_STREAM_END_EVENT: + // Happens when attempting to decode an empty buffer. + return nil + default: + panic("attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ))) + } + panic("unreachable") +} + +func (p *parser) node(kind int) *node { + return &node{ + kind: kind, + line: p.event.start_mark.line, + column: p.event.start_mark.column, + } +} + +func (p *parser) document() *node { + n := p.node(documentNode) + n.anchors = make(map[string]*node) + p.doc = n + p.skip() + n.children = append(n.children, p.parse()) + if p.event.typ != yaml_DOCUMENT_END_EVENT { + panic("expected end of document event but got " + strconv.Itoa(int(p.event.typ))) + } + p.skip() + return n +} + +func (p *parser) alias() *node { + n := p.node(aliasNode) + n.value = string(p.event.anchor) + p.skip() + return n +} + +func (p *parser) scalar() *node { + n := p.node(scalarNode) + n.value = string(p.event.value) + n.tag = string(p.event.tag) + n.implicit = p.event.implicit + p.anchor(n, p.event.anchor) + p.skip() + return n +} + +func (p *parser) sequence() *node { + n := p.node(sequenceNode) + p.anchor(n, p.event.anchor) + p.skip() + for p.event.typ != yaml_SEQUENCE_END_EVENT { + n.children = append(n.children, p.parse()) + } + p.skip() + return n +} + +func (p *parser) mapping() *node { + n := p.node(mappingNode) + p.anchor(n, p.event.anchor) + p.skip() + for p.event.typ != yaml_MAPPING_END_EVENT { + n.children = append(n.children, p.parse(), p.parse()) + } + p.skip() + return n +} + +// ---------------------------------------------------------------------------- +// Decoder, unmarshals a node into a provided value. + +type decoder struct { + doc *node + aliases map[string]bool + mapType reflect.Type + terrors []string +} + +var ( + mapItemType = reflect.TypeOf(MapItem{}) + durationType = reflect.TypeOf(time.Duration(0)) + defaultMapType = reflect.TypeOf(map[interface{}]interface{}{}) + ifaceType = defaultMapType.Elem() +) + +func newDecoder() *decoder { + d := &decoder{mapType: defaultMapType} + d.aliases = make(map[string]bool) + return d +} + +func (d *decoder) terror(n *node, tag string, out reflect.Value) { + if n.tag != "" { + tag = n.tag + } + value := n.value + if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG { + if len(value) > 10 { + value = " `" + value[:7] + "...`" + } else { + value = " `" + value + "`" + } + } + d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type())) +} + +func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { + terrlen := len(d.terrors) + err := u.UnmarshalYAML(func(v interface{}) (err error) { + defer handleErr(&err) + d.unmarshal(n, reflect.ValueOf(v)) + if len(d.terrors) > terrlen { + issues := d.terrors[terrlen:] + d.terrors = d.terrors[:terrlen] + return &TypeError{issues} + } + return nil + }) + if e, ok := err.(*TypeError); ok { + d.terrors = append(d.terrors, e.Errors...) + return false + } + if err != nil { + fail(err) + } + return true +} + +// d.prepare initializes and dereferences pointers and calls UnmarshalYAML +// if a value is found to implement it. +// It returns the initialized and dereferenced out value, whether +// unmarshalling was already done by UnmarshalYAML, and if so whether +// its types unmarshalled appropriately. +// +// If n holds a null value, prepare returns before doing anything. +func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { + if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "") { + return out, false, false + } + again := true + for again { + again = false + if out.Kind() == reflect.Ptr { + if out.IsNil() { + out.Set(reflect.New(out.Type().Elem())) + } + out = out.Elem() + again = true + } + if out.CanAddr() { + if u, ok := out.Addr().Interface().(Unmarshaler); ok { + good = d.callUnmarshaler(n, u) + return out, true, good + } + } + } + return out, false, false +} + +func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { + switch n.kind { + case documentNode: + return d.document(n, out) + case aliasNode: + return d.alias(n, out) + } + out, unmarshaled, good := d.prepare(n, out) + if unmarshaled { + return good + } + switch n.kind { + case scalarNode: + good = d.scalar(n, out) + case mappingNode: + good = d.mapping(n, out) + case sequenceNode: + good = d.sequence(n, out) + default: + panic("internal error: unknown node kind: " + strconv.Itoa(n.kind)) + } + return good +} + +func (d *decoder) document(n *node, out reflect.Value) (good bool) { + if len(n.children) == 1 { + d.doc = n + d.unmarshal(n.children[0], out) + return true + } + return false +} + +func (d *decoder) alias(n *node, out reflect.Value) (good bool) { + an, ok := d.doc.anchors[n.value] + if !ok { + failf("unknown anchor '%s' referenced", n.value) + } + if d.aliases[n.value] { + failf("anchor '%s' value contains itself", n.value) + } + d.aliases[n.value] = true + good = d.unmarshal(an, out) + delete(d.aliases, n.value) + return good +} + +var zeroValue reflect.Value + +func resetMap(out reflect.Value) { + for _, k := range out.MapKeys() { + out.SetMapIndex(k, zeroValue) + } +} + +func (d *decoder) scalar(n *node, out reflect.Value) (good bool) { + var tag string + var resolved interface{} + if n.tag == "" && !n.implicit { + tag = yaml_STR_TAG + resolved = n.value + } else { + tag, resolved = resolve(n.tag, n.value) + if tag == yaml_BINARY_TAG { + data, err := base64.StdEncoding.DecodeString(resolved.(string)) + if err != nil { + failf("!!binary value contains invalid base64 data") + } + resolved = string(data) + } + } + if resolved == nil { + if out.Kind() == reflect.Map && !out.CanAddr() { + resetMap(out) + } else { + out.Set(reflect.Zero(out.Type())) + } + return true + } + if s, ok := resolved.(string); ok && out.CanAddr() { + if u, ok := out.Addr().Interface().(encoding.TextUnmarshaler); ok { + err := u.UnmarshalText([]byte(s)) + if err != nil { + fail(err) + } + return true + } + } + switch out.Kind() { + case reflect.String: + if tag == yaml_BINARY_TAG { + out.SetString(resolved.(string)) + good = true + } else if resolved != nil { + out.SetString(n.value) + good = true + } + case reflect.Interface: + if resolved == nil { + out.Set(reflect.Zero(out.Type())) + } else { + out.Set(reflect.ValueOf(resolved)) + } + good = true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch resolved := resolved.(type) { + case int: + if !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + good = true + } + case int64: + if !out.OverflowInt(resolved) { + out.SetInt(resolved) + good = true + } + case uint64: + if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + good = true + } + case float64: + if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + good = true + } + case string: + if out.Type() == durationType { + d, err := time.ParseDuration(resolved) + if err == nil { + out.SetInt(int64(d)) + good = true + } + } + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch resolved := resolved.(type) { + case int: + if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + good = true + } + case int64: + if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + good = true + } + case uint64: + if !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + good = true + } + case float64: + if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + good = true + } + } + case reflect.Bool: + switch resolved := resolved.(type) { + case bool: + out.SetBool(resolved) + good = true + } + case reflect.Float32, reflect.Float64: + switch resolved := resolved.(type) { + case int: + out.SetFloat(float64(resolved)) + good = true + case int64: + out.SetFloat(float64(resolved)) + good = true + case uint64: + out.SetFloat(float64(resolved)) + good = true + case float64: + out.SetFloat(resolved) + good = true + } + case reflect.Ptr: + if out.Type().Elem() == reflect.TypeOf(resolved) { + // TODO DOes this make sense? When is out a Ptr except when decoding a nil value? + elem := reflect.New(out.Type().Elem()) + elem.Elem().Set(reflect.ValueOf(resolved)) + out.Set(elem) + good = true + } + } + if !good { + d.terror(n, tag, out) + } + return good +} + +func settableValueOf(i interface{}) reflect.Value { + v := reflect.ValueOf(i) + sv := reflect.New(v.Type()).Elem() + sv.Set(v) + return sv +} + +func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { + l := len(n.children) + + var iface reflect.Value + switch out.Kind() { + case reflect.Slice: + out.Set(reflect.MakeSlice(out.Type(), l, l)) + case reflect.Interface: + // No type hints. Will have to use a generic sequence. + iface = out + out = settableValueOf(make([]interface{}, l)) + default: + d.terror(n, yaml_SEQ_TAG, out) + return false + } + et := out.Type().Elem() + + j := 0 + for i := 0; i < l; i++ { + e := reflect.New(et).Elem() + if ok := d.unmarshal(n.children[i], e); ok { + out.Index(j).Set(e) + j++ + } + } + out.Set(out.Slice(0, j)) + if iface.IsValid() { + iface.Set(out) + } + return true +} + +func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { + switch out.Kind() { + case reflect.Struct: + return d.mappingStruct(n, out) + case reflect.Slice: + return d.mappingSlice(n, out) + case reflect.Map: + // okay + case reflect.Interface: + if d.mapType.Kind() == reflect.Map { + iface := out + out = reflect.MakeMap(d.mapType) + iface.Set(out) + } else { + slicev := reflect.New(d.mapType).Elem() + if !d.mappingSlice(n, slicev) { + return false + } + out.Set(slicev) + return true + } + default: + d.terror(n, yaml_MAP_TAG, out) + return false + } + outt := out.Type() + kt := outt.Key() + et := outt.Elem() + + mapType := d.mapType + if outt.Key() == ifaceType && outt.Elem() == ifaceType { + d.mapType = outt + } + + if out.IsNil() { + out.Set(reflect.MakeMap(outt)) + } + l := len(n.children) + for i := 0; i < l; i += 2 { + if isMerge(n.children[i]) { + d.merge(n.children[i+1], out) + continue + } + k := reflect.New(kt).Elem() + if d.unmarshal(n.children[i], k) { + kkind := k.Kind() + if kkind == reflect.Interface { + kkind = k.Elem().Kind() + } + if kkind == reflect.Map || kkind == reflect.Slice { + failf("invalid map key: %#v", k.Interface()) + } + e := reflect.New(et).Elem() + if d.unmarshal(n.children[i+1], e) { + out.SetMapIndex(k, e) + } + } + } + d.mapType = mapType + return true +} + +func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) { + outt := out.Type() + if outt.Elem() != mapItemType { + d.terror(n, yaml_MAP_TAG, out) + return false + } + + mapType := d.mapType + d.mapType = outt + + var slice []MapItem + var l = len(n.children) + for i := 0; i < l; i += 2 { + if isMerge(n.children[i]) { + d.merge(n.children[i+1], out) + continue + } + item := MapItem{} + k := reflect.ValueOf(&item.Key).Elem() + if d.unmarshal(n.children[i], k) { + v := reflect.ValueOf(&item.Value).Elem() + if d.unmarshal(n.children[i+1], v) { + slice = append(slice, item) + } + } + } + out.Set(reflect.ValueOf(slice)) + d.mapType = mapType + return true +} + +func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { + sinfo, err := getStructInfo(out.Type()) + if err != nil { + panic(err) + } + name := settableValueOf("") + l := len(n.children) + + var inlineMap reflect.Value + var elemType reflect.Type + if sinfo.InlineMap != -1 { + inlineMap = out.Field(sinfo.InlineMap) + inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) + elemType = inlineMap.Type().Elem() + } + + for i := 0; i < l; i += 2 { + ni := n.children[i] + if isMerge(ni) { + d.merge(n.children[i+1], out) + continue + } + if !d.unmarshal(ni, name) { + continue + } + if info, ok := sinfo.FieldsMap[name.String()]; ok { + var field reflect.Value + if info.Inline == nil { + field = out.Field(info.Num) + } else { + field = out.FieldByIndex(info.Inline) + } + d.unmarshal(n.children[i+1], field) + } else if sinfo.InlineMap != -1 { + if inlineMap.IsNil() { + inlineMap.Set(reflect.MakeMap(inlineMap.Type())) + } + value := reflect.New(elemType).Elem() + d.unmarshal(n.children[i+1], value) + inlineMap.SetMapIndex(name, value) + } + } + return true +} + +func failWantMap() { + failf("map merge requires map or sequence of maps as the value") +} + +func (d *decoder) merge(n *node, out reflect.Value) { + switch n.kind { + case mappingNode: + d.unmarshal(n, out) + case aliasNode: + an, ok := d.doc.anchors[n.value] + if ok && an.kind != mappingNode { + failWantMap() + } + d.unmarshal(n, out) + case sequenceNode: + // Step backwards as earlier nodes take precedence. + for i := len(n.children) - 1; i >= 0; i-- { + ni := n.children[i] + if ni.kind == aliasNode { + an, ok := d.doc.anchors[ni.value] + if ok && an.kind != mappingNode { + failWantMap() + } + } else if ni.kind != mappingNode { + failWantMap() + } + d.unmarshal(ni, out) + } + default: + failWantMap() + } +} + +func isMerge(n *node) bool { + return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG) +} diff --git a/vendor/gopkg.in/yaml.v2/decode_test.go b/vendor/gopkg.in/yaml.v2/decode_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c159760b64f956bc5cd16498504647028621fa4a --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/decode_test.go @@ -0,0 +1,988 @@ +package yaml_test + +import ( + "errors" + . "gopkg.in/check.v1" + "gopkg.in/yaml.v2" + "math" + "net" + "reflect" + "strings" + "time" +) + +var unmarshalIntTest = 123 + +var unmarshalTests = []struct { + data string + value interface{} +}{ + { + "", + &struct{}{}, + }, { + "{}", &struct{}{}, + }, { + "v: hi", + map[string]string{"v": "hi"}, + }, { + "v: hi", map[string]interface{}{"v": "hi"}, + }, { + "v: true", + map[string]string{"v": "true"}, + }, { + "v: true", + map[string]interface{}{"v": true}, + }, { + "v: 10", + map[string]interface{}{"v": 10}, + }, { + "v: 0b10", + map[string]interface{}{"v": 2}, + }, { + "v: 0xA", + map[string]interface{}{"v": 10}, + }, { + "v: 4294967296", + map[string]int64{"v": 4294967296}, + }, { + "v: 0.1", + map[string]interface{}{"v": 0.1}, + }, { + "v: .1", + map[string]interface{}{"v": 0.1}, + }, { + "v: .Inf", + map[string]interface{}{"v": math.Inf(+1)}, + }, { + "v: -.Inf", + map[string]interface{}{"v": math.Inf(-1)}, + }, { + "v: -10", + map[string]interface{}{"v": -10}, + }, { + "v: -.1", + map[string]interface{}{"v": -0.1}, + }, + + // Simple values. + { + "123", + &unmarshalIntTest, + }, + + // Floats from spec + { + "canonical: 6.8523e+5", + map[string]interface{}{"canonical": 6.8523e+5}, + }, { + "expo: 685.230_15e+03", + map[string]interface{}{"expo": 685.23015e+03}, + }, { + "fixed: 685_230.15", + map[string]interface{}{"fixed": 685230.15}, + }, { + "neginf: -.inf", + map[string]interface{}{"neginf": math.Inf(-1)}, + }, { + "fixed: 685_230.15", + map[string]float64{"fixed": 685230.15}, + }, + //{"sexa: 190:20:30.15", map[string]interface{}{"sexa": 0}}, // Unsupported + //{"notanum: .NaN", map[string]interface{}{"notanum": math.NaN()}}, // Equality of NaN fails. + + // Bools from spec + { + "canonical: y", + map[string]interface{}{"canonical": true}, + }, { + "answer: NO", + map[string]interface{}{"answer": false}, + }, { + "logical: True", + map[string]interface{}{"logical": true}, + }, { + "option: on", + map[string]interface{}{"option": true}, + }, { + "option: on", + map[string]bool{"option": true}, + }, + // Ints from spec + { + "canonical: 685230", + map[string]interface{}{"canonical": 685230}, + }, { + "decimal: +685_230", + map[string]interface{}{"decimal": 685230}, + }, { + "octal: 02472256", + map[string]interface{}{"octal": 685230}, + }, { + "hexa: 0x_0A_74_AE", + map[string]interface{}{"hexa": 685230}, + }, { + "bin: 0b1010_0111_0100_1010_1110", + map[string]interface{}{"bin": 685230}, + }, { + "bin: -0b101010", + map[string]interface{}{"bin": -42}, + }, { + "decimal: +685_230", + map[string]int{"decimal": 685230}, + }, + + //{"sexa: 190:20:30", map[string]interface{}{"sexa": 0}}, // Unsupported + + // Nulls from spec + { + "empty:", + map[string]interface{}{"empty": nil}, + }, { + "canonical: ~", + map[string]interface{}{"canonical": nil}, + }, { + "english: null", + map[string]interface{}{"english": nil}, + }, { + "~: null key", + map[interface{}]string{nil: "null key"}, + }, { + "empty:", + map[string]*bool{"empty": nil}, + }, + + // Flow sequence + { + "seq: [A,B]", + map[string]interface{}{"seq": []interface{}{"A", "B"}}, + }, { + "seq: [A,B,C,]", + map[string][]string{"seq": []string{"A", "B", "C"}}, + }, { + "seq: [A,1,C]", + map[string][]string{"seq": []string{"A", "1", "C"}}, + }, { + "seq: [A,1,C]", + map[string][]int{"seq": []int{1}}, + }, { + "seq: [A,1,C]", + map[string]interface{}{"seq": []interface{}{"A", 1, "C"}}, + }, + // Block sequence + { + "seq:\n - A\n - B", + map[string]interface{}{"seq": []interface{}{"A", "B"}}, + }, { + "seq:\n - A\n - B\n - C", + map[string][]string{"seq": []string{"A", "B", "C"}}, + }, { + "seq:\n - A\n - 1\n - C", + map[string][]string{"seq": []string{"A", "1", "C"}}, + }, { + "seq:\n - A\n - 1\n - C", + map[string][]int{"seq": []int{1}}, + }, { + "seq:\n - A\n - 1\n - C", + map[string]interface{}{"seq": []interface{}{"A", 1, "C"}}, + }, + + // Literal block scalar + { + "scalar: | # Comment\n\n literal\n\n \ttext\n\n", + map[string]string{"scalar": "\nliteral\n\n\ttext\n"}, + }, + + // Folded block scalar + { + "scalar: > # Comment\n\n folded\n line\n \n next\n line\n * one\n * two\n\n last\n line\n\n", + map[string]string{"scalar": "\nfolded line\nnext line\n * one\n * two\n\nlast line\n"}, + }, + + // Map inside interface with no type hints. + { + "a: {b: c}", + map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, + }, + + // Structs and type conversions. + { + "hello: world", + &struct{ Hello string }{"world"}, + }, { + "a: {b: c}", + &struct{ A struct{ B string } }{struct{ B string }{"c"}}, + }, { + "a: {b: c}", + &struct{ A *struct{ B string } }{&struct{ B string }{"c"}}, + }, { + "a: {b: c}", + &struct{ A map[string]string }{map[string]string{"b": "c"}}, + }, { + "a: {b: c}", + &struct{ A *map[string]string }{&map[string]string{"b": "c"}}, + }, { + "a:", + &struct{ A map[string]string }{}, + }, { + "a: 1", + &struct{ A int }{1}, + }, { + "a: 1", + &struct{ A float64 }{1}, + }, { + "a: 1.0", + &struct{ A int }{1}, + }, { + "a: 1.0", + &struct{ A uint }{1}, + }, { + "a: [1, 2]", + &struct{ A []int }{[]int{1, 2}}, + }, { + "a: 1", + &struct{ B int }{0}, + }, { + "a: 1", + &struct { + B int "a" + }{1}, + }, { + "a: y", + &struct{ A bool }{true}, + }, + + // Some cross type conversions + { + "v: 42", + map[string]uint{"v": 42}, + }, { + "v: -42", + map[string]uint{}, + }, { + "v: 4294967296", + map[string]uint64{"v": 4294967296}, + }, { + "v: -4294967296", + map[string]uint64{}, + }, + + // int + { + "int_max: 2147483647", + map[string]int{"int_max": math.MaxInt32}, + }, + { + "int_min: -2147483648", + map[string]int{"int_min": math.MinInt32}, + }, + { + "int_overflow: 9223372036854775808", // math.MaxInt64 + 1 + map[string]int{}, + }, + + // int64 + { + "int64_max: 9223372036854775807", + map[string]int64{"int64_max": math.MaxInt64}, + }, + { + "int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111", + map[string]int64{"int64_max_base2": math.MaxInt64}, + }, + { + "int64_min: -9223372036854775808", + map[string]int64{"int64_min": math.MinInt64}, + }, + { + "int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111", + map[string]int64{"int64_neg_base2": -math.MaxInt64}, + }, + { + "int64_overflow: 9223372036854775808", // math.MaxInt64 + 1 + map[string]int64{}, + }, + + // uint + { + "uint_min: 0", + map[string]uint{"uint_min": 0}, + }, + { + "uint_max: 4294967295", + map[string]uint{"uint_max": math.MaxUint32}, + }, + { + "uint_underflow: -1", + map[string]uint{}, + }, + + // uint64 + { + "uint64_min: 0", + map[string]uint{"uint64_min": 0}, + }, + { + "uint64_max: 18446744073709551615", + map[string]uint64{"uint64_max": math.MaxUint64}, + }, + { + "uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111", + map[string]uint64{"uint64_max_base2": math.MaxUint64}, + }, + { + "uint64_maxint64: 9223372036854775807", + map[string]uint64{"uint64_maxint64": math.MaxInt64}, + }, + { + "uint64_underflow: -1", + map[string]uint64{}, + }, + + // float32 + { + "float32_max: 3.40282346638528859811704183484516925440e+38", + map[string]float32{"float32_max": math.MaxFloat32}, + }, + { + "float32_nonzero: 1.401298464324817070923729583289916131280e-45", + map[string]float32{"float32_nonzero": math.SmallestNonzeroFloat32}, + }, + { + "float32_maxuint64: 18446744073709551615", + map[string]float32{"float32_maxuint64": float32(math.MaxUint64)}, + }, + { + "float32_maxuint64+1: 18446744073709551616", + map[string]float32{"float32_maxuint64+1": float32(math.MaxUint64 + 1)}, + }, + + // float64 + { + "float64_max: 1.797693134862315708145274237317043567981e+308", + map[string]float64{"float64_max": math.MaxFloat64}, + }, + { + "float64_nonzero: 4.940656458412465441765687928682213723651e-324", + map[string]float64{"float64_nonzero": math.SmallestNonzeroFloat64}, + }, + { + "float64_maxuint64: 18446744073709551615", + map[string]float64{"float64_maxuint64": float64(math.MaxUint64)}, + }, + { + "float64_maxuint64+1: 18446744073709551616", + map[string]float64{"float64_maxuint64+1": float64(math.MaxUint64 + 1)}, + }, + + // Overflow cases. + { + "v: 4294967297", + map[string]int32{}, + }, { + "v: 128", + map[string]int8{}, + }, + + // Quoted values. + { + "'1': '\"2\"'", + map[interface{}]interface{}{"1": "\"2\""}, + }, { + "v:\n- A\n- 'B\n\n C'\n", + map[string][]string{"v": []string{"A", "B\nC"}}, + }, + + // Explicit tags. + { + "v: !!float '1.1'", + map[string]interface{}{"v": 1.1}, + }, { + "v: !!null ''", + map[string]interface{}{"v": nil}, + }, { + "%TAG !y! tag:yaml.org,2002:\n---\nv: !y!int '1'", + map[string]interface{}{"v": 1}, + }, + + // Anchors and aliases. + { + "a: &x 1\nb: &y 2\nc: *x\nd: *y\n", + &struct{ A, B, C, D int }{1, 2, 1, 2}, + }, { + "a: &a {c: 1}\nb: *a", + &struct { + A, B struct { + C int + } + }{struct{ C int }{1}, struct{ C int }{1}}, + }, { + "a: &a [1, 2]\nb: *a", + &struct{ B []int }{[]int{1, 2}}, + }, { + "b: *a\na: &a {c: 1}", + &struct { + A, B struct { + C int + } + }{struct{ C int }{1}, struct{ C int }{1}}, + }, + + // Bug #1133337 + { + "foo: ''", + map[string]*string{"foo": new(string)}, + }, { + "foo: null", + map[string]string{"foo": ""}, + }, { + "foo: null", + map[string]interface{}{"foo": nil}, + }, + + // Ignored field + { + "a: 1\nb: 2\n", + &struct { + A int + B int "-" + }{1, 0}, + }, + + // Bug #1191981 + { + "" + + "%YAML 1.1\n" + + "--- !!str\n" + + `"Generic line break (no glyph)\n\` + "\n" + + ` Generic line break (glyphed)\n\` + "\n" + + ` Line separator\u2028\` + "\n" + + ` Paragraph separator\u2029"` + "\n", + "" + + "Generic line break (no glyph)\n" + + "Generic line break (glyphed)\n" + + "Line separator\u2028Paragraph separator\u2029", + }, + + // Struct inlining + { + "a: 1\nb: 2\nc: 3\n", + &struct { + A int + C inlineB `yaml:",inline"` + }{1, inlineB{2, inlineC{3}}}, + }, + + // Map inlining + { + "a: 1\nb: 2\nc: 3\n", + &struct { + A int + C map[string]int `yaml:",inline"` + }{1, map[string]int{"b": 2, "c": 3}}, + }, + + // bug 1243827 + { + "a: -b_c", + map[string]interface{}{"a": "-b_c"}, + }, + { + "a: +b_c", + map[string]interface{}{"a": "+b_c"}, + }, + { + "a: 50cent_of_dollar", + map[string]interface{}{"a": "50cent_of_dollar"}, + }, + + // Duration + { + "a: 3s", + map[string]time.Duration{"a": 3 * time.Second}, + }, + + // Issue #24. + { + "a: <foo>", + map[string]string{"a": "<foo>"}, + }, + + // Base 60 floats are obsolete and unsupported. + { + "a: 1:1\n", + map[string]string{"a": "1:1"}, + }, + + // Binary data. + { + "a: !!binary gIGC\n", + map[string]string{"a": "\x80\x81\x82"}, + }, { + "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", + map[string]string{"a": strings.Repeat("\x90", 54)}, + }, { + "a: !!binary |\n " + strings.Repeat("A", 70) + "\n ==\n", + map[string]string{"a": strings.Repeat("\x00", 52)}, + }, + + // Ordered maps. + { + "{b: 2, a: 1, d: 4, c: 3, sub: {e: 5}}", + &yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}}, + }, + + // Issue #39. + { + "a:\n b:\n c: d\n", + map[string]struct{ B interface{} }{"a": {map[interface{}]interface{}{"c": "d"}}}, + }, + + // Custom map type. + { + "a: {b: c}", + M{"a": M{"b": "c"}}, + }, + + // Support encoding.TextUnmarshaler. + { + "a: 1.2.3.4\n", + map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, + }, + { + "a: 2015-02-24T18:19:39Z\n", + map[string]time.Time{"a": time.Unix(1424801979, 0)}, + }, + + // Encode empty lists as zero-length slices. + { + "a: []", + &struct{ A []int }{[]int{}}, + }, + + // UTF-16-LE + { + "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n\x00", + M{"ñoño": "very yes"}, + }, + // UTF-16-LE with surrogate. + { + "\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \x00=\xd8\xd4\xdf\n\x00", + M{"ñoño": "very yes 🟔"}, + }, + + // UTF-16-BE + { + "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n", + M{"ñoño": "very yes"}, + }, + // UTF-16-BE with surrogate. + { + "\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \xd8=\xdf\xd4\x00\n", + M{"ñoño": "very yes 🟔"}, + }, +} + +type M map[interface{}]interface{} + +type inlineB struct { + B int + inlineC `yaml:",inline"` +} + +type inlineC struct { + C int +} + +func (s *S) TestUnmarshal(c *C) { + for _, item := range unmarshalTests { + t := reflect.ValueOf(item.value).Type() + var value interface{} + switch t.Kind() { + case reflect.Map: + value = reflect.MakeMap(t).Interface() + case reflect.String: + value = reflect.New(t).Interface() + case reflect.Ptr: + value = reflect.New(t.Elem()).Interface() + default: + c.Fatalf("missing case for %s", t) + } + err := yaml.Unmarshal([]byte(item.data), value) + if _, ok := err.(*yaml.TypeError); !ok { + c.Assert(err, IsNil) + } + if t.Kind() == reflect.String { + c.Assert(*value.(*string), Equals, item.value) + } else { + c.Assert(value, DeepEquals, item.value) + } + } +} + +func (s *S) TestUnmarshalNaN(c *C) { + value := map[string]interface{}{} + err := yaml.Unmarshal([]byte("notanum: .NaN"), &value) + c.Assert(err, IsNil) + c.Assert(math.IsNaN(value["notanum"].(float64)), Equals, true) +} + +var unmarshalErrorTests = []struct { + data, error string +}{ + {"v: !!float 'error'", "yaml: cannot decode !!str `error` as a !!float"}, + {"v: [A,", "yaml: line 1: did not find expected node content"}, + {"v:\n- [A,", "yaml: line 2: did not find expected node content"}, + {"a: *b\n", "yaml: unknown anchor 'b' referenced"}, + {"a: &a\n b: *a\n", "yaml: anchor 'a' value contains itself"}, + {"value: -", "yaml: block sequence entries are not allowed in this context"}, + {"a: !!binary ==", "yaml: !!binary value contains invalid base64 data"}, + {"{[.]}", `yaml: invalid map key: \[\]interface \{\}\{"\."\}`}, + {"{{.}}", `yaml: invalid map key: map\[interface\ \{\}\]interface \{\}\{".":interface \{\}\(nil\)\}`}, +} + +func (s *S) TestUnmarshalErrors(c *C) { + for _, item := range unmarshalErrorTests { + var value interface{} + err := yaml.Unmarshal([]byte(item.data), &value) + c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value)) + } +} + +var unmarshalerTests = []struct { + data, tag string + value interface{} +}{ + {"_: {hi: there}", "!!map", map[interface{}]interface{}{"hi": "there"}}, + {"_: [1,A]", "!!seq", []interface{}{1, "A"}}, + {"_: 10", "!!int", 10}, + {"_: null", "!!null", nil}, + {`_: BAR!`, "!!str", "BAR!"}, + {`_: "BAR!"`, "!!str", "BAR!"}, + {"_: !!foo 'BAR!'", "!!foo", "BAR!"}, +} + +var unmarshalerResult = map[int]error{} + +type unmarshalerType struct { + value interface{} +} + +func (o *unmarshalerType) UnmarshalYAML(unmarshal func(v interface{}) error) error { + if err := unmarshal(&o.value); err != nil { + return err + } + if i, ok := o.value.(int); ok { + if result, ok := unmarshalerResult[i]; ok { + return result + } + } + return nil +} + +type unmarshalerPointer struct { + Field *unmarshalerType "_" +} + +type unmarshalerValue struct { + Field unmarshalerType "_" +} + +func (s *S) TestUnmarshalerPointerField(c *C) { + for _, item := range unmarshalerTests { + obj := &unmarshalerPointer{} + err := yaml.Unmarshal([]byte(item.data), obj) + c.Assert(err, IsNil) + if item.value == nil { + c.Assert(obj.Field, IsNil) + } else { + c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value)) + c.Assert(obj.Field.value, DeepEquals, item.value) + } + } +} + +func (s *S) TestUnmarshalerValueField(c *C) { + for _, item := range unmarshalerTests { + obj := &unmarshalerValue{} + err := yaml.Unmarshal([]byte(item.data), obj) + c.Assert(err, IsNil) + c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value)) + c.Assert(obj.Field.value, DeepEquals, item.value) + } +} + +func (s *S) TestUnmarshalerWholeDocument(c *C) { + obj := &unmarshalerType{} + err := yaml.Unmarshal([]byte(unmarshalerTests[0].data), obj) + c.Assert(err, IsNil) + value, ok := obj.value.(map[interface{}]interface{}) + c.Assert(ok, Equals, true, Commentf("value: %#v", obj.value)) + c.Assert(value["_"], DeepEquals, unmarshalerTests[0].value) +} + +func (s *S) TestUnmarshalerTypeError(c *C) { + unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}} + unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}} + defer func() { + delete(unmarshalerResult, 2) + delete(unmarshalerResult, 4) + }() + + type T struct { + Before int + After int + M map[string]*unmarshalerType + } + var v T + data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}` + err := yaml.Unmarshal([]byte(data), &v) + c.Assert(err, ErrorMatches, ""+ + "yaml: unmarshal errors:\n"+ + " line 1: cannot unmarshal !!str `A` into int\n"+ + " foo\n"+ + " bar\n"+ + " line 1: cannot unmarshal !!str `B` into int") + c.Assert(v.M["abc"], NotNil) + c.Assert(v.M["def"], IsNil) + c.Assert(v.M["ghi"], NotNil) + c.Assert(v.M["jkl"], IsNil) + + c.Assert(v.M["abc"].value, Equals, 1) + c.Assert(v.M["ghi"].value, Equals, 3) +} + +type proxyTypeError struct{} + +func (v *proxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + var a int32 + var b int64 + if err := unmarshal(&s); err != nil { + panic(err) + } + if s == "a" { + if err := unmarshal(&b); err == nil { + panic("should have failed") + } + return unmarshal(&a) + } + if err := unmarshal(&a); err == nil { + panic("should have failed") + } + return unmarshal(&b) +} + +func (s *S) TestUnmarshalerTypeErrorProxying(c *C) { + type T struct { + Before int + After int + M map[string]*proxyTypeError + } + var v T + data := `{before: A, m: {abc: a, def: b}, after: B}` + err := yaml.Unmarshal([]byte(data), &v) + c.Assert(err, ErrorMatches, ""+ + "yaml: unmarshal errors:\n"+ + " line 1: cannot unmarshal !!str `A` into int\n"+ + " line 1: cannot unmarshal !!str `a` into int32\n"+ + " line 1: cannot unmarshal !!str `b` into int64\n"+ + " line 1: cannot unmarshal !!str `B` into int") +} + +type failingUnmarshaler struct{} + +var failingErr = errors.New("failingErr") + +func (ft *failingUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error { + return failingErr +} + +func (s *S) TestUnmarshalerError(c *C) { + err := yaml.Unmarshal([]byte("a: b"), &failingUnmarshaler{}) + c.Assert(err, Equals, failingErr) +} + +type sliceUnmarshaler []int + +func (su *sliceUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error { + var slice []int + err := unmarshal(&slice) + if err == nil { + *su = slice + return nil + } + + var intVal int + err = unmarshal(&intVal) + if err == nil { + *su = []int{intVal} + return nil + } + + return err +} + +func (s *S) TestUnmarshalerRetry(c *C) { + var su sliceUnmarshaler + err := yaml.Unmarshal([]byte("[1, 2, 3]"), &su) + c.Assert(err, IsNil) + c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1, 2, 3})) + + err = yaml.Unmarshal([]byte("1"), &su) + c.Assert(err, IsNil) + c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1})) +} + +// From http://yaml.org/type/merge.html +var mergeTests = ` +anchors: + list: + - &CENTER { "x": 1, "y": 2 } + - &LEFT { "x": 0, "y": 2 } + - &BIG { "r": 10 } + - &SMALL { "r": 1 } + +# All the following maps are equal: + +plain: + # Explicit keys + "x": 1 + "y": 2 + "r": 10 + label: center/big + +mergeOne: + # Merge one map + << : *CENTER + "r": 10 + label: center/big + +mergeMultiple: + # Merge multiple maps + << : [ *CENTER, *BIG ] + label: center/big + +override: + # Override + << : [ *BIG, *LEFT, *SMALL ] + "x": 1 + label: center/big + +shortTag: + # Explicit short merge tag + !!merge "<<" : [ *CENTER, *BIG ] + label: center/big + +longTag: + # Explicit merge long tag + !<tag:yaml.org,2002:merge> "<<" : [ *CENTER, *BIG ] + label: center/big + +inlineMap: + # Inlined map + << : {"x": 1, "y": 2, "r": 10} + label: center/big + +inlineSequenceMap: + # Inlined map in sequence + << : [ *CENTER, {"r": 10} ] + label: center/big +` + +func (s *S) TestMerge(c *C) { + var want = map[interface{}]interface{}{ + "x": 1, + "y": 2, + "r": 10, + "label": "center/big", + } + + var m map[interface{}]interface{} + err := yaml.Unmarshal([]byte(mergeTests), &m) + c.Assert(err, IsNil) + for name, test := range m { + if name == "anchors" { + continue + } + c.Assert(test, DeepEquals, want, Commentf("test %q failed", name)) + } +} + +func (s *S) TestMergeStruct(c *C) { + type Data struct { + X, Y, R int + Label string + } + want := Data{1, 2, 10, "center/big"} + + var m map[string]Data + err := yaml.Unmarshal([]byte(mergeTests), &m) + c.Assert(err, IsNil) + for name, test := range m { + if name == "anchors" { + continue + } + c.Assert(test, Equals, want, Commentf("test %q failed", name)) + } +} + +var unmarshalNullTests = []func() interface{}{ + func() interface{} { var v interface{}; v = "v"; return &v }, + func() interface{} { var s = "s"; return &s }, + func() interface{} { var s = "s"; sptr := &s; return &sptr }, + func() interface{} { var i = 1; return &i }, + func() interface{} { var i = 1; iptr := &i; return &iptr }, + func() interface{} { m := map[string]int{"s": 1}; return &m }, + func() interface{} { m := map[string]int{"s": 1}; return m }, +} + +func (s *S) TestUnmarshalNull(c *C) { + for _, test := range unmarshalNullTests { + item := test() + zero := reflect.Zero(reflect.TypeOf(item).Elem()).Interface() + err := yaml.Unmarshal([]byte("null"), item) + c.Assert(err, IsNil) + if reflect.TypeOf(item).Kind() == reflect.Map { + c.Assert(reflect.ValueOf(item).Interface(), DeepEquals, reflect.MakeMap(reflect.TypeOf(item)).Interface()) + } else { + c.Assert(reflect.ValueOf(item).Elem().Interface(), DeepEquals, zero) + } + } +} + +func (s *S) TestUnmarshalSliceOnPreset(c *C) { + // Issue #48. + v := struct{ A []int }{[]int{1}} + yaml.Unmarshal([]byte("a: [2]"), &v) + c.Assert(v.A, DeepEquals, []int{2}) +} + +//var data []byte +//func init() { +// var err error +// data, err = ioutil.ReadFile("/tmp/file.yaml") +// if err != nil { +// panic(err) +// } +//} +// +//func (s *S) BenchmarkUnmarshal(c *C) { +// var err error +// for i := 0; i < c.N; i++ { +// var v map[string]interface{} +// err = yaml.Unmarshal(data, &v) +// } +// if err != nil { +// panic(err) +// } +//} +// +//func (s *S) BenchmarkMarshal(c *C) { +// var v map[string]interface{} +// yaml.Unmarshal(data, &v) +// c.ResetTimer() +// for i := 0; i < c.N; i++ { +// yaml.Marshal(&v) +// } +//} diff --git a/vendor/gopkg.in/yaml.v2/emitterc.go b/vendor/gopkg.in/yaml.v2/emitterc.go new file mode 100644 index 0000000000000000000000000000000000000000..2befd553ed0263a2bb85819e344e244014dfadc9 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/emitterc.go @@ -0,0 +1,1685 @@ +package yaml + +import ( + "bytes" +) + +// Flush the buffer if needed. +func flush(emitter *yaml_emitter_t) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) { + return yaml_emitter_flush(emitter) + } + return true +} + +// Put a character to the output buffer. +func put(emitter *yaml_emitter_t, value byte) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + emitter.buffer[emitter.buffer_pos] = value + emitter.buffer_pos++ + emitter.column++ + return true +} + +// Put a line break to the output buffer. +func put_break(emitter *yaml_emitter_t) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + switch emitter.line_break { + case yaml_CR_BREAK: + emitter.buffer[emitter.buffer_pos] = '\r' + emitter.buffer_pos += 1 + case yaml_LN_BREAK: + emitter.buffer[emitter.buffer_pos] = '\n' + emitter.buffer_pos += 1 + case yaml_CRLN_BREAK: + emitter.buffer[emitter.buffer_pos+0] = '\r' + emitter.buffer[emitter.buffer_pos+1] = '\n' + emitter.buffer_pos += 2 + default: + panic("unknown line break setting") + } + emitter.column = 0 + emitter.line++ + return true +} + +// Copy a character from a string into buffer. +func write(emitter *yaml_emitter_t, s []byte, i *int) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + p := emitter.buffer_pos + w := width(s[*i]) + switch w { + case 4: + emitter.buffer[p+3] = s[*i+3] + fallthrough + case 3: + emitter.buffer[p+2] = s[*i+2] + fallthrough + case 2: + emitter.buffer[p+1] = s[*i+1] + fallthrough + case 1: + emitter.buffer[p+0] = s[*i+0] + default: + panic("unknown character width") + } + emitter.column++ + emitter.buffer_pos += w + *i += w + return true +} + +// Write a whole string into buffer. +func write_all(emitter *yaml_emitter_t, s []byte) bool { + for i := 0; i < len(s); { + if !write(emitter, s, &i) { + return false + } + } + return true +} + +// Copy a line break character from a string into buffer. +func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool { + if s[*i] == '\n' { + if !put_break(emitter) { + return false + } + *i++ + } else { + if !write(emitter, s, i) { + return false + } + emitter.column = 0 + emitter.line++ + } + return true +} + +// Set an emitter error and return false. +func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_EMITTER_ERROR + emitter.problem = problem + return false +} + +// Emit an event. +func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool { + emitter.events = append(emitter.events, *event) + for !yaml_emitter_need_more_events(emitter) { + event := &emitter.events[emitter.events_head] + if !yaml_emitter_analyze_event(emitter, event) { + return false + } + if !yaml_emitter_state_machine(emitter, event) { + return false + } + yaml_event_delete(event) + emitter.events_head++ + } + return true +} + +// Check if we need to accumulate more events before emitting. +// +// We accumulate extra +// - 1 event for DOCUMENT-START +// - 2 events for SEQUENCE-START +// - 3 events for MAPPING-START +// +func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { + if emitter.events_head == len(emitter.events) { + return true + } + var accumulate int + switch emitter.events[emitter.events_head].typ { + case yaml_DOCUMENT_START_EVENT: + accumulate = 1 + break + case yaml_SEQUENCE_START_EVENT: + accumulate = 2 + break + case yaml_MAPPING_START_EVENT: + accumulate = 3 + break + default: + return false + } + if len(emitter.events)-emitter.events_head > accumulate { + return false + } + var level int + for i := emitter.events_head; i < len(emitter.events); i++ { + switch emitter.events[i].typ { + case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT: + level++ + case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT: + level-- + } + if level == 0 { + return false + } + } + return true +} + +// Append a directive to the directives stack. +func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool { + for i := 0; i < len(emitter.tag_directives); i++ { + if bytes.Equal(value.handle, emitter.tag_directives[i].handle) { + if allow_duplicates { + return true + } + return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive") + } + } + + // [Go] Do we actually need to copy this given garbage collection + // and the lack of deallocating destructors? + tag_copy := yaml_tag_directive_t{ + handle: make([]byte, len(value.handle)), + prefix: make([]byte, len(value.prefix)), + } + copy(tag_copy.handle, value.handle) + copy(tag_copy.prefix, value.prefix) + emitter.tag_directives = append(emitter.tag_directives, tag_copy) + return true +} + +// Increase the indentation level. +func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { + emitter.indents = append(emitter.indents, emitter.indent) + if emitter.indent < 0 { + if flow { + emitter.indent = emitter.best_indent + } else { + emitter.indent = 0 + } + } else if !indentless { + emitter.indent += emitter.best_indent + } + return true +} + +// State dispatcher. +func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool { + switch emitter.state { + default: + case yaml_EMIT_STREAM_START_STATE: + return yaml_emitter_emit_stream_start(emitter, event) + + case yaml_EMIT_FIRST_DOCUMENT_START_STATE: + return yaml_emitter_emit_document_start(emitter, event, true) + + case yaml_EMIT_DOCUMENT_START_STATE: + return yaml_emitter_emit_document_start(emitter, event, false) + + case yaml_EMIT_DOCUMENT_CONTENT_STATE: + return yaml_emitter_emit_document_content(emitter, event) + + case yaml_EMIT_DOCUMENT_END_STATE: + return yaml_emitter_emit_document_end(emitter, event) + + case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, true) + + case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, false) + + case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, true) + + case yaml_EMIT_FLOW_MAPPING_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, false) + + case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: + return yaml_emitter_emit_flow_mapping_value(emitter, event, true) + + case yaml_EMIT_FLOW_MAPPING_VALUE_STATE: + return yaml_emitter_emit_flow_mapping_value(emitter, event, false) + + case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE: + return yaml_emitter_emit_block_sequence_item(emitter, event, true) + + case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE: + return yaml_emitter_emit_block_sequence_item(emitter, event, false) + + case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE: + return yaml_emitter_emit_block_mapping_key(emitter, event, true) + + case yaml_EMIT_BLOCK_MAPPING_KEY_STATE: + return yaml_emitter_emit_block_mapping_key(emitter, event, false) + + case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE: + return yaml_emitter_emit_block_mapping_value(emitter, event, true) + + case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE: + return yaml_emitter_emit_block_mapping_value(emitter, event, false) + + case yaml_EMIT_END_STATE: + return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END") + } + panic("invalid emitter state") +} + +// Expect STREAM-START. +func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if event.typ != yaml_STREAM_START_EVENT { + return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START") + } + if emitter.encoding == yaml_ANY_ENCODING { + emitter.encoding = event.encoding + if emitter.encoding == yaml_ANY_ENCODING { + emitter.encoding = yaml_UTF8_ENCODING + } + } + if emitter.best_indent < 2 || emitter.best_indent > 9 { + emitter.best_indent = 2 + } + if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 { + emitter.best_width = 80 + } + if emitter.best_width < 0 { + emitter.best_width = 1<<31 - 1 + } + if emitter.line_break == yaml_ANY_BREAK { + emitter.line_break = yaml_LN_BREAK + } + + emitter.indent = -1 + emitter.line = 0 + emitter.column = 0 + emitter.whitespace = true + emitter.indention = true + + if emitter.encoding != yaml_UTF8_ENCODING { + if !yaml_emitter_write_bom(emitter) { + return false + } + } + emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE + return true +} + +// Expect DOCUMENT-START or STREAM-END. +func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + + if event.typ == yaml_DOCUMENT_START_EVENT { + + if event.version_directive != nil { + if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) { + return false + } + } + + for i := 0; i < len(event.tag_directives); i++ { + tag_directive := &event.tag_directives[i] + if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) { + return false + } + if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) { + return false + } + } + + for i := 0; i < len(default_tag_directives); i++ { + tag_directive := &default_tag_directives[i] + if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) { + return false + } + } + + implicit := event.implicit + if !first || emitter.canonical { + implicit = false + } + + if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) { + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if event.version_directive != nil { + implicit = false + if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if len(event.tag_directives) > 0 { + implicit = false + for i := 0; i < len(event.tag_directives); i++ { + tag_directive := &event.tag_directives[i] + if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) { + return false + } + if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) { + return false + } + if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + } + + if yaml_emitter_check_empty_document(emitter) { + implicit = false + } + if !implicit { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) { + return false + } + if emitter.canonical { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + } + + emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE + return true + } + + if event.typ == yaml_STREAM_END_EVENT { + if emitter.open_ended { + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_flush(emitter) { + return false + } + emitter.state = yaml_EMIT_END_STATE + return true + } + + return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END") +} + +// Expect the root node. +func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { + emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) + return yaml_emitter_emit_node(emitter, event, true, false, false, false) +} + +// Expect DOCUMENT-END. +func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if event.typ != yaml_DOCUMENT_END_EVENT { + return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END") + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !event.implicit { + // [Go] Allocate the slice elsewhere. + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_flush(emitter) { + return false + } + emitter.state = yaml_EMIT_DOCUMENT_START_STATE + emitter.tag_directives = emitter.tag_directives[:0] + return true +} + +// Expect a flow item node. +func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + emitter.flow_level++ + } + + if event.typ == yaml_SEQUENCE_END_EVENT { + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.canonical && !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + + return true + } + + if !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) + return yaml_emitter_emit_node(emitter, event, false, true, false, false) +} + +// Expect a flow key node. +func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + emitter.flow_level++ + } + + if event.typ == yaml_MAPPING_END_EVENT { + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.canonical && !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + + if !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if !emitter.canonical && yaml_emitter_check_simple_key(emitter) { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, true) + } + if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a flow value node. +func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { + if simple { + if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { + return false + } + } else { + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a block item node. +func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_increase_indent(emitter, false, emitter.mapping_context && !emitter.indention) { + return false + } + } + if event.typ == yaml_SEQUENCE_END_EVENT { + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE) + return yaml_emitter_emit_node(emitter, event, false, true, false, false) +} + +// Expect a block key node. +func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_increase_indent(emitter, false, false) { + return false + } + } + if event.typ == yaml_MAPPING_END_EVENT { + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if yaml_emitter_check_simple_key(emitter) { + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, true) + } + if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a block value node. +func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { + if simple { + if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { + return false + } + } else { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a node. +func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, + root bool, sequence bool, mapping bool, simple_key bool) bool { + + emitter.root_context = root + emitter.sequence_context = sequence + emitter.mapping_context = mapping + emitter.simple_key_context = simple_key + + switch event.typ { + case yaml_ALIAS_EVENT: + return yaml_emitter_emit_alias(emitter, event) + case yaml_SCALAR_EVENT: + return yaml_emitter_emit_scalar(emitter, event) + case yaml_SEQUENCE_START_EVENT: + return yaml_emitter_emit_sequence_start(emitter, event) + case yaml_MAPPING_START_EVENT: + return yaml_emitter_emit_mapping_start(emitter, event) + default: + return yaml_emitter_set_emitter_error(emitter, + "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS") + } + return false +} + +// Expect ALIAS. +func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true +} + +// Expect SCALAR. +func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_select_scalar_style(emitter, event) { + return false + } + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + if !yaml_emitter_process_scalar(emitter) { + return false + } + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true +} + +// Expect SEQUENCE-START. +func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE || + yaml_emitter_check_empty_sequence(emitter) { + emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE + } else { + emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE + } + return true +} + +// Expect MAPPING-START. +func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE || + yaml_emitter_check_empty_mapping(emitter) { + emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE + } else { + emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE + } + return true +} + +// Check if the document content is an empty scalar. +func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool { + return false // [Go] Huh? +} + +// Check if the next events represent an empty sequence. +func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool { + if len(emitter.events)-emitter.events_head < 2 { + return false + } + return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT && + emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT +} + +// Check if the next events represent an empty mapping. +func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool { + if len(emitter.events)-emitter.events_head < 2 { + return false + } + return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT && + emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT +} + +// Check if the next node can be expressed as a simple key. +func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool { + length := 0 + switch emitter.events[emitter.events_head].typ { + case yaml_ALIAS_EVENT: + length += len(emitter.anchor_data.anchor) + case yaml_SCALAR_EVENT: + if emitter.scalar_data.multiline { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + + len(emitter.scalar_data.value) + case yaml_SEQUENCE_START_EVENT: + if !yaml_emitter_check_empty_sequence(emitter) { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + case yaml_MAPPING_START_EVENT: + if !yaml_emitter_check_empty_mapping(emitter) { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + default: + return false + } + return length <= 128 +} + +// Determine an acceptable scalar style. +func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool { + + no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 + if no_tag && !event.implicit && !event.quoted_implicit { + return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified") + } + + style := event.scalar_style() + if style == yaml_ANY_SCALAR_STYLE { + style = yaml_PLAIN_SCALAR_STYLE + } + if emitter.canonical { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + if emitter.simple_key_context && emitter.scalar_data.multiline { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + + if style == yaml_PLAIN_SCALAR_STYLE { + if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed || + emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + if no_tag && !event.implicit { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + } + if style == yaml_SINGLE_QUOTED_SCALAR_STYLE { + if !emitter.scalar_data.single_quoted_allowed { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + } + if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE { + if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + } + + if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE { + emitter.tag_data.handle = []byte{'!'} + } + emitter.scalar_data.style = style + return true +} + +// Write an achor. +func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool { + if emitter.anchor_data.anchor == nil { + return true + } + c := []byte{'&'} + if emitter.anchor_data.alias { + c[0] = '*' + } + if !yaml_emitter_write_indicator(emitter, c, true, false, false) { + return false + } + return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor) +} + +// Write a tag. +func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool { + if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 { + return true + } + if len(emitter.tag_data.handle) > 0 { + if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) { + return false + } + if len(emitter.tag_data.suffix) > 0 { + if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { + return false + } + } + } else { + // [Go] Allocate these slices elsewhere. + if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) { + return false + } + if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) { + return false + } + } + return true +} + +// Write a scalar. +func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool { + switch emitter.scalar_data.style { + case yaml_PLAIN_SCALAR_STYLE: + return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_SINGLE_QUOTED_SCALAR_STYLE: + return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_DOUBLE_QUOTED_SCALAR_STYLE: + return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_LITERAL_SCALAR_STYLE: + return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value) + + case yaml_FOLDED_SCALAR_STYLE: + return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value) + } + panic("unknown scalar style") +} + +// Check if a %YAML directive is valid. +func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool { + if version_directive.major != 1 || version_directive.minor != 1 { + return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive") + } + return true +} + +// Check if a %TAG directive is valid. +func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool { + handle := tag_directive.handle + prefix := tag_directive.prefix + if len(handle) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty") + } + if handle[0] != '!' { + return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'") + } + if handle[len(handle)-1] != '!' { + return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'") + } + for i := 1; i < len(handle)-1; i += width(handle[i]) { + if !is_alpha(handle, i) { + return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only") + } + } + if len(prefix) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty") + } + return true +} + +// Check if an anchor is valid. +func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool { + if len(anchor) == 0 { + problem := "anchor value must not be empty" + if alias { + problem = "alias value must not be empty" + } + return yaml_emitter_set_emitter_error(emitter, problem) + } + for i := 0; i < len(anchor); i += width(anchor[i]) { + if !is_alpha(anchor, i) { + problem := "anchor value must contain alphanumerical characters only" + if alias { + problem = "alias value must contain alphanumerical characters only" + } + return yaml_emitter_set_emitter_error(emitter, problem) + } + } + emitter.anchor_data.anchor = anchor + emitter.anchor_data.alias = alias + return true +} + +// Check if a tag is valid. +func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool { + if len(tag) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty") + } + for i := 0; i < len(emitter.tag_directives); i++ { + tag_directive := &emitter.tag_directives[i] + if bytes.HasPrefix(tag, tag_directive.prefix) { + emitter.tag_data.handle = tag_directive.handle + emitter.tag_data.suffix = tag[len(tag_directive.prefix):] + return true + } + } + emitter.tag_data.suffix = tag + return true +} + +// Check if a scalar is valid. +func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { + var ( + block_indicators = false + flow_indicators = false + line_breaks = false + special_characters = false + + leading_space = false + leading_break = false + trailing_space = false + trailing_break = false + break_space = false + space_break = false + + preceeded_by_whitespace = false + followed_by_whitespace = false + previous_space = false + previous_break = false + ) + + emitter.scalar_data.value = value + + if len(value) == 0 { + emitter.scalar_data.multiline = false + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = true + emitter.scalar_data.single_quoted_allowed = true + emitter.scalar_data.block_allowed = false + return true + } + + if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) { + block_indicators = true + flow_indicators = true + } + + preceeded_by_whitespace = true + for i, w := 0, 0; i < len(value); i += w { + w = width(value[i]) + followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) + + if i == 0 { + switch value[i] { + case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`': + flow_indicators = true + block_indicators = true + case '?', ':': + flow_indicators = true + if followed_by_whitespace { + block_indicators = true + } + case '-': + if followed_by_whitespace { + flow_indicators = true + block_indicators = true + } + } + } else { + switch value[i] { + case ',', '?', '[', ']', '{', '}': + flow_indicators = true + case ':': + flow_indicators = true + if followed_by_whitespace { + block_indicators = true + } + case '#': + if preceeded_by_whitespace { + flow_indicators = true + block_indicators = true + } + } + } + + if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { + special_characters = true + } + if is_space(value, i) { + if i == 0 { + leading_space = true + } + if i+width(value[i]) == len(value) { + trailing_space = true + } + if previous_break { + break_space = true + } + previous_space = true + previous_break = false + } else if is_break(value, i) { + line_breaks = true + if i == 0 { + leading_break = true + } + if i+width(value[i]) == len(value) { + trailing_break = true + } + if previous_space { + space_break = true + } + previous_space = false + previous_break = true + } else { + previous_space = false + previous_break = false + } + + // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition. + preceeded_by_whitespace = is_blankz(value, i) + } + + emitter.scalar_data.multiline = line_breaks + emitter.scalar_data.flow_plain_allowed = true + emitter.scalar_data.block_plain_allowed = true + emitter.scalar_data.single_quoted_allowed = true + emitter.scalar_data.block_allowed = true + + if leading_space || leading_break || trailing_space || trailing_break { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + } + if trailing_space { + emitter.scalar_data.block_allowed = false + } + if break_space { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + emitter.scalar_data.single_quoted_allowed = false + } + if space_break || special_characters { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + emitter.scalar_data.single_quoted_allowed = false + emitter.scalar_data.block_allowed = false + } + if line_breaks { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + } + if flow_indicators { + emitter.scalar_data.flow_plain_allowed = false + } + if block_indicators { + emitter.scalar_data.block_plain_allowed = false + } + return true +} + +// Check if the event data is valid. +func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { + + emitter.anchor_data.anchor = nil + emitter.tag_data.handle = nil + emitter.tag_data.suffix = nil + emitter.scalar_data.value = nil + + switch event.typ { + case yaml_ALIAS_EVENT: + if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) { + return false + } + + case yaml_SCALAR_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + if !yaml_emitter_analyze_scalar(emitter, event.value) { + return false + } + + case yaml_SEQUENCE_START_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + + case yaml_MAPPING_START_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + } + return true +} + +// Write the BOM character. +func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool { + if !flush(emitter) { + return false + } + pos := emitter.buffer_pos + emitter.buffer[pos+0] = '\xEF' + emitter.buffer[pos+1] = '\xBB' + emitter.buffer[pos+2] = '\xBF' + emitter.buffer_pos += 3 + return true +} + +func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool { + indent := emitter.indent + if indent < 0 { + indent = 0 + } + if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) { + if !put_break(emitter) { + return false + } + } + for emitter.column < indent { + if !put(emitter, ' ') { + return false + } + } + emitter.whitespace = true + emitter.indention = true + return true +} + +func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool { + if need_whitespace && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !write_all(emitter, indicator) { + return false + } + emitter.whitespace = is_whitespace + emitter.indention = (emitter.indention && is_indention) + emitter.open_ended = false + return true +} + +func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool { + if !write_all(emitter, value) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool { + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !write_all(emitter, value) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool { + if need_whitespace && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + for i := 0; i < len(value); { + var must_write bool + switch value[i] { + case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']': + must_write = true + default: + must_write = is_alpha(value, i) + } + if must_write { + if !write(emitter, value, &i) { + return false + } + } else { + w := width(value[i]) + for k := 0; k < w; k++ { + octet := value[i] + i++ + if !put(emitter, '%') { + return false + } + + c := octet >> 4 + if c < 10 { + c += '0' + } else { + c += 'A' - 10 + } + if !put(emitter, c) { + return false + } + + c = octet & 0x0f + if c < 10 { + c += '0' + } else { + c += 'A' - 10 + } + if !put(emitter, c) { + return false + } + } + } + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + + spaces := false + breaks := false + for i := 0; i < len(value); { + if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + spaces = true + } else if is_break(value, i) { + if !breaks && value[i] == '\n' { + if !put_break(emitter) { + return false + } + } + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + spaces = false + breaks = false + } + } + + emitter.whitespace = false + emitter.indention = false + if emitter.root_context { + emitter.open_ended = true + } + + return true +} + +func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + + if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) { + return false + } + + spaces := false + breaks := false + for i := 0; i < len(value); { + if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + spaces = true + } else if is_break(value, i) { + if !breaks && value[i] == '\n' { + if !put_break(emitter) { + return false + } + } + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if value[i] == '\'' { + if !put(emitter, '\'') { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + spaces = false + breaks = false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + spaces := false + if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) { + return false + } + + for i := 0; i < len(value); { + if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) || + is_bom(value, i) || is_break(value, i) || + value[i] == '"' || value[i] == '\\' { + + octet := value[i] + + var w int + var v rune + switch { + case octet&0x80 == 0x00: + w, v = 1, rune(octet&0x7F) + case octet&0xE0 == 0xC0: + w, v = 2, rune(octet&0x1F) + case octet&0xF0 == 0xE0: + w, v = 3, rune(octet&0x0F) + case octet&0xF8 == 0xF0: + w, v = 4, rune(octet&0x07) + } + for k := 1; k < w; k++ { + octet = value[i+k] + v = (v << 6) + (rune(octet) & 0x3F) + } + i += w + + if !put(emitter, '\\') { + return false + } + + var ok bool + switch v { + case 0x00: + ok = put(emitter, '0') + case 0x07: + ok = put(emitter, 'a') + case 0x08: + ok = put(emitter, 'b') + case 0x09: + ok = put(emitter, 't') + case 0x0A: + ok = put(emitter, 'n') + case 0x0b: + ok = put(emitter, 'v') + case 0x0c: + ok = put(emitter, 'f') + case 0x0d: + ok = put(emitter, 'r') + case 0x1b: + ok = put(emitter, 'e') + case 0x22: + ok = put(emitter, '"') + case 0x5c: + ok = put(emitter, '\\') + case 0x85: + ok = put(emitter, 'N') + case 0xA0: + ok = put(emitter, '_') + case 0x2028: + ok = put(emitter, 'L') + case 0x2029: + ok = put(emitter, 'P') + default: + if v <= 0xFF { + ok = put(emitter, 'x') + w = 2 + } else if v <= 0xFFFF { + ok = put(emitter, 'u') + w = 4 + } else { + ok = put(emitter, 'U') + w = 8 + } + for k := (w - 1) * 4; ok && k >= 0; k -= 4 { + digit := byte((v >> uint(k)) & 0x0F) + if digit < 10 { + ok = put(emitter, digit+'0') + } else { + ok = put(emitter, digit+'A'-10) + } + } + } + if !ok { + return false + } + spaces = false + } else if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 { + if !yaml_emitter_write_indent(emitter) { + return false + } + if is_space(value, i+1) { + if !put(emitter, '\\') { + return false + } + } + i += width(value[i]) + } else if !write(emitter, value, &i) { + return false + } + spaces = true + } else { + if !write(emitter, value, &i) { + return false + } + spaces = false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool { + if is_space(value, 0) || is_break(value, 0) { + indent_hint := []byte{'0' + byte(emitter.best_indent)} + if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) { + return false + } + } + + emitter.open_ended = false + + var chomp_hint [1]byte + if len(value) == 0 { + chomp_hint[0] = '-' + } else { + i := len(value) - 1 + for value[i]&0xC0 == 0x80 { + i-- + } + if !is_break(value, i) { + chomp_hint[0] = '-' + } else if i == 0 { + chomp_hint[0] = '+' + emitter.open_ended = true + } else { + i-- + for value[i]&0xC0 == 0x80 { + i-- + } + if is_break(value, i) { + chomp_hint[0] = '+' + emitter.open_ended = true + } + } + } + if chomp_hint[0] != 0 { + if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) { + return false + } + } + return true +} + +func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool { + if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) { + return false + } + if !yaml_emitter_write_block_scalar_hints(emitter, value) { + return false + } + if !put_break(emitter) { + return false + } + emitter.indention = true + emitter.whitespace = true + breaks := true + for i := 0; i < len(value); { + if is_break(value, i) { + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + breaks = false + } + } + + return true +} + +func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool { + if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) { + return false + } + if !yaml_emitter_write_block_scalar_hints(emitter, value) { + return false + } + + if !put_break(emitter) { + return false + } + emitter.indention = true + emitter.whitespace = true + + breaks := true + leading_spaces := true + for i := 0; i < len(value); { + if is_break(value, i) { + if !breaks && !leading_spaces && value[i] == '\n' { + k := 0 + for is_break(value, k) { + k += width(value[k]) + } + if !is_blankz(value, k) { + if !put_break(emitter) { + return false + } + } + } + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + leading_spaces = is_blank(value, i) + } + if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + emitter.indention = false + breaks = false + } + } + return true +} diff --git a/vendor/gopkg.in/yaml.v2/encode.go b/vendor/gopkg.in/yaml.v2/encode.go new file mode 100644 index 0000000000000000000000000000000000000000..84f84995517b6bced35582e4890988cab474e934 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/encode.go @@ -0,0 +1,306 @@ +package yaml + +import ( + "encoding" + "fmt" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "time" +) + +type encoder struct { + emitter yaml_emitter_t + event yaml_event_t + out []byte + flow bool +} + +func newEncoder() (e *encoder) { + e = &encoder{} + e.must(yaml_emitter_initialize(&e.emitter)) + yaml_emitter_set_output_string(&e.emitter, &e.out) + yaml_emitter_set_unicode(&e.emitter, true) + e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)) + e.emit() + e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true)) + e.emit() + return e +} + +func (e *encoder) finish() { + e.must(yaml_document_end_event_initialize(&e.event, true)) + e.emit() + e.emitter.open_ended = false + e.must(yaml_stream_end_event_initialize(&e.event)) + e.emit() +} + +func (e *encoder) destroy() { + yaml_emitter_delete(&e.emitter) +} + +func (e *encoder) emit() { + // This will internally delete the e.event value. + if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT { + e.must(false) + } +} + +func (e *encoder) must(ok bool) { + if !ok { + msg := e.emitter.problem + if msg == "" { + msg = "unknown problem generating YAML content" + } + failf("%s", msg) + } +} + +func (e *encoder) marshal(tag string, in reflect.Value) { + if !in.IsValid() { + e.nilv() + return + } + iface := in.Interface() + if m, ok := iface.(Marshaler); ok { + v, err := m.MarshalYAML() + if err != nil { + fail(err) + } + if v == nil { + e.nilv() + return + } + in = reflect.ValueOf(v) + } else if m, ok := iface.(encoding.TextMarshaler); ok { + text, err := m.MarshalText() + if err != nil { + fail(err) + } + in = reflect.ValueOf(string(text)) + } + switch in.Kind() { + case reflect.Interface: + if in.IsNil() { + e.nilv() + } else { + e.marshal(tag, in.Elem()) + } + case reflect.Map: + e.mapv(tag, in) + case reflect.Ptr: + if in.IsNil() { + e.nilv() + } else { + e.marshal(tag, in.Elem()) + } + case reflect.Struct: + e.structv(tag, in) + case reflect.Slice: + if in.Type().Elem() == mapItemType { + e.itemsv(tag, in) + } else { + e.slicev(tag, in) + } + case reflect.String: + e.stringv(tag, in) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if in.Type() == durationType { + e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String())) + } else { + e.intv(tag, in) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + e.uintv(tag, in) + case reflect.Float32, reflect.Float64: + e.floatv(tag, in) + case reflect.Bool: + e.boolv(tag, in) + default: + panic("cannot marshal type: " + in.Type().String()) + } +} + +func (e *encoder) mapv(tag string, in reflect.Value) { + e.mappingv(tag, func() { + keys := keyList(in.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + e.marshal("", k) + e.marshal("", in.MapIndex(k)) + } + }) +} + +func (e *encoder) itemsv(tag string, in reflect.Value) { + e.mappingv(tag, func() { + slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem) + for _, item := range slice { + e.marshal("", reflect.ValueOf(item.Key)) + e.marshal("", reflect.ValueOf(item.Value)) + } + }) +} + +func (e *encoder) structv(tag string, in reflect.Value) { + sinfo, err := getStructInfo(in.Type()) + if err != nil { + panic(err) + } + e.mappingv(tag, func() { + for _, info := range sinfo.FieldsList { + var value reflect.Value + if info.Inline == nil { + value = in.Field(info.Num) + } else { + value = in.FieldByIndex(info.Inline) + } + if info.OmitEmpty && isZero(value) { + continue + } + e.marshal("", reflect.ValueOf(info.Key)) + e.flow = info.Flow + e.marshal("", value) + } + if sinfo.InlineMap >= 0 { + m := in.Field(sinfo.InlineMap) + if m.Len() > 0 { + e.flow = false + keys := keyList(m.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + if _, found := sinfo.FieldsMap[k.String()]; found { + panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String())) + } + e.marshal("", k) + e.flow = false + e.marshal("", m.MapIndex(k)) + } + } + } + }) +} + +func (e *encoder) mappingv(tag string, f func()) { + implicit := tag == "" + style := yaml_BLOCK_MAPPING_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_MAPPING_STYLE + } + e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) + e.emit() + f() + e.must(yaml_mapping_end_event_initialize(&e.event)) + e.emit() +} + +func (e *encoder) slicev(tag string, in reflect.Value) { + implicit := tag == "" + style := yaml_BLOCK_SEQUENCE_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_SEQUENCE_STYLE + } + e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) + e.emit() + n := in.Len() + for i := 0; i < n; i++ { + e.marshal("", in.Index(i)) + } + e.must(yaml_sequence_end_event_initialize(&e.event)) + e.emit() +} + +// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. +// +// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported +// in YAML 1.2 and by this package, but these should be marshalled quoted for +// the time being for compatibility with other parsers. +func isBase60Float(s string) (result bool) { + // Fast path. + if s == "" { + return false + } + c := s[0] + if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { + return false + } + // Do the full match. + return base60float.MatchString(s) +} + +// From http://yaml.org/type/float.html, except the regular expression there +// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. +var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) + +func (e *encoder) stringv(tag string, in reflect.Value) { + var style yaml_scalar_style_t + s := in.String() + rtag, rs := resolve("", s) + if rtag == yaml_BINARY_TAG { + if tag == "" || tag == yaml_STR_TAG { + tag = rtag + s = rs.(string) + } else if tag == yaml_BINARY_TAG { + failf("explicitly tagged !!binary data must be base64-encoded") + } else { + failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) + } + } + if tag == "" && (rtag != yaml_STR_TAG || isBase60Float(s)) { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } else if strings.Contains(s, "\n") { + style = yaml_LITERAL_SCALAR_STYLE + } else { + style = yaml_PLAIN_SCALAR_STYLE + } + e.emitScalar(s, "", tag, style) +} + +func (e *encoder) boolv(tag string, in reflect.Value) { + var s string + if in.Bool() { + s = "true" + } else { + s = "false" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) intv(tag string, in reflect.Value) { + s := strconv.FormatInt(in.Int(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) uintv(tag string, in reflect.Value) { + s := strconv.FormatUint(in.Uint(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) floatv(tag string, in reflect.Value) { + // FIXME: Handle 64 bits here. + s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32) + switch s { + case "+Inf": + s = ".inf" + case "-Inf": + s = "-.inf" + case "NaN": + s = ".nan" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) nilv() { + e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { + implicit := tag == "" + e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) + e.emit() +} diff --git a/vendor/gopkg.in/yaml.v2/encode_test.go b/vendor/gopkg.in/yaml.v2/encode_test.go new file mode 100644 index 0000000000000000000000000000000000000000..84099bd3850a5189f6b5e7375e1786ab3bcdd754 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/encode_test.go @@ -0,0 +1,501 @@ +package yaml_test + +import ( + "fmt" + "math" + "strconv" + "strings" + "time" + + . "gopkg.in/check.v1" + "gopkg.in/yaml.v2" + "net" + "os" +) + +var marshalIntTest = 123 + +var marshalTests = []struct { + value interface{} + data string +}{ + { + nil, + "null\n", + }, { + &struct{}{}, + "{}\n", + }, { + map[string]string{"v": "hi"}, + "v: hi\n", + }, { + map[string]interface{}{"v": "hi"}, + "v: hi\n", + }, { + map[string]string{"v": "true"}, + "v: \"true\"\n", + }, { + map[string]string{"v": "false"}, + "v: \"false\"\n", + }, { + map[string]interface{}{"v": true}, + "v: true\n", + }, { + map[string]interface{}{"v": false}, + "v: false\n", + }, { + map[string]interface{}{"v": 10}, + "v: 10\n", + }, { + map[string]interface{}{"v": -10}, + "v: -10\n", + }, { + map[string]uint{"v": 42}, + "v: 42\n", + }, { + map[string]interface{}{"v": int64(4294967296)}, + "v: 4294967296\n", + }, { + map[string]int64{"v": int64(4294967296)}, + "v: 4294967296\n", + }, { + map[string]uint64{"v": 4294967296}, + "v: 4294967296\n", + }, { + map[string]interface{}{"v": "10"}, + "v: \"10\"\n", + }, { + map[string]interface{}{"v": 0.1}, + "v: 0.1\n", + }, { + map[string]interface{}{"v": float64(0.1)}, + "v: 0.1\n", + }, { + map[string]interface{}{"v": -0.1}, + "v: -0.1\n", + }, { + map[string]interface{}{"v": math.Inf(+1)}, + "v: .inf\n", + }, { + map[string]interface{}{"v": math.Inf(-1)}, + "v: -.inf\n", + }, { + map[string]interface{}{"v": math.NaN()}, + "v: .nan\n", + }, { + map[string]interface{}{"v": nil}, + "v: null\n", + }, { + map[string]interface{}{"v": ""}, + "v: \"\"\n", + }, { + map[string][]string{"v": []string{"A", "B"}}, + "v:\n- A\n- B\n", + }, { + map[string][]string{"v": []string{"A", "B\nC"}}, + "v:\n- A\n- |-\n B\n C\n", + }, { + map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}}, + "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", + }, { + map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, + "a:\n b: c\n", + }, { + map[string]interface{}{"a": "-"}, + "a: '-'\n", + }, + + // Simple values. + { + &marshalIntTest, + "123\n", + }, + + // Structures + { + &struct{ Hello string }{"world"}, + "hello: world\n", + }, { + &struct { + A struct { + B string + } + }{struct{ B string }{"c"}}, + "a:\n b: c\n", + }, { + &struct { + A *struct { + B string + } + }{&struct{ B string }{"c"}}, + "a:\n b: c\n", + }, { + &struct { + A *struct { + B string + } + }{}, + "a: null\n", + }, { + &struct{ A int }{1}, + "a: 1\n", + }, { + &struct{ A []int }{[]int{1, 2}}, + "a:\n- 1\n- 2\n", + }, { + &struct { + B int "a" + }{1}, + "a: 1\n", + }, { + &struct{ A bool }{true}, + "a: true\n", + }, + + // Conditional flag + { + &struct { + A int "a,omitempty" + B int "b,omitempty" + }{1, 0}, + "a: 1\n", + }, { + &struct { + A int "a,omitempty" + B int "b,omitempty" + }{0, 0}, + "{}\n", + }, { + &struct { + A *struct{ X, y int } "a,omitempty,flow" + }{&struct{ X, y int }{1, 2}}, + "a: {x: 1}\n", + }, { + &struct { + A *struct{ X, y int } "a,omitempty,flow" + }{nil}, + "{}\n", + }, { + &struct { + A *struct{ X, y int } "a,omitempty,flow" + }{&struct{ X, y int }{}}, + "a: {x: 0}\n", + }, { + &struct { + A struct{ X, y int } "a,omitempty,flow" + }{struct{ X, y int }{1, 2}}, + "a: {x: 1}\n", + }, { + &struct { + A struct{ X, y int } "a,omitempty,flow" + }{struct{ X, y int }{0, 1}}, + "{}\n", + }, { + &struct { + A float64 "a,omitempty" + B float64 "b,omitempty" + }{1, 0}, + "a: 1\n", + }, + + // Flow flag + { + &struct { + A []int "a,flow" + }{[]int{1, 2}}, + "a: [1, 2]\n", + }, { + &struct { + A map[string]string "a,flow" + }{map[string]string{"b": "c", "d": "e"}}, + "a: {b: c, d: e}\n", + }, { + &struct { + A struct { + B, D string + } "a,flow" + }{struct{ B, D string }{"c", "e"}}, + "a: {b: c, d: e}\n", + }, + + // Unexported field + { + &struct { + u int + A int + }{0, 1}, + "a: 1\n", + }, + + // Ignored field + { + &struct { + A int + B int "-" + }{1, 2}, + "a: 1\n", + }, + + // Struct inlining + { + &struct { + A int + C inlineB `yaml:",inline"` + }{1, inlineB{2, inlineC{3}}}, + "a: 1\nb: 2\nc: 3\n", + }, + + // Map inlining + { + &struct { + A int + C map[string]int `yaml:",inline"` + }{1, map[string]int{"b": 2, "c": 3}}, + "a: 1\nb: 2\nc: 3\n", + }, + + // Duration + { + map[string]time.Duration{"a": 3 * time.Second}, + "a: 3s\n", + }, + + // Issue #24: bug in map merging logic. + { + map[string]string{"a": "<foo>"}, + "a: <foo>\n", + }, + + // Issue #34: marshal unsupported base 60 floats quoted for compatibility + // with old YAML 1.1 parsers. + { + map[string]string{"a": "1:1"}, + "a: \"1:1\"\n", + }, + + // Binary data. + { + map[string]string{"a": "\x00"}, + "a: \"\\0\"\n", + }, { + map[string]string{"a": "\x80\x81\x82"}, + "a: !!binary gIGC\n", + }, { + map[string]string{"a": strings.Repeat("\x90", 54)}, + "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", + }, + + // Ordered maps. + { + &yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}}, + "b: 2\na: 1\nd: 4\nc: 3\nsub:\n e: 5\n", + }, + + // Encode unicode as utf-8 rather than in escaped form. + { + map[string]string{"a": "ä½ å¥½"}, + "a: ä½ å¥½\n", + }, + + // Support encoding.TextMarshaler. + { + map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, + "a: 1.2.3.4\n", + }, + { + map[string]time.Time{"a": time.Unix(1424801979, 0)}, + "a: 2015-02-24T18:19:39Z\n", + }, + + // Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible). + { + map[string]string{"a": "b: c"}, + "a: 'b: c'\n", + }, + + // Containing hash mark ('#') in string should be quoted + { + map[string]string{"a": "Hello #comment"}, + "a: 'Hello #comment'\n", + }, + { + map[string]string{"a": "ä½ å¥½ #comment"}, + "a: 'ä½ å¥½ #comment'\n", + }, +} + +func (s *S) TestMarshal(c *C) { + defer os.Setenv("TZ", os.Getenv("TZ")) + os.Setenv("TZ", "UTC") + for _, item := range marshalTests { + data, err := yaml.Marshal(item.value) + c.Assert(err, IsNil) + c.Assert(string(data), Equals, item.data) + } +} + +var marshalErrorTests = []struct { + value interface{} + error string + panic string +}{{ + value: &struct { + B int + inlineB ",inline" + }{1, inlineB{2, inlineC{3}}}, + panic: `Duplicated key 'b' in struct struct \{ B int; .*`, +}, { + value: &struct { + A int + B map[string]int ",inline" + }{1, map[string]int{"a": 2}}, + panic: `Can't have key "a" in inlined map; conflicts with struct field`, +}} + +func (s *S) TestMarshalErrors(c *C) { + for _, item := range marshalErrorTests { + if item.panic != "" { + c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic) + } else { + _, err := yaml.Marshal(item.value) + c.Assert(err, ErrorMatches, item.error) + } + } +} + +func (s *S) TestMarshalTypeCache(c *C) { + var data []byte + var err error + func() { + type T struct{ A int } + data, err = yaml.Marshal(&T{}) + c.Assert(err, IsNil) + }() + func() { + type T struct{ B int } + data, err = yaml.Marshal(&T{}) + c.Assert(err, IsNil) + }() + c.Assert(string(data), Equals, "b: 0\n") +} + +var marshalerTests = []struct { + data string + value interface{} +}{ + {"_:\n hi: there\n", map[interface{}]interface{}{"hi": "there"}}, + {"_:\n- 1\n- A\n", []interface{}{1, "A"}}, + {"_: 10\n", 10}, + {"_: null\n", nil}, + {"_: BAR!\n", "BAR!"}, +} + +type marshalerType struct { + value interface{} +} + +func (o marshalerType) MarshalText() ([]byte, error) { + panic("MarshalText called on type with MarshalYAML") +} + +func (o marshalerType) MarshalYAML() (interface{}, error) { + return o.value, nil +} + +type marshalerValue struct { + Field marshalerType "_" +} + +func (s *S) TestMarshaler(c *C) { + for _, item := range marshalerTests { + obj := &marshalerValue{} + obj.Field.value = item.value + data, err := yaml.Marshal(obj) + c.Assert(err, IsNil) + c.Assert(string(data), Equals, string(item.data)) + } +} + +func (s *S) TestMarshalerWholeDocument(c *C) { + obj := &marshalerType{} + obj.value = map[string]string{"hello": "world!"} + data, err := yaml.Marshal(obj) + c.Assert(err, IsNil) + c.Assert(string(data), Equals, "hello: world!\n") +} + +type failingMarshaler struct{} + +func (ft *failingMarshaler) MarshalYAML() (interface{}, error) { + return nil, failingErr +} + +func (s *S) TestMarshalerError(c *C) { + _, err := yaml.Marshal(&failingMarshaler{}) + c.Assert(err, Equals, failingErr) +} + +func (s *S) TestSortedOutput(c *C) { + order := []interface{}{ + false, + true, + 1, + uint(1), + 1.0, + 1.1, + 1.2, + 2, + uint(2), + 2.0, + 2.1, + "", + ".1", + ".2", + ".a", + "1", + "2", + "a!10", + "a/2", + "a/10", + "a~10", + "ab/1", + "b/1", + "b/01", + "b/2", + "b/02", + "b/3", + "b/03", + "b1", + "b01", + "b3", + "c2.10", + "c10.2", + "d1", + "d12", + "d12a", + } + m := make(map[interface{}]int) + for _, k := range order { + m[k] = 1 + } + data, err := yaml.Marshal(m) + c.Assert(err, IsNil) + out := "\n" + string(data) + last := 0 + for i, k := range order { + repr := fmt.Sprint(k) + if s, ok := k.(string); ok { + if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil { + repr = `"` + repr + `"` + } + } + index := strings.Index(out, "\n"+repr+":") + if index == -1 { + c.Fatalf("%#v is not in the output: %#v", k, out) + } + if index < last { + c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out) + } + last = index + } +} diff --git a/vendor/gopkg.in/yaml.v2/parserc.go b/vendor/gopkg.in/yaml.v2/parserc.go new file mode 100644 index 0000000000000000000000000000000000000000..0a7037ad1b2a6c352b7250278daf2feb9bb23a42 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/parserc.go @@ -0,0 +1,1096 @@ +package yaml + +import ( + "bytes" +) + +// The parser implements the following grammar: +// +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// implicit_document ::= block_node DOCUMENT-END* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// block_node_or_indentless_sequence ::= +// ALIAS +// | properties (block_content | indentless_block_sequence)? +// | block_content +// | indentless_block_sequence +// block_node ::= ALIAS +// | properties block_content? +// | block_content +// flow_node ::= ALIAS +// | properties flow_content? +// | flow_content +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// block_content ::= block_collection | flow_collection | SCALAR +// flow_content ::= flow_collection | SCALAR +// block_collection ::= block_sequence | block_mapping +// flow_collection ::= flow_sequence | flow_mapping +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// block_mapping ::= BLOCK-MAPPING_START +// ((KEY block_node_or_indentless_sequence?)? +// (VALUE block_node_or_indentless_sequence?)?)* +// BLOCK-END +// flow_sequence ::= FLOW-SEQUENCE-START +// (flow_sequence_entry FLOW-ENTRY)* +// flow_sequence_entry? +// FLOW-SEQUENCE-END +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// flow_mapping ::= FLOW-MAPPING-START +// (flow_mapping_entry FLOW-ENTRY)* +// flow_mapping_entry? +// FLOW-MAPPING-END +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + +// Peek the next token in the token queue. +func peek_token(parser *yaml_parser_t) *yaml_token_t { + if parser.token_available || yaml_parser_fetch_more_tokens(parser) { + return &parser.tokens[parser.tokens_head] + } + return nil +} + +// Remove the next token from the queue (must be called after peek_token). +func skip_token(parser *yaml_parser_t) { + parser.token_available = false + parser.tokens_parsed++ + parser.stream_end_produced = parser.tokens[parser.tokens_head].typ == yaml_STREAM_END_TOKEN + parser.tokens_head++ +} + +// Get the next event. +func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool { + // Erase the event object. + *event = yaml_event_t{} + + // No events after the end of the stream or error. + if parser.stream_end_produced || parser.error != yaml_NO_ERROR || parser.state == yaml_PARSE_END_STATE { + return true + } + + // Generate the next event. + return yaml_parser_state_machine(parser, event) +} + +// Set parser error. +func yaml_parser_set_parser_error(parser *yaml_parser_t, problem string, problem_mark yaml_mark_t) bool { + parser.error = yaml_PARSER_ERROR + parser.problem = problem + parser.problem_mark = problem_mark + return false +} + +func yaml_parser_set_parser_error_context(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string, problem_mark yaml_mark_t) bool { + parser.error = yaml_PARSER_ERROR + parser.context = context + parser.context_mark = context_mark + parser.problem = problem + parser.problem_mark = problem_mark + return false +} + +// State dispatcher. +func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool { + //trace("yaml_parser_state_machine", "state:", parser.state.String()) + + switch parser.state { + case yaml_PARSE_STREAM_START_STATE: + return yaml_parser_parse_stream_start(parser, event) + + case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: + return yaml_parser_parse_document_start(parser, event, true) + + case yaml_PARSE_DOCUMENT_START_STATE: + return yaml_parser_parse_document_start(parser, event, false) + + case yaml_PARSE_DOCUMENT_CONTENT_STATE: + return yaml_parser_parse_document_content(parser, event) + + case yaml_PARSE_DOCUMENT_END_STATE: + return yaml_parser_parse_document_end(parser, event) + + case yaml_PARSE_BLOCK_NODE_STATE: + return yaml_parser_parse_node(parser, event, true, false) + + case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: + return yaml_parser_parse_node(parser, event, true, true) + + case yaml_PARSE_FLOW_NODE_STATE: + return yaml_parser_parse_node(parser, event, false, false) + + case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: + return yaml_parser_parse_block_sequence_entry(parser, event, true) + + case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_block_sequence_entry(parser, event, false) + + case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_indentless_sequence_entry(parser, event) + + case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: + return yaml_parser_parse_block_mapping_key(parser, event, true) + + case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: + return yaml_parser_parse_block_mapping_key(parser, event, false) + + case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: + return yaml_parser_parse_block_mapping_value(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: + return yaml_parser_parse_flow_sequence_entry(parser, event, true) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_flow_sequence_entry(parser, event, false) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event) + + case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: + return yaml_parser_parse_flow_mapping_key(parser, event, true) + + case yaml_PARSE_FLOW_MAPPING_KEY_STATE: + return yaml_parser_parse_flow_mapping_key(parser, event, false) + + case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: + return yaml_parser_parse_flow_mapping_value(parser, event, false) + + case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: + return yaml_parser_parse_flow_mapping_value(parser, event, true) + + default: + panic("invalid parser state") + } + return false +} + +// Parse the production: +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// ************ +func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_STREAM_START_TOKEN { + return yaml_parser_set_parser_error(parser, "did not find expected <stream-start>", token.start_mark) + } + parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE + *event = yaml_event_t{ + typ: yaml_STREAM_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + encoding: token.encoding, + } + skip_token(parser) + return true +} + +// Parse the productions: +// implicit_document ::= block_node DOCUMENT-END* +// * +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// ************************* +func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { + + token := peek_token(parser) + if token == nil { + return false + } + + // Parse extra document end indicators. + if !implicit { + for token.typ == yaml_DOCUMENT_END_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } + + if implicit && token.typ != yaml_VERSION_DIRECTIVE_TOKEN && + token.typ != yaml_TAG_DIRECTIVE_TOKEN && + token.typ != yaml_DOCUMENT_START_TOKEN && + token.typ != yaml_STREAM_END_TOKEN { + // Parse an implicit document. + if !yaml_parser_process_directives(parser, nil, nil) { + return false + } + parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) + parser.state = yaml_PARSE_BLOCK_NODE_STATE + + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + } else if token.typ != yaml_STREAM_END_TOKEN { + // Parse an explicit document. + var version_directive *yaml_version_directive_t + var tag_directives []yaml_tag_directive_t + start_mark := token.start_mark + if !yaml_parser_process_directives(parser, &version_directive, &tag_directives) { + return false + } + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_DOCUMENT_START_TOKEN { + yaml_parser_set_parser_error(parser, + "did not find expected <document start>", token.start_mark) + return false + } + parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) + parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE + end_mark := token.end_mark + + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + version_directive: version_directive, + tag_directives: tag_directives, + implicit: false, + } + skip_token(parser) + + } else { + // Parse the stream end. + parser.state = yaml_PARSE_END_STATE + *event = yaml_event_t{ + typ: yaml_STREAM_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + } + + return true +} + +// Parse the productions: +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// *********** +// +func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN || + token.typ == yaml_TAG_DIRECTIVE_TOKEN || + token.typ == yaml_DOCUMENT_START_TOKEN || + token.typ == yaml_DOCUMENT_END_TOKEN || + token.typ == yaml_STREAM_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + return yaml_parser_process_empty_scalar(parser, event, + token.start_mark) + } + return yaml_parser_parse_node(parser, event, true, false) +} + +// Parse the productions: +// implicit_document ::= block_node DOCUMENT-END* +// ************* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// +func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + start_mark := token.start_mark + end_mark := token.start_mark + + implicit := true + if token.typ == yaml_DOCUMENT_END_TOKEN { + end_mark = token.end_mark + skip_token(parser) + implicit = false + } + + parser.tag_directives = parser.tag_directives[:0] + + parser.state = yaml_PARSE_DOCUMENT_START_STATE + *event = yaml_event_t{ + typ: yaml_DOCUMENT_END_EVENT, + start_mark: start_mark, + end_mark: end_mark, + implicit: implicit, + } + return true +} + +// Parse the productions: +// block_node_or_indentless_sequence ::= +// ALIAS +// ***** +// | properties (block_content | indentless_block_sequence)? +// ********** * +// | block_content | indentless_block_sequence +// * +// block_node ::= ALIAS +// ***** +// | properties block_content? +// ********** * +// | block_content +// * +// flow_node ::= ALIAS +// ***** +// | properties flow_content? +// ********** * +// | flow_content +// * +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// ************************* +// block_content ::= block_collection | flow_collection | SCALAR +// ****** +// flow_content ::= flow_collection | SCALAR +// ****** +func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { + //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_ALIAS_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + *event = yaml_event_t{ + typ: yaml_ALIAS_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + anchor: token.value, + } + skip_token(parser) + return true + } + + start_mark := token.start_mark + end_mark := token.start_mark + + var tag_token bool + var tag_handle, tag_suffix, anchor []byte + var tag_mark yaml_mark_t + if token.typ == yaml_ANCHOR_TOKEN { + anchor = token.value + start_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_TAG_TOKEN { + tag_token = true + tag_handle = token.value + tag_suffix = token.suffix + tag_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } else if token.typ == yaml_TAG_TOKEN { + tag_token = true + tag_handle = token.value + tag_suffix = token.suffix + start_mark = token.start_mark + tag_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_ANCHOR_TOKEN { + anchor = token.value + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } + + var tag []byte + if tag_token { + if len(tag_handle) == 0 { + tag = tag_suffix + tag_suffix = nil + } else { + for i := range parser.tag_directives { + if bytes.Equal(parser.tag_directives[i].handle, tag_handle) { + tag = append([]byte(nil), parser.tag_directives[i].prefix...) + tag = append(tag, tag_suffix...) + break + } + } + if len(tag) == 0 { + yaml_parser_set_parser_error_context(parser, + "while parsing a node", start_mark, + "found undefined tag handle", tag_mark) + return false + } + } + } + + implicit := len(tag) == 0 + if indentless_sequence && token.typ == yaml_BLOCK_ENTRY_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), + } + return true + } + if token.typ == yaml_SCALAR_TOKEN { + var plain_implicit, quoted_implicit bool + end_mark = token.end_mark + if (len(tag) == 0 && token.style == yaml_PLAIN_SCALAR_STYLE) || (len(tag) == 1 && tag[0] == '!') { + plain_implicit = true + } else if len(tag) == 0 { + quoted_implicit = true + } + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + value: token.value, + implicit: plain_implicit, + quoted_implicit: quoted_implicit, + style: yaml_style_t(token.style), + } + skip_token(parser) + return true + } + if token.typ == yaml_FLOW_SEQUENCE_START_TOKEN { + // [Go] Some of the events below can be merged as they differ only on style. + end_mark = token.end_mark + parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE), + } + return true + } + if token.typ == yaml_FLOW_MAPPING_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), + } + return true + } + if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), + } + return true + } + if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE), + } + return true + } + if len(anchor) > 0 || len(tag) > 0 { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + quoted_implicit: false, + style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), + } + return true + } + + context := "while parsing a flow node" + if block { + context = "while parsing a block node" + } + yaml_parser_set_parser_error_context(parser, context, start_mark, + "did not find expected node content", token.start_mark) + return false +} + +// Parse the productions: +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// ******************** *********** * ********* +// +func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_BLOCK_ENTRY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, true, false) + } else { + parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + } + if token.typ == yaml_BLOCK_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + skip_token(parser) + return true + } + + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a block collection", context_mark, + "did not find expected '-' indicator", token.start_mark) +} + +// Parse the productions: +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// *********** * +func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_BLOCK_ENTRY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_BLOCK_ENTRY_TOKEN && + token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, true, false) + } + parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.start_mark, // [Go] Shouldn't this be token.end_mark? + } + return true +} + +// Parse the productions: +// block_mapping ::= BLOCK-MAPPING_START +// ******************* +// ((KEY block_node_or_indentless_sequence?)? +// *** * +// (VALUE block_node_or_indentless_sequence?)?)* +// +// BLOCK-END +// ********* +// +func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_KEY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, true, true) + } else { + parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + } else if token.typ == yaml_BLOCK_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + return true + } + + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a block mapping", context_mark, + "did not find expected key", token.start_mark) +} + +// Parse the productions: +// block_mapping ::= BLOCK-MAPPING_START +// +// ((KEY block_node_or_indentless_sequence?)? +// +// (VALUE block_node_or_indentless_sequence?)?)* +// ***** * +// BLOCK-END +// +// +func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VALUE_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE) + return yaml_parser_parse_node(parser, event, true, true) + } + parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Parse the productions: +// flow_sequence ::= FLOW-SEQUENCE-START +// ******************* +// (flow_sequence_entry FLOW-ENTRY)* +// * ********** +// flow_sequence_entry? +// * +// FLOW-SEQUENCE-END +// ***************** +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * +// +func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + if !first { + if token.typ == yaml_FLOW_ENTRY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } else { + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a flow sequence", context_mark, + "did not find expected ',' or ']'", token.start_mark) + } + } + + if token.typ == yaml_KEY_TOKEN { + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + implicit: true, + style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), + } + skip_token(parser) + return true + } else if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + skip_token(parser) + return true +} + +// +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// *** * +// +func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_FLOW_ENTRY_TOKEN && + token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + mark := token.end_mark + skip_token(parser) + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) +} + +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// ***** * +// +func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VALUE_TOKEN { + skip_token(parser) + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * +// +func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.start_mark, // [Go] Shouldn't this be end_mark? + } + return true +} + +// Parse the productions: +// flow_mapping ::= FLOW-MAPPING-START +// ****************** +// (flow_mapping_entry FLOW-ENTRY)* +// * ********** +// flow_mapping_entry? +// ****************** +// FLOW-MAPPING-END +// **************** +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * *** * +// +func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ != yaml_FLOW_MAPPING_END_TOKEN { + if !first { + if token.typ == yaml_FLOW_ENTRY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } else { + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a flow mapping", context_mark, + "did not find expected ',' or '}'", token.start_mark) + } + } + + if token.typ == yaml_KEY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_FLOW_ENTRY_TOKEN && + token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } else { + parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) + } + } else if token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + return true +} + +// Parse the productions: +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * ***** * +// +func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { + token := peek_token(parser) + if token == nil { + return false + } + if empty { + parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) + } + if token.typ == yaml_VALUE_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Generate an empty scalar event. +func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t, mark yaml_mark_t) bool { + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: mark, + end_mark: mark, + value: nil, // Empty + implicit: true, + style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), + } + return true +} + +var default_tag_directives = []yaml_tag_directive_t{ + {[]byte("!"), []byte("!")}, + {[]byte("!!"), []byte("tag:yaml.org,2002:")}, +} + +// Parse directives. +func yaml_parser_process_directives(parser *yaml_parser_t, + version_directive_ref **yaml_version_directive_t, + tag_directives_ref *[]yaml_tag_directive_t) bool { + + var version_directive *yaml_version_directive_t + var tag_directives []yaml_tag_directive_t + + token := peek_token(parser) + if token == nil { + return false + } + + for token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN { + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN { + if version_directive != nil { + yaml_parser_set_parser_error(parser, + "found duplicate %YAML directive", token.start_mark) + return false + } + if token.major != 1 || token.minor != 1 { + yaml_parser_set_parser_error(parser, + "found incompatible YAML document", token.start_mark) + return false + } + version_directive = &yaml_version_directive_t{ + major: token.major, + minor: token.minor, + } + } else if token.typ == yaml_TAG_DIRECTIVE_TOKEN { + value := yaml_tag_directive_t{ + handle: token.value, + prefix: token.prefix, + } + if !yaml_parser_append_tag_directive(parser, value, false, token.start_mark) { + return false + } + tag_directives = append(tag_directives, value) + } + + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + + for i := range default_tag_directives { + if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) { + return false + } + } + + if version_directive_ref != nil { + *version_directive_ref = version_directive + } + if tag_directives_ref != nil { + *tag_directives_ref = tag_directives + } + return true +} + +// Append a tag directive to the directives stack. +func yaml_parser_append_tag_directive(parser *yaml_parser_t, value yaml_tag_directive_t, allow_duplicates bool, mark yaml_mark_t) bool { + for i := range parser.tag_directives { + if bytes.Equal(value.handle, parser.tag_directives[i].handle) { + if allow_duplicates { + return true + } + return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark) + } + } + + // [Go] I suspect the copy is unnecessary. This was likely done + // because there was no way to track ownership of the data. + value_copy := yaml_tag_directive_t{ + handle: make([]byte, len(value.handle)), + prefix: make([]byte, len(value.prefix)), + } + copy(value_copy.handle, value.handle) + copy(value_copy.prefix, value.prefix) + parser.tag_directives = append(parser.tag_directives, value_copy) + return true +} diff --git a/vendor/gopkg.in/yaml.v2/readerc.go b/vendor/gopkg.in/yaml.v2/readerc.go new file mode 100644 index 0000000000000000000000000000000000000000..f450791717bf2b80b70932fbd795051c8522952b --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/readerc.go @@ -0,0 +1,394 @@ +package yaml + +import ( + "io" +) + +// Set the reader error and return 0. +func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { + parser.error = yaml_READER_ERROR + parser.problem = problem + parser.problem_offset = offset + parser.problem_value = value + return false +} + +// Byte order marks. +const ( + bom_UTF8 = "\xef\xbb\xbf" + bom_UTF16LE = "\xff\xfe" + bom_UTF16BE = "\xfe\xff" +) + +// Determine the input stream encoding by checking the BOM symbol. If no BOM is +// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. +func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { + // Ensure that we had enough bytes in the raw buffer. + for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { + if !yaml_parser_update_raw_buffer(parser) { + return false + } + } + + // Determine the encoding. + buf := parser.raw_buffer + pos := parser.raw_buffer_pos + avail := len(buf) - pos + if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { + parser.encoding = yaml_UTF16LE_ENCODING + parser.raw_buffer_pos += 2 + parser.offset += 2 + } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { + parser.encoding = yaml_UTF16BE_ENCODING + parser.raw_buffer_pos += 2 + parser.offset += 2 + } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { + parser.encoding = yaml_UTF8_ENCODING + parser.raw_buffer_pos += 3 + parser.offset += 3 + } else { + parser.encoding = yaml_UTF8_ENCODING + } + return true +} + +// Update the raw buffer. +func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { + size_read := 0 + + // Return if the raw buffer is full. + if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { + return true + } + + // Return on EOF. + if parser.eof { + return true + } + + // Move the remaining bytes in the raw buffer to the beginning. + if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { + copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) + } + parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] + parser.raw_buffer_pos = 0 + + // Call the read handler to fill the buffer. + size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) + parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] + if err == io.EOF { + parser.eof = true + } else if err != nil { + return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) + } + return true +} + +// Ensure that the buffer contains at least `length` characters. +// Return true on success, false on failure. +// +// The length is supposed to be significantly less that the buffer size. +func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { + if parser.read_handler == nil { + panic("read handler must be set") + } + + // If the EOF flag is set and the raw buffer is empty, do nothing. + if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { + return true + } + + // Return if the buffer contains enough characters. + if parser.unread >= length { + return true + } + + // Determine the input encoding if it is not known yet. + if parser.encoding == yaml_ANY_ENCODING { + if !yaml_parser_determine_encoding(parser) { + return false + } + } + + // Move the unread characters to the beginning of the buffer. + buffer_len := len(parser.buffer) + if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { + copy(parser.buffer, parser.buffer[parser.buffer_pos:]) + buffer_len -= parser.buffer_pos + parser.buffer_pos = 0 + } else if parser.buffer_pos == buffer_len { + buffer_len = 0 + parser.buffer_pos = 0 + } + + // Open the whole buffer for writing, and cut it before returning. + parser.buffer = parser.buffer[:cap(parser.buffer)] + + // Fill the buffer until it has enough characters. + first := true + for parser.unread < length { + + // Fill the raw buffer if necessary. + if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { + if !yaml_parser_update_raw_buffer(parser) { + parser.buffer = parser.buffer[:buffer_len] + return false + } + } + first = false + + // Decode the raw buffer. + inner: + for parser.raw_buffer_pos != len(parser.raw_buffer) { + var value rune + var width int + + raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos + + // Decode the next character. + switch parser.encoding { + case yaml_UTF8_ENCODING: + // Decode a UTF-8 character. Check RFC 3629 + // (http://www.ietf.org/rfc/rfc3629.txt) for more details. + // + // The following table (taken from the RFC) is used for + // decoding. + // + // Char. number range | UTF-8 octet sequence + // (hexadecimal) | (binary) + // --------------------+------------------------------------ + // 0000 0000-0000 007F | 0xxxxxxx + // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + // + // Additionally, the characters in the range 0xD800-0xDFFF + // are prohibited as they are reserved for use with UTF-16 + // surrogate pairs. + + // Determine the length of the UTF-8 sequence. + octet := parser.raw_buffer[parser.raw_buffer_pos] + switch { + case octet&0x80 == 0x00: + width = 1 + case octet&0xE0 == 0xC0: + width = 2 + case octet&0xF0 == 0xE0: + width = 3 + case octet&0xF8 == 0xF0: + width = 4 + default: + // The leading octet is invalid. + return yaml_parser_set_reader_error(parser, + "invalid leading UTF-8 octet", + parser.offset, int(octet)) + } + + // Check if the raw buffer contains an incomplete character. + if width > raw_unread { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-8 octet sequence", + parser.offset, -1) + } + break inner + } + + // Decode the leading octet. + switch { + case octet&0x80 == 0x00: + value = rune(octet & 0x7F) + case octet&0xE0 == 0xC0: + value = rune(octet & 0x1F) + case octet&0xF0 == 0xE0: + value = rune(octet & 0x0F) + case octet&0xF8 == 0xF0: + value = rune(octet & 0x07) + default: + value = 0 + } + + // Check and decode the trailing octets. + for k := 1; k < width; k++ { + octet = parser.raw_buffer[parser.raw_buffer_pos+k] + + // Check if the octet is valid. + if (octet & 0xC0) != 0x80 { + return yaml_parser_set_reader_error(parser, + "invalid trailing UTF-8 octet", + parser.offset+k, int(octet)) + } + + // Decode the octet. + value = (value << 6) + rune(octet&0x3F) + } + + // Check the length of the sequence against the value. + switch { + case width == 1: + case width == 2 && value >= 0x80: + case width == 3 && value >= 0x800: + case width == 4 && value >= 0x10000: + default: + return yaml_parser_set_reader_error(parser, + "invalid length of a UTF-8 sequence", + parser.offset, -1) + } + + // Check the range of the value. + if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { + return yaml_parser_set_reader_error(parser, + "invalid Unicode character", + parser.offset, int(value)) + } + + case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: + var low, high int + if parser.encoding == yaml_UTF16LE_ENCODING { + low, high = 0, 1 + } else { + low, high = 1, 0 + } + + // The UTF-16 encoding is not as simple as one might + // naively think. Check RFC 2781 + // (http://www.ietf.org/rfc/rfc2781.txt). + // + // Normally, two subsequent bytes describe a Unicode + // character. However a special technique (called a + // surrogate pair) is used for specifying character + // values larger than 0xFFFF. + // + // A surrogate pair consists of two pseudo-characters: + // high surrogate area (0xD800-0xDBFF) + // low surrogate area (0xDC00-0xDFFF) + // + // The following formulas are used for decoding + // and encoding characters using surrogate pairs: + // + // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) + // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) + // W1 = 110110yyyyyyyyyy + // W2 = 110111xxxxxxxxxx + // + // where U is the character value, W1 is the high surrogate + // area, W2 is the low surrogate area. + + // Check for incomplete UTF-16 character. + if raw_unread < 2 { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-16 character", + parser.offset, -1) + } + break inner + } + + // Get the character. + value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + + (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) + + // Check for unexpected low surrogate area. + if value&0xFC00 == 0xDC00 { + return yaml_parser_set_reader_error(parser, + "unexpected low surrogate area", + parser.offset, int(value)) + } + + // Check for a high surrogate area. + if value&0xFC00 == 0xD800 { + width = 4 + + // Check for incomplete surrogate pair. + if raw_unread < 4 { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-16 surrogate pair", + parser.offset, -1) + } + break inner + } + + // Get the next character. + value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + + (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) + + // Check for a low surrogate area. + if value2&0xFC00 != 0xDC00 { + return yaml_parser_set_reader_error(parser, + "expected low surrogate area", + parser.offset+2, int(value2)) + } + + // Generate the value of the surrogate pair. + value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) + } else { + width = 2 + } + + default: + panic("impossible") + } + + // Check if the character is in the allowed range: + // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) + // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) + // | [#x10000-#x10FFFF] (32 bit) + switch { + case value == 0x09: + case value == 0x0A: + case value == 0x0D: + case value >= 0x20 && value <= 0x7E: + case value == 0x85: + case value >= 0xA0 && value <= 0xD7FF: + case value >= 0xE000 && value <= 0xFFFD: + case value >= 0x10000 && value <= 0x10FFFF: + default: + return yaml_parser_set_reader_error(parser, + "control characters are not allowed", + parser.offset, int(value)) + } + + // Move the raw pointers. + parser.raw_buffer_pos += width + parser.offset += width + + // Finally put the character into the buffer. + if value <= 0x7F { + // 0000 0000-0000 007F . 0xxxxxxx + parser.buffer[buffer_len+0] = byte(value) + buffer_len += 1 + } else if value <= 0x7FF { + // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) + parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) + buffer_len += 2 + } else if value <= 0xFFFF { + // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) + parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) + parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) + buffer_len += 3 + } else { + // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) + parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) + parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) + parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) + buffer_len += 4 + } + + parser.unread++ + } + + // On EOF, put NUL into the buffer and return. + if parser.eof { + parser.buffer[buffer_len] = 0 + buffer_len++ + parser.unread++ + break + } + } + parser.buffer = parser.buffer[:buffer_len] + return true +} diff --git a/vendor/gopkg.in/yaml.v2/resolve.go b/vendor/gopkg.in/yaml.v2/resolve.go new file mode 100644 index 0000000000000000000000000000000000000000..93a86327434b94e457b3871f7dc6ab6bccdb4630 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/resolve.go @@ -0,0 +1,203 @@ +package yaml + +import ( + "encoding/base64" + "math" + "strconv" + "strings" + "unicode/utf8" +) + +type resolveMapItem struct { + value interface{} + tag string +} + +var resolveTable = make([]byte, 256) +var resolveMap = make(map[string]resolveMapItem) + +func init() { + t := resolveTable + t[int('+')] = 'S' // Sign + t[int('-')] = 'S' + for _, c := range "0123456789" { + t[int(c)] = 'D' // Digit + } + for _, c := range "yYnNtTfFoO~" { + t[int(c)] = 'M' // In map + } + t[int('.')] = '.' // Float (potentially in map) + + var resolveMapList = []struct { + v interface{} + tag string + l []string + }{ + {true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}}, + {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}}, + {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}}, + {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}}, + {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}}, + {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}}, + {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}}, + {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}}, + {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}}, + {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}}, + {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}}, + {"<<", yaml_MERGE_TAG, []string{"<<"}}, + } + + m := resolveMap + for _, item := range resolveMapList { + for _, s := range item.l { + m[s] = resolveMapItem{item.v, item.tag} + } + } +} + +const longTagPrefix = "tag:yaml.org,2002:" + +func shortTag(tag string) string { + // TODO This can easily be made faster and produce less garbage. + if strings.HasPrefix(tag, longTagPrefix) { + return "!!" + tag[len(longTagPrefix):] + } + return tag +} + +func longTag(tag string) string { + if strings.HasPrefix(tag, "!!") { + return longTagPrefix + tag[2:] + } + return tag +} + +func resolvableTag(tag string) bool { + switch tag { + case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG: + return true + } + return false +} + +func resolve(tag string, in string) (rtag string, out interface{}) { + if !resolvableTag(tag) { + return tag, in + } + + defer func() { + switch tag { + case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG: + return + } + failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) + }() + + // Any data is accepted as a !!str or !!binary. + // Otherwise, the prefix is enough of a hint about what it might be. + hint := byte('N') + if in != "" { + hint = resolveTable[in[0]] + } + if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG { + // Handle things we can lookup in a map. + if item, ok := resolveMap[in]; ok { + return item.tag, item.value + } + + // Base 60 floats are a bad idea, were dropped in YAML 1.2, and + // are purposefully unsupported here. They're still quoted on + // the way out for compatibility with other parser, though. + + switch hint { + case 'M': + // We've already checked the map above. + + case '.': + // Not in the map, so maybe a normal float. + floatv, err := strconv.ParseFloat(in, 64) + if err == nil { + return yaml_FLOAT_TAG, floatv + } + + case 'D', 'S': + // Int, float, or timestamp. + plain := strings.Replace(in, "_", "", -1) + intv, err := strconv.ParseInt(plain, 0, 64) + if err == nil { + if intv == int64(int(intv)) { + return yaml_INT_TAG, int(intv) + } else { + return yaml_INT_TAG, intv + } + } + uintv, err := strconv.ParseUint(plain, 0, 64) + if err == nil { + return yaml_INT_TAG, uintv + } + floatv, err := strconv.ParseFloat(plain, 64) + if err == nil { + return yaml_FLOAT_TAG, floatv + } + if strings.HasPrefix(plain, "0b") { + intv, err := strconv.ParseInt(plain[2:], 2, 64) + if err == nil { + if intv == int64(int(intv)) { + return yaml_INT_TAG, int(intv) + } else { + return yaml_INT_TAG, intv + } + } + uintv, err := strconv.ParseUint(plain[2:], 2, 64) + if err == nil { + return yaml_INT_TAG, uintv + } + } else if strings.HasPrefix(plain, "-0b") { + intv, err := strconv.ParseInt(plain[3:], 2, 64) + if err == nil { + if intv == int64(int(intv)) { + return yaml_INT_TAG, -int(intv) + } else { + return yaml_INT_TAG, -intv + } + } + } + // XXX Handle timestamps here. + + default: + panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")") + } + } + if tag == yaml_BINARY_TAG { + return yaml_BINARY_TAG, in + } + if utf8.ValidString(in) { + return yaml_STR_TAG, in + } + return yaml_BINARY_TAG, encodeBase64(in) +} + +// encodeBase64 encodes s as base64 that is broken up into multiple lines +// as appropriate for the resulting length. +func encodeBase64(s string) string { + const lineLen = 70 + encLen := base64.StdEncoding.EncodedLen(len(s)) + lines := encLen/lineLen + 1 + buf := make([]byte, encLen*2+lines) + in := buf[0:encLen] + out := buf[encLen:] + base64.StdEncoding.Encode(in, []byte(s)) + k := 0 + for i := 0; i < len(in); i += lineLen { + j := i + lineLen + if j > len(in) { + j = len(in) + } + k += copy(out[k:], in[i:j]) + if lines > 1 { + out[k] = '\n' + k++ + } + } + return string(out[:k]) +} diff --git a/vendor/gopkg.in/yaml.v2/scannerc.go b/vendor/gopkg.in/yaml.v2/scannerc.go new file mode 100644 index 0000000000000000000000000000000000000000..25808000f28f7971a7bef97530fa5eec5181abed --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/scannerc.go @@ -0,0 +1,2710 @@ +package yaml + +import ( + "bytes" + "fmt" +) + +// Introduction +// ************ +// +// The following notes assume that you are familiar with the YAML specification +// (http://yaml.org/spec/cvs/current.html). We mostly follow it, although in +// some cases we are less restrictive that it requires. +// +// The process of transforming a YAML stream into a sequence of events is +// divided on two steps: Scanning and Parsing. +// +// The Scanner transforms the input stream into a sequence of tokens, while the +// parser transform the sequence of tokens produced by the Scanner into a +// sequence of parsing events. +// +// The Scanner is rather clever and complicated. The Parser, on the contrary, +// is a straightforward implementation of a recursive-descendant parser (or, +// LL(1) parser, as it is usually called). +// +// Actually there are two issues of Scanning that might be called "clever", the +// rest is quite straightforward. The issues are "block collection start" and +// "simple keys". Both issues are explained below in details. +// +// Here the Scanning step is explained and implemented. We start with the list +// of all the tokens produced by the Scanner together with short descriptions. +// +// Now, tokens: +// +// STREAM-START(encoding) # The stream start. +// STREAM-END # The stream end. +// VERSION-DIRECTIVE(major,minor) # The '%YAML' directive. +// TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive. +// DOCUMENT-START # '---' +// DOCUMENT-END # '...' +// BLOCK-SEQUENCE-START # Indentation increase denoting a block +// BLOCK-MAPPING-START # sequence or a block mapping. +// BLOCK-END # Indentation decrease. +// FLOW-SEQUENCE-START # '[' +// FLOW-SEQUENCE-END # ']' +// BLOCK-SEQUENCE-START # '{' +// BLOCK-SEQUENCE-END # '}' +// BLOCK-ENTRY # '-' +// FLOW-ENTRY # ',' +// KEY # '?' or nothing (simple keys). +// VALUE # ':' +// ALIAS(anchor) # '*anchor' +// ANCHOR(anchor) # '&anchor' +// TAG(handle,suffix) # '!handle!suffix' +// SCALAR(value,style) # A scalar. +// +// The following two tokens are "virtual" tokens denoting the beginning and the +// end of the stream: +// +// STREAM-START(encoding) +// STREAM-END +// +// We pass the information about the input stream encoding with the +// STREAM-START token. +// +// The next two tokens are responsible for tags: +// +// VERSION-DIRECTIVE(major,minor) +// TAG-DIRECTIVE(handle,prefix) +// +// Example: +// +// %YAML 1.1 +// %TAG ! !foo +// %TAG !yaml! tag:yaml.org,2002: +// --- +// +// The correspoding sequence of tokens: +// +// STREAM-START(utf-8) +// VERSION-DIRECTIVE(1,1) +// TAG-DIRECTIVE("!","!foo") +// TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:") +// DOCUMENT-START +// STREAM-END +// +// Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole +// line. +// +// The document start and end indicators are represented by: +// +// DOCUMENT-START +// DOCUMENT-END +// +// Note that if a YAML stream contains an implicit document (without '---' +// and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be +// produced. +// +// In the following examples, we present whole documents together with the +// produced tokens. +// +// 1. An implicit document: +// +// 'a scalar' +// +// Tokens: +// +// STREAM-START(utf-8) +// SCALAR("a scalar",single-quoted) +// STREAM-END +// +// 2. An explicit document: +// +// --- +// 'a scalar' +// ... +// +// Tokens: +// +// STREAM-START(utf-8) +// DOCUMENT-START +// SCALAR("a scalar",single-quoted) +// DOCUMENT-END +// STREAM-END +// +// 3. Several documents in a stream: +// +// 'a scalar' +// --- +// 'another scalar' +// --- +// 'yet another scalar' +// +// Tokens: +// +// STREAM-START(utf-8) +// SCALAR("a scalar",single-quoted) +// DOCUMENT-START +// SCALAR("another scalar",single-quoted) +// DOCUMENT-START +// SCALAR("yet another scalar",single-quoted) +// STREAM-END +// +// We have already introduced the SCALAR token above. The following tokens are +// used to describe aliases, anchors, tag, and scalars: +// +// ALIAS(anchor) +// ANCHOR(anchor) +// TAG(handle,suffix) +// SCALAR(value,style) +// +// The following series of examples illustrate the usage of these tokens: +// +// 1. A recursive sequence: +// +// &A [ *A ] +// +// Tokens: +// +// STREAM-START(utf-8) +// ANCHOR("A") +// FLOW-SEQUENCE-START +// ALIAS("A") +// FLOW-SEQUENCE-END +// STREAM-END +// +// 2. A tagged scalar: +// +// !!float "3.14" # A good approximation. +// +// Tokens: +// +// STREAM-START(utf-8) +// TAG("!!","float") +// SCALAR("3.14",double-quoted) +// STREAM-END +// +// 3. Various scalar styles: +// +// --- # Implicit empty plain scalars do not produce tokens. +// --- a plain scalar +// --- 'a single-quoted scalar' +// --- "a double-quoted scalar" +// --- |- +// a literal scalar +// --- >- +// a folded +// scalar +// +// Tokens: +// +// STREAM-START(utf-8) +// DOCUMENT-START +// DOCUMENT-START +// SCALAR("a plain scalar",plain) +// DOCUMENT-START +// SCALAR("a single-quoted scalar",single-quoted) +// DOCUMENT-START +// SCALAR("a double-quoted scalar",double-quoted) +// DOCUMENT-START +// SCALAR("a literal scalar",literal) +// DOCUMENT-START +// SCALAR("a folded scalar",folded) +// STREAM-END +// +// Now it's time to review collection-related tokens. We will start with +// flow collections: +// +// FLOW-SEQUENCE-START +// FLOW-SEQUENCE-END +// FLOW-MAPPING-START +// FLOW-MAPPING-END +// FLOW-ENTRY +// KEY +// VALUE +// +// The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and +// FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}' +// correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the +// indicators '?' and ':', which are used for denoting mapping keys and values, +// are represented by the KEY and VALUE tokens. +// +// The following examples show flow collections: +// +// 1. A flow sequence: +// +// [item 1, item 2, item 3] +// +// Tokens: +// +// STREAM-START(utf-8) +// FLOW-SEQUENCE-START +// SCALAR("item 1",plain) +// FLOW-ENTRY +// SCALAR("item 2",plain) +// FLOW-ENTRY +// SCALAR("item 3",plain) +// FLOW-SEQUENCE-END +// STREAM-END +// +// 2. A flow mapping: +// +// { +// a simple key: a value, # Note that the KEY token is produced. +// ? a complex key: another value, +// } +// +// Tokens: +// +// STREAM-START(utf-8) +// FLOW-MAPPING-START +// KEY +// SCALAR("a simple key",plain) +// VALUE +// SCALAR("a value",plain) +// FLOW-ENTRY +// KEY +// SCALAR("a complex key",plain) +// VALUE +// SCALAR("another value",plain) +// FLOW-ENTRY +// FLOW-MAPPING-END +// STREAM-END +// +// A simple key is a key which is not denoted by the '?' indicator. Note that +// the Scanner still produce the KEY token whenever it encounters a simple key. +// +// For scanning block collections, the following tokens are used (note that we +// repeat KEY and VALUE here): +// +// BLOCK-SEQUENCE-START +// BLOCK-MAPPING-START +// BLOCK-END +// BLOCK-ENTRY +// KEY +// VALUE +// +// The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation +// increase that precedes a block collection (cf. the INDENT token in Python). +// The token BLOCK-END denote indentation decrease that ends a block collection +// (cf. the DEDENT token in Python). However YAML has some syntax pecularities +// that makes detections of these tokens more complex. +// +// The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators +// '-', '?', and ':' correspondingly. +// +// The following examples show how the tokens BLOCK-SEQUENCE-START, +// BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner: +// +// 1. Block sequences: +// +// - item 1 +// - item 2 +// - +// - item 3.1 +// - item 3.2 +// - +// key 1: value 1 +// key 2: value 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-ENTRY +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 3.1",plain) +// BLOCK-ENTRY +// SCALAR("item 3.2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// 2. Block mappings: +// +// a simple key: a value # The KEY token is produced here. +// ? a complex key +// : another value +// a mapping: +// key 1: value 1 +// key 2: value 2 +// a sequence: +// - item 1 +// - item 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("a simple key",plain) +// VALUE +// SCALAR("a value",plain) +// KEY +// SCALAR("a complex key",plain) +// VALUE +// SCALAR("another value",plain) +// KEY +// SCALAR("a mapping",plain) +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// KEY +// SCALAR("a sequence",plain) +// VALUE +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// YAML does not always require to start a new block collection from a new +// line. If the current line contains only '-', '?', and ':' indicators, a new +// block collection may start at the current line. The following examples +// illustrate this case: +// +// 1. Collections in a sequence: +// +// - - item 1 +// - item 2 +// - key 1: value 1 +// key 2: value 2 +// - ? complex key +// : complex value +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("complex key") +// VALUE +// SCALAR("complex value") +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// 2. Collections in a mapping: +// +// ? a sequence +// : - item 1 +// - item 2 +// ? a mapping +// : key 1: value 1 +// key 2: value 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("a sequence",plain) +// VALUE +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// KEY +// SCALAR("a mapping",plain) +// VALUE +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// YAML also permits non-indented sequences if they are included into a block +// mapping. In this case, the token BLOCK-SEQUENCE-START is not produced: +// +// key: +// - item 1 # BLOCK-SEQUENCE-START is NOT produced here. +// - item 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("key",plain) +// VALUE +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// + +// Ensure that the buffer contains the required number of characters. +// Return true on success, false on failure (reader error or memory error). +func cache(parser *yaml_parser_t, length int) bool { + // [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B) + return parser.unread >= length || yaml_parser_update_buffer(parser, length) +} + +// Advance the buffer pointer. +func skip(parser *yaml_parser_t) { + parser.mark.index++ + parser.mark.column++ + parser.unread-- + parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) +} + +func skip_line(parser *yaml_parser_t) { + if is_crlf(parser.buffer, parser.buffer_pos) { + parser.mark.index += 2 + parser.mark.column = 0 + parser.mark.line++ + parser.unread -= 2 + parser.buffer_pos += 2 + } else if is_break(parser.buffer, parser.buffer_pos) { + parser.mark.index++ + parser.mark.column = 0 + parser.mark.line++ + parser.unread-- + parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) + } +} + +// Copy a character to a string buffer and advance pointers. +func read(parser *yaml_parser_t, s []byte) []byte { + w := width(parser.buffer[parser.buffer_pos]) + if w == 0 { + panic("invalid character sequence") + } + if len(s) == 0 { + s = make([]byte, 0, 32) + } + if w == 1 && len(s)+w <= cap(s) { + s = s[:len(s)+1] + s[len(s)-1] = parser.buffer[parser.buffer_pos] + parser.buffer_pos++ + } else { + s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...) + parser.buffer_pos += w + } + parser.mark.index++ + parser.mark.column++ + parser.unread-- + return s +} + +// Copy a line break character to a string buffer and advance pointers. +func read_line(parser *yaml_parser_t, s []byte) []byte { + buf := parser.buffer + pos := parser.buffer_pos + switch { + case buf[pos] == '\r' && buf[pos+1] == '\n': + // CR LF . LF + s = append(s, '\n') + parser.buffer_pos += 2 + parser.mark.index++ + parser.unread-- + case buf[pos] == '\r' || buf[pos] == '\n': + // CR|LF . LF + s = append(s, '\n') + parser.buffer_pos += 1 + case buf[pos] == '\xC2' && buf[pos+1] == '\x85': + // NEL . LF + s = append(s, '\n') + parser.buffer_pos += 2 + case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'): + // LS|PS . LS|PS + s = append(s, buf[parser.buffer_pos:pos+3]...) + parser.buffer_pos += 3 + default: + return s + } + parser.mark.index++ + parser.mark.column = 0 + parser.mark.line++ + parser.unread-- + return s +} + +// Get the next token. +func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool { + // Erase the token object. + *token = yaml_token_t{} // [Go] Is this necessary? + + // No tokens after STREAM-END or error. + if parser.stream_end_produced || parser.error != yaml_NO_ERROR { + return true + } + + // Ensure that the tokens queue contains enough tokens. + if !parser.token_available { + if !yaml_parser_fetch_more_tokens(parser) { + return false + } + } + + // Fetch the next token from the queue. + *token = parser.tokens[parser.tokens_head] + parser.tokens_head++ + parser.tokens_parsed++ + parser.token_available = false + + if token.typ == yaml_STREAM_END_TOKEN { + parser.stream_end_produced = true + } + return true +} + +// Set the scanner error and return false. +func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool { + parser.error = yaml_SCANNER_ERROR + parser.context = context + parser.context_mark = context_mark + parser.problem = problem + parser.problem_mark = parser.mark + return false +} + +func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool { + context := "while parsing a tag" + if directive { + context = "while parsing a %TAG directive" + } + return yaml_parser_set_scanner_error(parser, context, context_mark, "did not find URI escaped octet") +} + +func trace(args ...interface{}) func() { + pargs := append([]interface{}{"+++"}, args...) + fmt.Println(pargs...) + pargs = append([]interface{}{"---"}, args...) + return func() { fmt.Println(pargs...) } +} + +// Ensure that the tokens queue contains at least one token which can be +// returned to the Parser. +func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { + // While we need more tokens to fetch, do it. + for { + // Check if we really need to fetch more tokens. + need_more_tokens := false + + if parser.tokens_head == len(parser.tokens) { + // Queue is empty. + need_more_tokens = true + } else { + // Check if any potential simple key may occupy the head position. + if !yaml_parser_stale_simple_keys(parser) { + return false + } + + for i := range parser.simple_keys { + simple_key := &parser.simple_keys[i] + if simple_key.possible && simple_key.token_number == parser.tokens_parsed { + need_more_tokens = true + break + } + } + } + + // We are finished. + if !need_more_tokens { + break + } + // Fetch the next token. + if !yaml_parser_fetch_next_token(parser) { + return false + } + } + + parser.token_available = true + return true +} + +// The dispatcher for token fetchers. +func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { + // Ensure that the buffer is initialized. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check if we just started scanning. Fetch STREAM-START then. + if !parser.stream_start_produced { + return yaml_parser_fetch_stream_start(parser) + } + + // Eat whitespaces and comments until we reach the next token. + if !yaml_parser_scan_to_next_token(parser) { + return false + } + + // Remove obsolete potential simple keys. + if !yaml_parser_stale_simple_keys(parser) { + return false + } + + // Check the indentation level against the current column. + if !yaml_parser_unroll_indent(parser, parser.mark.column) { + return false + } + + // Ensure that the buffer contains at least 4 characters. 4 is the length + // of the longest indicators ('--- ' and '... '). + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + + // Is it the end of the stream? + if is_z(parser.buffer, parser.buffer_pos) { + return yaml_parser_fetch_stream_end(parser) + } + + // Is it a directive? + if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' { + return yaml_parser_fetch_directive(parser) + } + + buf := parser.buffer + pos := parser.buffer_pos + + // Is it the document start indicator? + if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) { + return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN) + } + + // Is it the document end indicator? + if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) { + return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN) + } + + // Is it the flow sequence start indicator? + if buf[pos] == '[' { + return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN) + } + + // Is it the flow mapping start indicator? + if parser.buffer[parser.buffer_pos] == '{' { + return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN) + } + + // Is it the flow sequence end indicator? + if parser.buffer[parser.buffer_pos] == ']' { + return yaml_parser_fetch_flow_collection_end(parser, + yaml_FLOW_SEQUENCE_END_TOKEN) + } + + // Is it the flow mapping end indicator? + if parser.buffer[parser.buffer_pos] == '}' { + return yaml_parser_fetch_flow_collection_end(parser, + yaml_FLOW_MAPPING_END_TOKEN) + } + + // Is it the flow entry indicator? + if parser.buffer[parser.buffer_pos] == ',' { + return yaml_parser_fetch_flow_entry(parser) + } + + // Is it the block entry indicator? + if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) { + return yaml_parser_fetch_block_entry(parser) + } + + // Is it the key indicator? + if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_key(parser) + } + + // Is it the value indicator? + if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_value(parser) + } + + // Is it an alias? + if parser.buffer[parser.buffer_pos] == '*' { + return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN) + } + + // Is it an anchor? + if parser.buffer[parser.buffer_pos] == '&' { + return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN) + } + + // Is it a tag? + if parser.buffer[parser.buffer_pos] == '!' { + return yaml_parser_fetch_tag(parser) + } + + // Is it a literal scalar? + if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 { + return yaml_parser_fetch_block_scalar(parser, true) + } + + // Is it a folded scalar? + if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 { + return yaml_parser_fetch_block_scalar(parser, false) + } + + // Is it a single-quoted scalar? + if parser.buffer[parser.buffer_pos] == '\'' { + return yaml_parser_fetch_flow_scalar(parser, true) + } + + // Is it a double-quoted scalar? + if parser.buffer[parser.buffer_pos] == '"' { + return yaml_parser_fetch_flow_scalar(parser, false) + } + + // Is it a plain scalar? + // + // A plain scalar may start with any non-blank characters except + // + // '-', '?', ':', ',', '[', ']', '{', '}', + // '#', '&', '*', '!', '|', '>', '\'', '\"', + // '%', '@', '`'. + // + // In the block context (and, for the '-' indicator, in the flow context + // too), it may also start with the characters + // + // '-', '?', ':' + // + // if it is followed by a non-space character. + // + // The last rule is more restrictive than the specification requires. + // [Go] Make this logic more reasonable. + //switch parser.buffer[parser.buffer_pos] { + //case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`': + //} + if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' || + parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || + parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || + parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' || + parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' || + parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' || + parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' || + parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' || + parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') || + (parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) || + (parser.flow_level == 0 && + (parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') && + !is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_plain_scalar(parser) + } + + // If we don't determine the token type so far, it is an error. + return yaml_parser_set_scanner_error(parser, + "while scanning for the next token", parser.mark, + "found character that cannot start any token") +} + +// Check the list of potential simple keys and remove the positions that +// cannot contain simple keys anymore. +func yaml_parser_stale_simple_keys(parser *yaml_parser_t) bool { + // Check for a potential simple key for each flow level. + for i := range parser.simple_keys { + simple_key := &parser.simple_keys[i] + + // The specification requires that a simple key + // + // - is limited to a single line, + // - is shorter than 1024 characters. + if simple_key.possible && (simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index) { + + // Check if the potential simple key to be removed is required. + if simple_key.required { + return yaml_parser_set_scanner_error(parser, + "while scanning a simple key", simple_key.mark, + "could not find expected ':'") + } + simple_key.possible = false + } + } + return true +} + +// Check if a simple key may start at the current position and add it if +// needed. +func yaml_parser_save_simple_key(parser *yaml_parser_t) bool { + // A simple key is required at the current position if the scanner is in + // the block context and the current column coincides with the indentation + // level. + + required := parser.flow_level == 0 && parser.indent == parser.mark.column + + // A simple key is required only when it is the first token in the current + // line. Therefore it is always allowed. But we add a check anyway. + if required && !parser.simple_key_allowed { + panic("should not happen") + } + + // + // If the current position may start a simple key, save it. + // + if parser.simple_key_allowed { + simple_key := yaml_simple_key_t{ + possible: true, + required: required, + token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), + } + simple_key.mark = parser.mark + + if !yaml_parser_remove_simple_key(parser) { + return false + } + parser.simple_keys[len(parser.simple_keys)-1] = simple_key + } + return true +} + +// Remove a potential simple key at the current flow level. +func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { + i := len(parser.simple_keys) - 1 + if parser.simple_keys[i].possible { + // If the key is required, it is an error. + if parser.simple_keys[i].required { + return yaml_parser_set_scanner_error(parser, + "while scanning a simple key", parser.simple_keys[i].mark, + "could not find expected ':'") + } + } + // Remove the key from the stack. + parser.simple_keys[i].possible = false + return true +} + +// Increase the flow level and resize the simple key list if needed. +func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { + // Reset the simple key on the next level. + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) + + // Increase the flow level. + parser.flow_level++ + return true +} + +// Decrease the flow level. +func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { + if parser.flow_level > 0 { + parser.flow_level-- + parser.simple_keys = parser.simple_keys[:len(parser.simple_keys)-1] + } + return true +} + +// Push the current indentation level to the stack and set the new level +// the current column is greater than the indentation level. In this case, +// append or insert the specified token into the token queue. +func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool { + // In the flow context, do nothing. + if parser.flow_level > 0 { + return true + } + + if parser.indent < column { + // Push the current indentation level to the stack and set the new + // indentation level. + parser.indents = append(parser.indents, parser.indent) + parser.indent = column + + // Create a token and insert it into the queue. + token := yaml_token_t{ + typ: typ, + start_mark: mark, + end_mark: mark, + } + if number > -1 { + number -= parser.tokens_parsed + } + yaml_insert_token(parser, number, &token) + } + return true +} + +// Pop indentation levels from the indents stack until the current level +// becomes less or equal to the column. For each indentation level, append +// the BLOCK-END token. +func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool { + // In the flow context, do nothing. + if parser.flow_level > 0 { + return true + } + + // Loop through the indentation levels in the stack. + for parser.indent > column { + // Create a token and append it to the queue. + token := yaml_token_t{ + typ: yaml_BLOCK_END_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + } + yaml_insert_token(parser, -1, &token) + + // Pop the indentation level. + parser.indent = parser.indents[len(parser.indents)-1] + parser.indents = parser.indents[:len(parser.indents)-1] + } + return true +} + +// Initialize the scanner and produce the STREAM-START token. +func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool { + + // Set the initial indentation. + parser.indent = -1 + + // Initialize the simple key stack. + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) + + // A simple key is allowed at the beginning of the stream. + parser.simple_key_allowed = true + + // We have started. + parser.stream_start_produced = true + + // Create the STREAM-START token and append it to the queue. + token := yaml_token_t{ + typ: yaml_STREAM_START_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + encoding: parser.encoding, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the STREAM-END token and shut down the scanner. +func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { + + // Force new line. + if parser.mark.column != 0 { + parser.mark.column = 0 + parser.mark.line++ + } + + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Create the STREAM-END token and append it to the queue. + token := yaml_token_t{ + typ: yaml_STREAM_END_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. +func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. + token := yaml_token_t{} + if !yaml_parser_scan_directive(parser, &token) { + return false + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the DOCUMENT-START or DOCUMENT-END token. +func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Consume the token. + start_mark := parser.mark + + skip(parser) + skip(parser) + skip(parser) + + end_mark := parser.mark + + // Create the DOCUMENT-START or DOCUMENT-END token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. +func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // The indicators '[' and '{' may start a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // Increase the flow level. + if !yaml_parser_increase_flow_level(parser) { + return false + } + + // A simple key may follow the indicators '[' and '{'. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. +func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // Reset any potential simple key on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Decrease the flow level. + if !yaml_parser_decrease_flow_level(parser) { + return false + } + + // No simple keys after the indicators ']' and '}'. + parser.simple_key_allowed = false + + // Consume the token. + + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-ENTRY token. +func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool { + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after ','. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-ENTRY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_FLOW_ENTRY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the BLOCK-ENTRY token. +func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool { + // Check if the scanner is in the block context. + if parser.flow_level == 0 { + // Check if we are allowed to start a new entry. + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "block sequence entries are not allowed in this context") + } + // Add the BLOCK-SEQUENCE-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) { + return false + } + } else { + // It is an error for the '-' indicator to occur in the flow context, + // but we let the Parser detect and report about it because the Parser + // is able to point to the context. + } + + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after '-'. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the BLOCK-ENTRY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_BLOCK_ENTRY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the KEY token. +func yaml_parser_fetch_key(parser *yaml_parser_t) bool { + + // In the block context, additional checks are required. + if parser.flow_level == 0 { + // Check if we are allowed to start a new key (not nessesary simple). + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "mapping keys are not allowed in this context") + } + // Add the BLOCK-MAPPING-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { + return false + } + } + + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after '?' in the block context. + parser.simple_key_allowed = parser.flow_level == 0 + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the KEY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_KEY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the VALUE token. +func yaml_parser_fetch_value(parser *yaml_parser_t) bool { + + simple_key := &parser.simple_keys[len(parser.simple_keys)-1] + + // Have we found a simple key? + if simple_key.possible { + // Create the KEY token and insert it into the queue. + token := yaml_token_t{ + typ: yaml_KEY_TOKEN, + start_mark: simple_key.mark, + end_mark: simple_key.mark, + } + yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token) + + // In the block context, we may need to add the BLOCK-MAPPING-START token. + if !yaml_parser_roll_indent(parser, simple_key.mark.column, + simple_key.token_number, + yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) { + return false + } + + // Remove the simple key. + simple_key.possible = false + + // A simple key cannot follow another simple key. + parser.simple_key_allowed = false + + } else { + // The ':' indicator follows a complex key. + + // In the block context, extra checks are required. + if parser.flow_level == 0 { + + // Check if we are allowed to start a complex value. + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "mapping values are not allowed in this context") + } + + // Add the BLOCK-MAPPING-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { + return false + } + } + + // Simple keys after ':' are allowed in the block context. + parser.simple_key_allowed = parser.flow_level == 0 + } + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the VALUE token and append it to the queue. + token := yaml_token_t{ + typ: yaml_VALUE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the ALIAS or ANCHOR token. +func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // An anchor or an alias could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow an anchor or an alias. + parser.simple_key_allowed = false + + // Create the ALIAS or ANCHOR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_anchor(parser, &token, typ) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the TAG token. +func yaml_parser_fetch_tag(parser *yaml_parser_t) bool { + // A tag could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a tag. + parser.simple_key_allowed = false + + // Create the TAG token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_tag(parser, &token) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. +func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool { + // Remove any potential simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // A simple key may follow a block scalar. + parser.simple_key_allowed = true + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_block_scalar(parser, &token, literal) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. +func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool { + // A plain scalar could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a flow scalar. + parser.simple_key_allowed = false + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_flow_scalar(parser, &token, single) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,plain) token. +func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool { + // A plain scalar could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a flow scalar. + parser.simple_key_allowed = false + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_plain_scalar(parser, &token) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Eat whitespaces and comments until the next token is found. +func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { + + // Until the next token is not found. + for { + // Allow the BOM mark to start a line. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) { + skip(parser) + } + + // Eat whitespaces. + // Tabs are allowed: + // - in the flow context + // - in the block context, but not at the beginning of the line or + // after '-', '?', or ':' (complex value). + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Eat a comment until a line break. + if parser.buffer[parser.buffer_pos] == '#' { + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // If it is a line break, eat it. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + + // In the block context, a new line may start a simple key. + if parser.flow_level == 0 { + parser.simple_key_allowed = true + } + } else { + break // We have found a token. + } + } + + return true +} + +// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// +func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { + // Eat '%'. + start_mark := parser.mark + skip(parser) + + // Scan the directive name. + var name []byte + if !yaml_parser_scan_directive_name(parser, start_mark, &name) { + return false + } + + // Is it a YAML directive? + if bytes.Equal(name, []byte("YAML")) { + // Scan the VERSION directive value. + var major, minor int8 + if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) { + return false + } + end_mark := parser.mark + + // Create a VERSION-DIRECTIVE token. + *token = yaml_token_t{ + typ: yaml_VERSION_DIRECTIVE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + major: major, + minor: minor, + } + + // Is it a TAG directive? + } else if bytes.Equal(name, []byte("TAG")) { + // Scan the TAG directive value. + var handle, prefix []byte + if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) { + return false + } + end_mark := parser.mark + + // Create a TAG-DIRECTIVE token. + *token = yaml_token_t{ + typ: yaml_TAG_DIRECTIVE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: handle, + prefix: prefix, + } + + // Unknown directive. + } else { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "found unknown directive name") + return false + } + + // Eat the rest of the line including any comments. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + if parser.buffer[parser.buffer_pos] == '#' { + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // Check if we are at the end of the line. + if !is_breakz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "did not find expected comment or line break") + return false + } + + // Eat a line break. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } + + return true +} + +// Scan the directive name. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^ +// +func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { + // Consume the directive name. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + var s []byte + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the name is empty. + if len(s) == 0 { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "could not find expected directive name") + return false + } + + // Check for an blank character after the name. + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "found unexpected non-alphabetical character") + return false + } + *name = s + return true +} + +// Scan the value of VERSION-DIRECTIVE. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^ +func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { + // Eat whitespaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Consume the major version number. + if !yaml_parser_scan_version_directive_number(parser, start_mark, major) { + return false + } + + // Eat '.'. + if parser.buffer[parser.buffer_pos] != '.' { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "did not find expected digit or '.' character") + } + + skip(parser) + + // Consume the minor version number. + if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) { + return false + } + return true +} + +const max_number_length = 2 + +// Scan the version number of VERSION-DIRECTIVE. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^ +// %YAML 1.1 # a comment \n +// ^ +func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { + + // Repeat while the next character is digit. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + var value, length int8 + for is_digit(parser.buffer, parser.buffer_pos) { + // Check if the number is too long. + length++ + if length > max_number_length { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "found extremely long version number") + } + value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos)) + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the number was present. + if length == 0 { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "did not find expected version number") + } + *number = value + return true +} + +// Scan the value of a TAG-DIRECTIVE token. +// +// Scope: +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// +func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { + var handle_value, prefix_value []byte + + // Eat whitespaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Scan a handle. + if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) { + return false + } + + // Expect a whitespace. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blank(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", + start_mark, "did not find expected whitespace") + return false + } + + // Eat whitespaces. + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Scan a prefix. + if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) { + return false + } + + // Expect a whitespace or line break. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", + start_mark, "did not find expected whitespace or line break") + return false + } + + *handle = handle_value + *prefix = prefix_value + return true +} + +func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool { + var s []byte + + // Eat the indicator character. + start_mark := parser.mark + skip(parser) + + // Consume the value. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + end_mark := parser.mark + + /* + * Check if length of the anchor is greater than 0 and it is followed by + * a whitespace character or one of the indicators: + * + * '?', ':', ',', ']', '}', '%', '@', '`'. + */ + + if len(s) == 0 || + !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' || + parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' || + parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || + parser.buffer[parser.buffer_pos] == '`') { + context := "while scanning an alias" + if typ == yaml_ANCHOR_TOKEN { + context = "while scanning an anchor" + } + yaml_parser_set_scanner_error(parser, context, start_mark, + "did not find expected alphabetic or numeric character") + return false + } + + // Create a token. + *token = yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + value: s, + } + + return true +} + +/* + * Scan a TAG token. + */ + +func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool { + var handle, suffix []byte + + start_mark := parser.mark + + // Check if the tag is in the canonical form. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + if parser.buffer[parser.buffer_pos+1] == '<' { + // Keep the handle as '' + + // Eat '!<' + skip(parser) + skip(parser) + + // Consume the tag value. + if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { + return false + } + + // Check for '>' and eat it. + if parser.buffer[parser.buffer_pos] != '>' { + yaml_parser_set_scanner_error(parser, "while scanning a tag", + start_mark, "did not find the expected '>'") + return false + } + + skip(parser) + } else { + // The tag has either the '!suffix' or the '!handle!suffix' form. + + // First, try to scan a handle. + if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) { + return false + } + + // Check if it is, indeed, handle. + if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' { + // Scan the suffix now. + if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { + return false + } + } else { + // It wasn't a handle after all. Scan the rest of the tag. + if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) { + return false + } + + // Set the handle to '!'. + handle = []byte{'!'} + + // A special case: the '!' tag. Set the handle to '' and the + // suffix to '!'. + if len(suffix) == 0 { + handle, suffix = suffix, handle + } + } + } + + // Check the character which ends the tag. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a tag", + start_mark, "did not find expected whitespace or line break") + return false + } + + end_mark := parser.mark + + // Create a token. + *token = yaml_token_t{ + typ: yaml_TAG_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: handle, + suffix: suffix, + } + return true +} + +// Scan a tag handle. +func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool { + // Check the initial '!' character. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.buffer[parser.buffer_pos] != '!' { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected '!'") + return false + } + + var s []byte + + // Copy the '!' character. + s = read(parser, s) + + // Copy all subsequent alphabetical and numerical characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the trailing character is '!' and copy it. + if parser.buffer[parser.buffer_pos] == '!' { + s = read(parser, s) + } else { + // It's either the '!' tag or not really a tag handle. If it's a %TAG + // directive, it's an error. If it's a tag token, it must be a part of URI. + if directive && !(s[0] == '!' && s[1] == 0) { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected '!'") + return false + } + } + + *handle = s + return true +} + +// Scan a tag. +func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { + //size_t length = head ? strlen((char *)head) : 0 + var s []byte + + // Copy the head if needed. + // + // Note that we don't copy the leading '!' character. + if len(head) > 1 { + s = append(s, head[1:]...) + } + + // Scan the tag. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // The set of characters that may appear in URI is as follows: + // + // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', + // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', + // '%'. + // [Go] Convert this into more reasonable logic. + for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' || + parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' || + parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' || + parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' || + parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' || + parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' || + parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' || + parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' || + parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' || + parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || + parser.buffer[parser.buffer_pos] == '%' { + // Check if it is a URI-escape sequence. + if parser.buffer[parser.buffer_pos] == '%' { + if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) { + return false + } + } else { + s = read(parser, s) + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the tag is non-empty. + if len(s) == 0 { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected tag URI") + return false + } + *uri = s + return true +} + +// Decode an URI-escape sequence corresponding to a single UTF-8 character. +func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool { + + // Decode the required number of characters. + w := 1024 + for w > 0 { + // Check for a URI-escaped octet. + if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { + return false + } + + if !(parser.buffer[parser.buffer_pos] == '%' && + is_hex(parser.buffer, parser.buffer_pos+1) && + is_hex(parser.buffer, parser.buffer_pos+2)) { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find URI escaped octet") + } + + // Get the octet. + octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2)) + + // If it is the leading octet, determine the length of the UTF-8 sequence. + if w == 1024 { + w = width(octet) + if w == 0 { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "found an incorrect leading UTF-8 octet") + } + } else { + // Check if the trailing octet is correct. + if octet&0xC0 != 0x80 { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "found an incorrect trailing UTF-8 octet") + } + } + + // Copy the octet and move the pointers. + *s = append(*s, octet) + skip(parser) + skip(parser) + skip(parser) + w-- + } + return true +} + +// Scan a block scalar. +func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool { + // Eat the indicator '|' or '>'. + start_mark := parser.mark + skip(parser) + + // Scan the additional block scalar indicators. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check for a chomping indicator. + var chomping, increment int + if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { + // Set the chomping method and eat the indicator. + if parser.buffer[parser.buffer_pos] == '+' { + chomping = +1 + } else { + chomping = -1 + } + skip(parser) + + // Check for an indentation indicator. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if is_digit(parser.buffer, parser.buffer_pos) { + // Check that the indentation is greater than 0. + if parser.buffer[parser.buffer_pos] == '0' { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found an indentation indicator equal to 0") + return false + } + + // Get the indentation level and eat the indicator. + increment = as_digit(parser.buffer, parser.buffer_pos) + skip(parser) + } + + } else if is_digit(parser.buffer, parser.buffer_pos) { + // Do the same as above, but in the opposite order. + + if parser.buffer[parser.buffer_pos] == '0' { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found an indentation indicator equal to 0") + return false + } + increment = as_digit(parser.buffer, parser.buffer_pos) + skip(parser) + + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { + if parser.buffer[parser.buffer_pos] == '+' { + chomping = +1 + } else { + chomping = -1 + } + skip(parser) + } + } + + // Eat whitespaces and comments to the end of the line. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + if parser.buffer[parser.buffer_pos] == '#' { + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // Check if we are at the end of the line. + if !is_breakz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "did not find expected comment or line break") + return false + } + + // Eat a line break. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } + + end_mark := parser.mark + + // Set the indentation level if it was specified. + var indent int + if increment > 0 { + if parser.indent >= 0 { + indent = parser.indent + increment + } else { + indent = increment + } + } + + // Scan the leading line breaks and determine the indentation level if needed. + var s, leading_break, trailing_breaks []byte + if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { + return false + } + + // Scan the block scalar content. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + var leading_blank, trailing_blank bool + for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) { + // We are at the beginning of a non-empty line. + + // Is it a trailing whitespace? + trailing_blank = is_blank(parser.buffer, parser.buffer_pos) + + // Check if we need to fold the leading line break. + if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' { + // Do we need to join the lines by space? + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } + } else { + s = append(s, leading_break...) + } + leading_break = leading_break[:0] + + // Append the remaining line breaks. + s = append(s, trailing_breaks...) + trailing_breaks = trailing_breaks[:0] + + // Is it a leading whitespace? + leading_blank = is_blank(parser.buffer, parser.buffer_pos) + + // Consume the current line. + for !is_breakz(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Consume the line break. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + leading_break = read_line(parser, leading_break) + + // Eat the following indentation spaces and line breaks. + if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { + return false + } + } + + // Chomp the tail. + if chomping != -1 { + s = append(s, leading_break...) + } + if chomping == 1 { + s = append(s, trailing_breaks...) + } + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_LITERAL_SCALAR_STYLE, + } + if !literal { + token.style = yaml_FOLDED_SCALAR_STYLE + } + return true +} + +// Scan indentation spaces and line breaks for a block scalar. Determine the +// indentation level if needed. +func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool { + *end_mark = parser.mark + + // Eat the indentation spaces and line breaks. + max_indent := 0 + for { + // Eat the indentation spaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + if parser.mark.column > max_indent { + max_indent = parser.mark.column + } + + // Check for a tab character messing the indentation. + if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) { + return yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found a tab character where an indentation space is expected") + } + + // Have we found a non-empty line? + if !is_break(parser.buffer, parser.buffer_pos) { + break + } + + // Consume the line break. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + // [Go] Should really be returning breaks instead. + *breaks = read_line(parser, *breaks) + *end_mark = parser.mark + } + + // Determine the indentation level if needed. + if *indent == 0 { + *indent = max_indent + if *indent < parser.indent+1 { + *indent = parser.indent + 1 + } + if *indent < 1 { + *indent = 1 + } + } + return true +} + +// Scan a quoted scalar. +func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool { + // Eat the left quote. + start_mark := parser.mark + skip(parser) + + // Consume the content of the quoted scalar. + var s, leading_break, trailing_breaks, whitespaces []byte + for { + // Check that there are no document indicators at the beginning of the line. + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + + if parser.mark.column == 0 && + ((parser.buffer[parser.buffer_pos+0] == '-' && + parser.buffer[parser.buffer_pos+1] == '-' && + parser.buffer[parser.buffer_pos+2] == '-') || + (parser.buffer[parser.buffer_pos+0] == '.' && + parser.buffer[parser.buffer_pos+1] == '.' && + parser.buffer[parser.buffer_pos+2] == '.')) && + is_blankz(parser.buffer, parser.buffer_pos+3) { + yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", + start_mark, "found unexpected document indicator") + return false + } + + // Check for EOF. + if is_z(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", + start_mark, "found unexpected end of stream") + return false + } + + // Consume non-blank characters. + leading_blanks := false + for !is_blankz(parser.buffer, parser.buffer_pos) { + if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' { + // Is is an escaped single quote. + s = append(s, '\'') + skip(parser) + skip(parser) + + } else if single && parser.buffer[parser.buffer_pos] == '\'' { + // It is a right single quote. + break + } else if !single && parser.buffer[parser.buffer_pos] == '"' { + // It is a right double quote. + break + + } else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) { + // It is an escaped line break. + if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { + return false + } + skip(parser) + skip_line(parser) + leading_blanks = true + break + + } else if !single && parser.buffer[parser.buffer_pos] == '\\' { + // It is an escape sequence. + code_length := 0 + + // Check the escape character. + switch parser.buffer[parser.buffer_pos+1] { + case '0': + s = append(s, 0) + case 'a': + s = append(s, '\x07') + case 'b': + s = append(s, '\x08') + case 't', '\t': + s = append(s, '\x09') + case 'n': + s = append(s, '\x0A') + case 'v': + s = append(s, '\x0B') + case 'f': + s = append(s, '\x0C') + case 'r': + s = append(s, '\x0D') + case 'e': + s = append(s, '\x1B') + case ' ': + s = append(s, '\x20') + case '"': + s = append(s, '"') + case '\'': + s = append(s, '\'') + case '\\': + s = append(s, '\\') + case 'N': // NEL (#x85) + s = append(s, '\xC2') + s = append(s, '\x85') + case '_': // #xA0 + s = append(s, '\xC2') + s = append(s, '\xA0') + case 'L': // LS (#x2028) + s = append(s, '\xE2') + s = append(s, '\x80') + s = append(s, '\xA8') + case 'P': // PS (#x2029) + s = append(s, '\xE2') + s = append(s, '\x80') + s = append(s, '\xA9') + case 'x': + code_length = 2 + case 'u': + code_length = 4 + case 'U': + code_length = 8 + default: + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "found unknown escape character") + return false + } + + skip(parser) + skip(parser) + + // Consume an arbitrary escape code. + if code_length > 0 { + var value int + + // Scan the character value. + if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) { + return false + } + for k := 0; k < code_length; k++ { + if !is_hex(parser.buffer, parser.buffer_pos+k) { + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "did not find expected hexdecimal number") + return false + } + value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k) + } + + // Check the value and write the character. + if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF { + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "found invalid Unicode character escape code") + return false + } + if value <= 0x7F { + s = append(s, byte(value)) + } else if value <= 0x7FF { + s = append(s, byte(0xC0+(value>>6))) + s = append(s, byte(0x80+(value&0x3F))) + } else if value <= 0xFFFF { + s = append(s, byte(0xE0+(value>>12))) + s = append(s, byte(0x80+((value>>6)&0x3F))) + s = append(s, byte(0x80+(value&0x3F))) + } else { + s = append(s, byte(0xF0+(value>>18))) + s = append(s, byte(0x80+((value>>12)&0x3F))) + s = append(s, byte(0x80+((value>>6)&0x3F))) + s = append(s, byte(0x80+(value&0x3F))) + } + + // Advance the pointer. + for k := 0; k < code_length; k++ { + skip(parser) + } + } + } else { + // It is a non-escaped non-blank character. + s = read(parser, s) + } + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + } + + // Check if we are at the end of the scalar. + if single { + if parser.buffer[parser.buffer_pos] == '\'' { + break + } + } else { + if parser.buffer[parser.buffer_pos] == '"' { + break + } + } + + // Consume blank characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { + if is_blank(parser.buffer, parser.buffer_pos) { + // Consume a space or a tab character. + if !leading_blanks { + whitespaces = read(parser, whitespaces) + } else { + skip(parser) + } + } else { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + // Check if it is a first line break. + if !leading_blanks { + whitespaces = whitespaces[:0] + leading_break = read_line(parser, leading_break) + leading_blanks = true + } else { + trailing_breaks = read_line(parser, trailing_breaks) + } + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Join the whitespaces or fold line breaks. + if leading_blanks { + // Do we need to fold line breaks? + if len(leading_break) > 0 && leading_break[0] == '\n' { + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } else { + s = append(s, trailing_breaks...) + } + } else { + s = append(s, leading_break...) + s = append(s, trailing_breaks...) + } + trailing_breaks = trailing_breaks[:0] + leading_break = leading_break[:0] + } else { + s = append(s, whitespaces...) + whitespaces = whitespaces[:0] + } + } + + // Eat the right quote. + skip(parser) + end_mark := parser.mark + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_SINGLE_QUOTED_SCALAR_STYLE, + } + if !single { + token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + return true +} + +// Scan a plain scalar. +func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool { + + var s, leading_break, trailing_breaks, whitespaces []byte + var leading_blanks bool + var indent = parser.indent + 1 + + start_mark := parser.mark + end_mark := parser.mark + + // Consume the content of the plain scalar. + for { + // Check for a document indicator. + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + if parser.mark.column == 0 && + ((parser.buffer[parser.buffer_pos+0] == '-' && + parser.buffer[parser.buffer_pos+1] == '-' && + parser.buffer[parser.buffer_pos+2] == '-') || + (parser.buffer[parser.buffer_pos+0] == '.' && + parser.buffer[parser.buffer_pos+1] == '.' && + parser.buffer[parser.buffer_pos+2] == '.')) && + is_blankz(parser.buffer, parser.buffer_pos+3) { + break + } + + // Check for a comment. + if parser.buffer[parser.buffer_pos] == '#' { + break + } + + // Consume non-blank characters. + for !is_blankz(parser.buffer, parser.buffer_pos) { + + // Check for 'x:x' in the flow context. TODO: Fix the test "spec-08-13". + if parser.flow_level > 0 && + parser.buffer[parser.buffer_pos] == ':' && + !is_blankz(parser.buffer, parser.buffer_pos+1) { + yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", + start_mark, "found unexpected ':'") + return false + } + + // Check for indicators that may end a plain scalar. + if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) || + (parser.flow_level > 0 && + (parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == ':' || + parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || + parser.buffer[parser.buffer_pos] == '}')) { + break + } + + // Check if we need to join whitespaces and breaks. + if leading_blanks || len(whitespaces) > 0 { + if leading_blanks { + // Do we need to fold line breaks? + if leading_break[0] == '\n' { + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } else { + s = append(s, trailing_breaks...) + } + } else { + s = append(s, leading_break...) + s = append(s, trailing_breaks...) + } + trailing_breaks = trailing_breaks[:0] + leading_break = leading_break[:0] + leading_blanks = false + } else { + s = append(s, whitespaces...) + whitespaces = whitespaces[:0] + } + } + + // Copy the character. + s = read(parser, s) + + end_mark = parser.mark + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + } + + // Is it the end? + if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) { + break + } + + // Consume blank characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { + if is_blank(parser.buffer, parser.buffer_pos) { + + // Check for tab character that abuse indentation. + if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", + start_mark, "found a tab character that violate indentation") + return false + } + + // Consume a space or a tab character. + if !leading_blanks { + whitespaces = read(parser, whitespaces) + } else { + skip(parser) + } + } else { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + // Check if it is a first line break. + if !leading_blanks { + whitespaces = whitespaces[:0] + leading_break = read_line(parser, leading_break) + leading_blanks = true + } else { + trailing_breaks = read_line(parser, trailing_breaks) + } + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check indentation level. + if parser.flow_level == 0 && parser.mark.column < indent { + break + } + } + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_PLAIN_SCALAR_STYLE, + } + + // Note that we change the 'simple_key_allowed' flag. + if leading_blanks { + parser.simple_key_allowed = true + } + return true +} diff --git a/vendor/gopkg.in/yaml.v2/sorter.go b/vendor/gopkg.in/yaml.v2/sorter.go new file mode 100644 index 0000000000000000000000000000000000000000..5958822f9c6bbf74a52caf43925cb65adfe1a6e8 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/sorter.go @@ -0,0 +1,104 @@ +package yaml + +import ( + "reflect" + "unicode" +) + +type keyList []reflect.Value + +func (l keyList) Len() int { return len(l) } +func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l keyList) Less(i, j int) bool { + a := l[i] + b := l[j] + ak := a.Kind() + bk := b.Kind() + for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { + a = a.Elem() + ak = a.Kind() + } + for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { + b = b.Elem() + bk = b.Kind() + } + af, aok := keyFloat(a) + bf, bok := keyFloat(b) + if aok && bok { + if af != bf { + return af < bf + } + if ak != bk { + return ak < bk + } + return numLess(a, b) + } + if ak != reflect.String || bk != reflect.String { + return ak < bk + } + ar, br := []rune(a.String()), []rune(b.String()) + for i := 0; i < len(ar) && i < len(br); i++ { + if ar[i] == br[i] { + continue + } + al := unicode.IsLetter(ar[i]) + bl := unicode.IsLetter(br[i]) + if al && bl { + return ar[i] < br[i] + } + if al || bl { + return bl + } + var ai, bi int + var an, bn int64 + for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { + an = an*10 + int64(ar[ai]-'0') + } + for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { + bn = bn*10 + int64(br[bi]-'0') + } + if an != bn { + return an < bn + } + if ai != bi { + return ai < bi + } + return ar[i] < br[i] + } + return len(ar) < len(br) +} + +// keyFloat returns a float value for v if it is a number/bool +// and whether it is a number/bool or not. +func keyFloat(v reflect.Value) (f float64, ok bool) { + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return float64(v.Int()), true + case reflect.Float32, reflect.Float64: + return v.Float(), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return float64(v.Uint()), true + case reflect.Bool: + if v.Bool() { + return 1, true + } + return 0, true + } + return 0, false +} + +// numLess returns whether a < b. +// a and b must necessarily have the same kind. +func numLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return a.Int() < b.Int() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Bool: + return !a.Bool() && b.Bool() + } + panic("not a number") +} diff --git a/vendor/gopkg.in/yaml.v2/suite_test.go b/vendor/gopkg.in/yaml.v2/suite_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c5cf1ed4f6e6321e2f8398a9960a1c7abf2243e2 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/suite_test.go @@ -0,0 +1,12 @@ +package yaml_test + +import ( + . "gopkg.in/check.v1" + "testing" +) + +func Test(t *testing.T) { TestingT(t) } + +type S struct{} + +var _ = Suite(&S{}) diff --git a/vendor/gopkg.in/yaml.v2/writerc.go b/vendor/gopkg.in/yaml.v2/writerc.go new file mode 100644 index 0000000000000000000000000000000000000000..190362f25dfb9f6a6b56cf0ba873277e80e69ed9 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/writerc.go @@ -0,0 +1,89 @@ +package yaml + +// Set the writer error and return false. +func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_WRITER_ERROR + emitter.problem = problem + return false +} + +// Flush the output buffer. +func yaml_emitter_flush(emitter *yaml_emitter_t) bool { + if emitter.write_handler == nil { + panic("write handler not set") + } + + // Check if the buffer is empty. + if emitter.buffer_pos == 0 { + return true + } + + // If the output encoding is UTF-8, we don't need to recode the buffer. + if emitter.encoding == yaml_UTF8_ENCODING { + if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { + return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) + } + emitter.buffer_pos = 0 + return true + } + + // Recode the buffer into the raw buffer. + var low, high int + if emitter.encoding == yaml_UTF16LE_ENCODING { + low, high = 0, 1 + } else { + high, low = 1, 0 + } + + pos := 0 + for pos < emitter.buffer_pos { + // See the "reader.c" code for more details on UTF-8 encoding. Note + // that we assume that the buffer contains a valid UTF-8 sequence. + + // Read the next UTF-8 character. + octet := emitter.buffer[pos] + + var w int + var value rune + switch { + case octet&0x80 == 0x00: + w, value = 1, rune(octet&0x7F) + case octet&0xE0 == 0xC0: + w, value = 2, rune(octet&0x1F) + case octet&0xF0 == 0xE0: + w, value = 3, rune(octet&0x0F) + case octet&0xF8 == 0xF0: + w, value = 4, rune(octet&0x07) + } + for k := 1; k < w; k++ { + octet = emitter.buffer[pos+k] + value = (value << 6) + (rune(octet) & 0x3F) + } + pos += w + + // Write the character. + if value < 0x10000 { + var b [2]byte + b[high] = byte(value >> 8) + b[low] = byte(value & 0xFF) + emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1]) + } else { + // Write the character using a surrogate pair (check "reader.c"). + var b [4]byte + value -= 0x10000 + b[high] = byte(0xD8 + (value >> 18)) + b[low] = byte((value >> 10) & 0xFF) + b[high+2] = byte(0xDC + ((value >> 8) & 0xFF)) + b[low+2] = byte(value & 0xFF) + emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3]) + } + } + + // Write the raw buffer. + if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil { + return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) + } + emitter.buffer_pos = 0 + emitter.raw_buffer = emitter.raw_buffer[:0] + return true +} diff --git a/vendor/gopkg.in/yaml.v2/yaml.go b/vendor/gopkg.in/yaml.v2/yaml.go new file mode 100644 index 0000000000000000000000000000000000000000..36d6b883a6c0ba97ce3f7fe5d1c5c50ed345d390 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/yaml.go @@ -0,0 +1,346 @@ +// Package yaml implements YAML support for the Go language. +// +// Source code and other details for the project are available at GitHub: +// +// https://github.com/go-yaml/yaml +// +package yaml + +import ( + "errors" + "fmt" + "reflect" + "strings" + "sync" +) + +// MapSlice encodes and decodes as a YAML map. +// The order of keys is preserved when encoding and decoding. +type MapSlice []MapItem + +// MapItem is an item in a MapSlice. +type MapItem struct { + Key, Value interface{} +} + +// The Unmarshaler interface may be implemented by types to customize their +// behavior when being unmarshaled from a YAML document. The UnmarshalYAML +// method receives a function that may be called to unmarshal the original +// YAML value into a field or variable. It is safe to call the unmarshal +// function parameter more than once if necessary. +type Unmarshaler interface { + UnmarshalYAML(unmarshal func(interface{}) error) error +} + +// The Marshaler interface may be implemented by types to customize their +// behavior when being marshaled into a YAML document. The returned value +// is marshaled in place of the original value implementing Marshaler. +// +// If an error is returned by MarshalYAML, the marshaling procedure stops +// and returns with the provided error. +type Marshaler interface { + MarshalYAML() (interface{}, error) +} + +// Unmarshal decodes the first document found within the in byte slice +// and assigns decoded values into the out value. +// +// Maps and pointers (to a struct, string, int, etc) are accepted as out +// values. If an internal pointer within a struct is not initialized, +// the yaml package will initialize it if necessary for unmarshalling +// the provided data. The out parameter must not be nil. +// +// The type of the decoded values should be compatible with the respective +// values in out. If one or more values cannot be decoded due to a type +// mismatches, decoding continues partially until the end of the YAML +// content, and a *yaml.TypeError is returned with details for all +// missed values. +// +// Struct fields are only unmarshalled if they are exported (have an +// upper case first letter), and are unmarshalled using the field name +// lowercased as the default key. Custom keys may be defined via the +// "yaml" name in the field tag: the content preceding the first comma +// is used as the key, and the following comma-separated options are +// used to tweak the marshalling process (see Marshal). +// Conflicting names result in a runtime error. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// var t T +// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) +// +// See the documentation of Marshal for the format of tags and a list of +// supported tag options. +// +func Unmarshal(in []byte, out interface{}) (err error) { + defer handleErr(&err) + d := newDecoder() + p := newParser(in) + defer p.destroy() + node := p.parse() + if node != nil { + v := reflect.ValueOf(out) + if v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + d.unmarshal(node, v) + } + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +// Marshal serializes the value provided into a YAML document. The structure +// of the generated document will reflect the structure of the value itself. +// Maps and pointers (to struct, string, int, etc) are accepted as the in value. +// +// Struct fields are only unmarshalled if they are exported (have an upper case +// first letter), and are unmarshalled using the field name lowercased as the +// default key. Custom keys may be defined via the "yaml" name in the field +// tag: the content preceding the first comma is used as the key, and the +// following comma-separated options are used to tweak the marshalling process. +// Conflicting names result in a runtime error. +// +// The field tag format accepted is: +// +// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)` +// +// The following flags are currently supported: +// +// omitempty Only include the field if it's not set to the zero +// value for the type or to empty slices or maps. +// Does not apply to zero valued structs. +// +// flow Marshal using a flow style (useful for structs, +// sequences and maps). +// +// inline Inline the field, which must be a struct or a map, +// causing all of its fields or keys to be processed as if +// they were part of the outer struct. For maps, keys must +// not conflict with the yaml keys of other struct fields. +// +// In addition, if the key is "-", the field is ignored. +// +// For example: +// +// type T struct { +// F int "a,omitempty" +// B int +// } +// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" +// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" +// +func Marshal(in interface{}) (out []byte, err error) { + defer handleErr(&err) + e := newEncoder() + defer e.destroy() + e.marshal("", reflect.ValueOf(in)) + e.finish() + out = e.out + return +} + +func handleErr(err *error) { + if v := recover(); v != nil { + if e, ok := v.(yamlError); ok { + *err = e.err + } else { + panic(v) + } + } +} + +type yamlError struct { + err error +} + +func fail(err error) { + panic(yamlError{err}) +} + +func failf(format string, args ...interface{}) { + panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) +} + +// A TypeError is returned by Unmarshal when one or more fields in +// the YAML document cannot be properly decoded into the requested +// types. When this error is returned, the value is still +// unmarshaled partially. +type TypeError struct { + Errors []string +} + +func (e *TypeError) Error() string { + return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) +} + +// -------------------------------------------------------------------------- +// Maintain a mapping of keys to structure field indexes + +// The code in this section was copied from mgo/bson. + +// structInfo holds details for the serialization of fields of +// a given struct. +type structInfo struct { + FieldsMap map[string]fieldInfo + FieldsList []fieldInfo + + // InlineMap is the number of the field in the struct that + // contains an ,inline map, or -1 if there's none. + InlineMap int +} + +type fieldInfo struct { + Key string + Num int + OmitEmpty bool + Flow bool + + // Inline holds the field index if the field is part of an inlined struct. + Inline []int +} + +var structMap = make(map[reflect.Type]*structInfo) +var fieldMapMutex sync.RWMutex + +func getStructInfo(st reflect.Type) (*structInfo, error) { + fieldMapMutex.RLock() + sinfo, found := structMap[st] + fieldMapMutex.RUnlock() + if found { + return sinfo, nil + } + + n := st.NumField() + fieldsMap := make(map[string]fieldInfo) + fieldsList := make([]fieldInfo, 0, n) + inlineMap := -1 + for i := 0; i != n; i++ { + field := st.Field(i) + if field.PkgPath != "" && !field.Anonymous { + continue // Private field + } + + info := fieldInfo{Num: i} + + tag := field.Tag.Get("yaml") + if tag == "" && strings.Index(string(field.Tag), ":") < 0 { + tag = string(field.Tag) + } + if tag == "-" { + continue + } + + inline := false + fields := strings.Split(tag, ",") + if len(fields) > 1 { + for _, flag := range fields[1:] { + switch flag { + case "omitempty": + info.OmitEmpty = true + case "flow": + info.Flow = true + case "inline": + inline = true + default: + return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)) + } + } + tag = fields[0] + } + + if inline { + switch field.Type.Kind() { + case reflect.Map: + if inlineMap >= 0 { + return nil, errors.New("Multiple ,inline maps in struct " + st.String()) + } + if field.Type.Key() != reflect.TypeOf("") { + return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) + } + inlineMap = info.Num + case reflect.Struct: + sinfo, err := getStructInfo(field.Type) + if err != nil { + return nil, err + } + for _, finfo := range sinfo.FieldsList { + if _, found := fieldsMap[finfo.Key]; found { + msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + if finfo.Inline == nil { + finfo.Inline = []int{i, finfo.Num} + } else { + finfo.Inline = append([]int{i}, finfo.Inline...) + } + fieldsMap[finfo.Key] = finfo + fieldsList = append(fieldsList, finfo) + } + default: + //return nil, errors.New("Option ,inline needs a struct value or map field") + return nil, errors.New("Option ,inline needs a struct value field") + } + continue + } + + if tag != "" { + info.Key = tag + } else { + info.Key = strings.ToLower(field.Name) + } + + if _, found = fieldsMap[info.Key]; found { + msg := "Duplicated key '" + info.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + + fieldsList = append(fieldsList, info) + fieldsMap[info.Key] = info + } + + sinfo = &structInfo{fieldsMap, fieldsList, inlineMap} + + fieldMapMutex.Lock() + structMap[st] = sinfo + fieldMapMutex.Unlock() + return sinfo, nil +} + +func isZero(v reflect.Value) bool { + switch v.Kind() { + case reflect.String: + return len(v.String()) == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Slice: + return v.Len() == 0 + case reflect.Map: + return v.Len() == 0 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Struct: + vt := v.Type() + for i := v.NumField() - 1; i >= 0; i-- { + if vt.Field(i).PkgPath != "" { + continue // Private field + } + if !isZero(v.Field(i)) { + return false + } + } + return true + } + return false +} diff --git a/vendor/gopkg.in/yaml.v2/yamlh.go b/vendor/gopkg.in/yaml.v2/yamlh.go new file mode 100644 index 0000000000000000000000000000000000000000..d60a6b6b0035bec3773f023680b38cd3dbec93d0 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/yamlh.go @@ -0,0 +1,716 @@ +package yaml + +import ( + "io" +) + +// The version directive data. +type yaml_version_directive_t struct { + major int8 // The major version number. + minor int8 // The minor version number. +} + +// The tag directive data. +type yaml_tag_directive_t struct { + handle []byte // The tag handle. + prefix []byte // The tag prefix. +} + +type yaml_encoding_t int + +// The stream encoding. +const ( + // Let the parser choose the encoding. + yaml_ANY_ENCODING yaml_encoding_t = iota + + yaml_UTF8_ENCODING // The default UTF-8 encoding. + yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. + yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. +) + +type yaml_break_t int + +// Line break types. +const ( + // Let the parser choose the break type. + yaml_ANY_BREAK yaml_break_t = iota + + yaml_CR_BREAK // Use CR for line breaks (Mac style). + yaml_LN_BREAK // Use LN for line breaks (Unix style). + yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). +) + +type yaml_error_type_t int + +// Many bad things could happen with the parser and emitter. +const ( + // No error is produced. + yaml_NO_ERROR yaml_error_type_t = iota + + yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. + yaml_READER_ERROR // Cannot read or decode the input stream. + yaml_SCANNER_ERROR // Cannot scan the input stream. + yaml_PARSER_ERROR // Cannot parse the input stream. + yaml_COMPOSER_ERROR // Cannot compose a YAML document. + yaml_WRITER_ERROR // Cannot write to the output stream. + yaml_EMITTER_ERROR // Cannot emit a YAML stream. +) + +// The pointer position. +type yaml_mark_t struct { + index int // The position index. + line int // The position line. + column int // The position column. +} + +// Node Styles + +type yaml_style_t int8 + +type yaml_scalar_style_t yaml_style_t + +// Scalar styles. +const ( + // Let the emitter choose the style. + yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota + + yaml_PLAIN_SCALAR_STYLE // The plain scalar style. + yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. + yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. + yaml_LITERAL_SCALAR_STYLE // The literal scalar style. + yaml_FOLDED_SCALAR_STYLE // The folded scalar style. +) + +type yaml_sequence_style_t yaml_style_t + +// Sequence styles. +const ( + // Let the emitter choose the style. + yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota + + yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. + yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. +) + +type yaml_mapping_style_t yaml_style_t + +// Mapping styles. +const ( + // Let the emitter choose the style. + yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota + + yaml_BLOCK_MAPPING_STYLE // The block mapping style. + yaml_FLOW_MAPPING_STYLE // The flow mapping style. +) + +// Tokens + +type yaml_token_type_t int + +// Token types. +const ( + // An empty token. + yaml_NO_TOKEN yaml_token_type_t = iota + + yaml_STREAM_START_TOKEN // A STREAM-START token. + yaml_STREAM_END_TOKEN // A STREAM-END token. + + yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. + yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. + yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. + yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. + + yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. + yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. + yaml_BLOCK_END_TOKEN // A BLOCK-END token. + + yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. + yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. + yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. + yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. + + yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. + yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. + yaml_KEY_TOKEN // A KEY token. + yaml_VALUE_TOKEN // A VALUE token. + + yaml_ALIAS_TOKEN // An ALIAS token. + yaml_ANCHOR_TOKEN // An ANCHOR token. + yaml_TAG_TOKEN // A TAG token. + yaml_SCALAR_TOKEN // A SCALAR token. +) + +func (tt yaml_token_type_t) String() string { + switch tt { + case yaml_NO_TOKEN: + return "yaml_NO_TOKEN" + case yaml_STREAM_START_TOKEN: + return "yaml_STREAM_START_TOKEN" + case yaml_STREAM_END_TOKEN: + return "yaml_STREAM_END_TOKEN" + case yaml_VERSION_DIRECTIVE_TOKEN: + return "yaml_VERSION_DIRECTIVE_TOKEN" + case yaml_TAG_DIRECTIVE_TOKEN: + return "yaml_TAG_DIRECTIVE_TOKEN" + case yaml_DOCUMENT_START_TOKEN: + return "yaml_DOCUMENT_START_TOKEN" + case yaml_DOCUMENT_END_TOKEN: + return "yaml_DOCUMENT_END_TOKEN" + case yaml_BLOCK_SEQUENCE_START_TOKEN: + return "yaml_BLOCK_SEQUENCE_START_TOKEN" + case yaml_BLOCK_MAPPING_START_TOKEN: + return "yaml_BLOCK_MAPPING_START_TOKEN" + case yaml_BLOCK_END_TOKEN: + return "yaml_BLOCK_END_TOKEN" + case yaml_FLOW_SEQUENCE_START_TOKEN: + return "yaml_FLOW_SEQUENCE_START_TOKEN" + case yaml_FLOW_SEQUENCE_END_TOKEN: + return "yaml_FLOW_SEQUENCE_END_TOKEN" + case yaml_FLOW_MAPPING_START_TOKEN: + return "yaml_FLOW_MAPPING_START_TOKEN" + case yaml_FLOW_MAPPING_END_TOKEN: + return "yaml_FLOW_MAPPING_END_TOKEN" + case yaml_BLOCK_ENTRY_TOKEN: + return "yaml_BLOCK_ENTRY_TOKEN" + case yaml_FLOW_ENTRY_TOKEN: + return "yaml_FLOW_ENTRY_TOKEN" + case yaml_KEY_TOKEN: + return "yaml_KEY_TOKEN" + case yaml_VALUE_TOKEN: + return "yaml_VALUE_TOKEN" + case yaml_ALIAS_TOKEN: + return "yaml_ALIAS_TOKEN" + case yaml_ANCHOR_TOKEN: + return "yaml_ANCHOR_TOKEN" + case yaml_TAG_TOKEN: + return "yaml_TAG_TOKEN" + case yaml_SCALAR_TOKEN: + return "yaml_SCALAR_TOKEN" + } + return "<unknown token>" +} + +// The token structure. +type yaml_token_t struct { + // The token type. + typ yaml_token_type_t + + // The start/end of the token. + start_mark, end_mark yaml_mark_t + + // The stream encoding (for yaml_STREAM_START_TOKEN). + encoding yaml_encoding_t + + // The alias/anchor/scalar value or tag/tag directive handle + // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). + value []byte + + // The tag suffix (for yaml_TAG_TOKEN). + suffix []byte + + // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). + prefix []byte + + // The scalar style (for yaml_SCALAR_TOKEN). + style yaml_scalar_style_t + + // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). + major, minor int8 +} + +// Events + +type yaml_event_type_t int8 + +// Event types. +const ( + // An empty event. + yaml_NO_EVENT yaml_event_type_t = iota + + yaml_STREAM_START_EVENT // A STREAM-START event. + yaml_STREAM_END_EVENT // A STREAM-END event. + yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. + yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. + yaml_ALIAS_EVENT // An ALIAS event. + yaml_SCALAR_EVENT // A SCALAR event. + yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. + yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. + yaml_MAPPING_START_EVENT // A MAPPING-START event. + yaml_MAPPING_END_EVENT // A MAPPING-END event. +) + +// The event structure. +type yaml_event_t struct { + + // The event type. + typ yaml_event_type_t + + // The start and end of the event. + start_mark, end_mark yaml_mark_t + + // The document encoding (for yaml_STREAM_START_EVENT). + encoding yaml_encoding_t + + // The version directive (for yaml_DOCUMENT_START_EVENT). + version_directive *yaml_version_directive_t + + // The list of tag directives (for yaml_DOCUMENT_START_EVENT). + tag_directives []yaml_tag_directive_t + + // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). + anchor []byte + + // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). + tag []byte + + // The scalar value (for yaml_SCALAR_EVENT). + value []byte + + // Is the document start/end indicator implicit, or the tag optional? + // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). + implicit bool + + // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). + quoted_implicit bool + + // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). + style yaml_style_t +} + +func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } +func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } +func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } + +// Nodes + +const ( + yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. + yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. + yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. + yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. + yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. + yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. + + yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. + yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. + + // Not in original libyaml. + yaml_BINARY_TAG = "tag:yaml.org,2002:binary" + yaml_MERGE_TAG = "tag:yaml.org,2002:merge" + + yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. + yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. + yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. +) + +type yaml_node_type_t int + +// Node types. +const ( + // An empty node. + yaml_NO_NODE yaml_node_type_t = iota + + yaml_SCALAR_NODE // A scalar node. + yaml_SEQUENCE_NODE // A sequence node. + yaml_MAPPING_NODE // A mapping node. +) + +// An element of a sequence node. +type yaml_node_item_t int + +// An element of a mapping node. +type yaml_node_pair_t struct { + key int // The key of the element. + value int // The value of the element. +} + +// The node structure. +type yaml_node_t struct { + typ yaml_node_type_t // The node type. + tag []byte // The node tag. + + // The node data. + + // The scalar parameters (for yaml_SCALAR_NODE). + scalar struct { + value []byte // The scalar value. + length int // The length of the scalar value. + style yaml_scalar_style_t // The scalar style. + } + + // The sequence parameters (for YAML_SEQUENCE_NODE). + sequence struct { + items_data []yaml_node_item_t // The stack of sequence items. + style yaml_sequence_style_t // The sequence style. + } + + // The mapping parameters (for yaml_MAPPING_NODE). + mapping struct { + pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). + pairs_start *yaml_node_pair_t // The beginning of the stack. + pairs_end *yaml_node_pair_t // The end of the stack. + pairs_top *yaml_node_pair_t // The top of the stack. + style yaml_mapping_style_t // The mapping style. + } + + start_mark yaml_mark_t // The beginning of the node. + end_mark yaml_mark_t // The end of the node. + +} + +// The document structure. +type yaml_document_t struct { + + // The document nodes. + nodes []yaml_node_t + + // The version directive. + version_directive *yaml_version_directive_t + + // The list of tag directives. + tag_directives_data []yaml_tag_directive_t + tag_directives_start int // The beginning of the tag directives list. + tag_directives_end int // The end of the tag directives list. + + start_implicit int // Is the document start indicator implicit? + end_implicit int // Is the document end indicator implicit? + + // The start/end of the document. + start_mark, end_mark yaml_mark_t +} + +// The prototype of a read handler. +// +// The read handler is called when the parser needs to read more bytes from the +// source. The handler should write not more than size bytes to the buffer. +// The number of written bytes should be set to the size_read variable. +// +// [in,out] data A pointer to an application data specified by +// yaml_parser_set_input(). +// [out] buffer The buffer to write the data from the source. +// [in] size The size of the buffer. +// [out] size_read The actual number of bytes read from the source. +// +// On success, the handler should return 1. If the handler failed, +// the returned value should be 0. On EOF, the handler should set the +// size_read to 0 and return 1. +type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) + +// This structure holds information about a potential simple key. +type yaml_simple_key_t struct { + possible bool // Is a simple key possible? + required bool // Is a simple key required? + token_number int // The number of the token. + mark yaml_mark_t // The position mark. +} + +// The states of the parser. +type yaml_parser_state_t int + +const ( + yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota + + yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. + yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. + yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. + yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. + yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. + yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. + yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. + yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. + yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. + yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. + yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. + yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. + yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. + yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. + yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. + yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. + yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. + yaml_PARSE_END_STATE // Expect nothing. +) + +func (ps yaml_parser_state_t) String() string { + switch ps { + case yaml_PARSE_STREAM_START_STATE: + return "yaml_PARSE_STREAM_START_STATE" + case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: + return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" + case yaml_PARSE_DOCUMENT_START_STATE: + return "yaml_PARSE_DOCUMENT_START_STATE" + case yaml_PARSE_DOCUMENT_CONTENT_STATE: + return "yaml_PARSE_DOCUMENT_CONTENT_STATE" + case yaml_PARSE_DOCUMENT_END_STATE: + return "yaml_PARSE_DOCUMENT_END_STATE" + case yaml_PARSE_BLOCK_NODE_STATE: + return "yaml_PARSE_BLOCK_NODE_STATE" + case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: + return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" + case yaml_PARSE_FLOW_NODE_STATE: + return "yaml_PARSE_FLOW_NODE_STATE" + case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: + return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" + case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: + return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" + case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: + return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" + case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: + return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" + case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: + return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" + case yaml_PARSE_FLOW_MAPPING_KEY_STATE: + return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" + case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: + return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: + return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" + case yaml_PARSE_END_STATE: + return "yaml_PARSE_END_STATE" + } + return "<unknown parser state>" +} + +// This structure holds aliases data. +type yaml_alias_data_t struct { + anchor []byte // The anchor. + index int // The node id. + mark yaml_mark_t // The anchor mark. +} + +// The parser structure. +// +// All members are internal. Manage the structure using the +// yaml_parser_ family of functions. +type yaml_parser_t struct { + + // Error handling + + error yaml_error_type_t // Error type. + + problem string // Error description. + + // The byte about which the problem occured. + problem_offset int + problem_value int + problem_mark yaml_mark_t + + // The error context. + context string + context_mark yaml_mark_t + + // Reader stuff + + read_handler yaml_read_handler_t // Read handler. + + input_file io.Reader // File input data. + input []byte // String input data. + input_pos int + + eof bool // EOF flag + + buffer []byte // The working buffer. + buffer_pos int // The current position of the buffer. + + unread int // The number of unread characters in the buffer. + + raw_buffer []byte // The raw buffer. + raw_buffer_pos int // The current position of the buffer. + + encoding yaml_encoding_t // The input encoding. + + offset int // The offset of the current position (in bytes). + mark yaml_mark_t // The mark of the current position. + + // Scanner stuff + + stream_start_produced bool // Have we started to scan the input stream? + stream_end_produced bool // Have we reached the end of the input stream? + + flow_level int // The number of unclosed '[' and '{' indicators. + + tokens []yaml_token_t // The tokens queue. + tokens_head int // The head of the tokens queue. + tokens_parsed int // The number of tokens fetched from the queue. + token_available bool // Does the tokens queue contain a token ready for dequeueing. + + indent int // The current indentation level. + indents []int // The indentation levels stack. + + simple_key_allowed bool // May a simple key occur at the current position? + simple_keys []yaml_simple_key_t // The stack of simple keys. + + // Parser stuff + + state yaml_parser_state_t // The current parser state. + states []yaml_parser_state_t // The parser states stack. + marks []yaml_mark_t // The stack of marks. + tag_directives []yaml_tag_directive_t // The list of TAG directives. + + // Dumper stuff + + aliases []yaml_alias_data_t // The alias data. + + document *yaml_document_t // The currently parsed document. +} + +// Emitter Definitions + +// The prototype of a write handler. +// +// The write handler is called when the emitter needs to flush the accumulated +// characters to the output. The handler should write @a size bytes of the +// @a buffer to the output. +// +// @param[in,out] data A pointer to an application data specified by +// yaml_emitter_set_output(). +// @param[in] buffer The buffer with bytes to be written. +// @param[in] size The size of the buffer. +// +// @returns On success, the handler should return @c 1. If the handler failed, +// the returned value should be @c 0. +// +type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error + +type yaml_emitter_state_t int + +// The emitter states. +const ( + // Expect STREAM-START. + yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota + + yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. + yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. + yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. + yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. + yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. + yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. + yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. + yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. + yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. + yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. + yaml_EMIT_END_STATE // Expect nothing. +) + +// The emitter structure. +// +// All members are internal. Manage the structure using the @c yaml_emitter_ +// family of functions. +type yaml_emitter_t struct { + + // Error handling + + error yaml_error_type_t // Error type. + problem string // Error description. + + // Writer stuff + + write_handler yaml_write_handler_t // Write handler. + + output_buffer *[]byte // String output data. + output_file io.Writer // File output data. + + buffer []byte // The working buffer. + buffer_pos int // The current position of the buffer. + + raw_buffer []byte // The raw buffer. + raw_buffer_pos int // The current position of the buffer. + + encoding yaml_encoding_t // The stream encoding. + + // Emitter stuff + + canonical bool // If the output is in the canonical style? + best_indent int // The number of indentation spaces. + best_width int // The preferred width of the output lines. + unicode bool // Allow unescaped non-ASCII characters? + line_break yaml_break_t // The preferred line break. + + state yaml_emitter_state_t // The current emitter state. + states []yaml_emitter_state_t // The stack of states. + + events []yaml_event_t // The event queue. + events_head int // The head of the event queue. + + indents []int // The stack of indentation levels. + + tag_directives []yaml_tag_directive_t // The list of tag directives. + + indent int // The current indentation level. + + flow_level int // The current flow level. + + root_context bool // Is it the document root context? + sequence_context bool // Is it a sequence context? + mapping_context bool // Is it a mapping context? + simple_key_context bool // Is it a simple mapping key context? + + line int // The current line. + column int // The current column. + whitespace bool // If the last character was a whitespace? + indention bool // If the last character was an indentation character (' ', '-', '?', ':')? + open_ended bool // If an explicit document end is required? + + // Anchor analysis. + anchor_data struct { + anchor []byte // The anchor value. + alias bool // Is it an alias? + } + + // Tag analysis. + tag_data struct { + handle []byte // The tag handle. + suffix []byte // The tag suffix. + } + + // Scalar analysis. + scalar_data struct { + value []byte // The scalar value. + multiline bool // Does the scalar contain line breaks? + flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? + block_plain_allowed bool // Can the scalar be expressed in the block plain style? + single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? + block_allowed bool // Can the scalar be expressed in the literal or folded styles? + style yaml_scalar_style_t // The output style. + } + + // Dumper stuff + + opened bool // If the stream was already opened? + closed bool // If the stream was already closed? + + // The information associated with the document nodes. + anchors *struct { + references int // The number of references. + anchor int // The anchor id. + serialized bool // If the node has been emitted? + } + + last_anchor_id int // The last assigned anchor id. + + document *yaml_document_t // The currently emitted document. +} diff --git a/vendor/gopkg.in/yaml.v2/yamlprivateh.go b/vendor/gopkg.in/yaml.v2/yamlprivateh.go new file mode 100644 index 0000000000000000000000000000000000000000..8110ce3c37a6b4e152aee0fb65e36b99f55be6b6 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/yamlprivateh.go @@ -0,0 +1,173 @@ +package yaml + +const ( + // The size of the input raw buffer. + input_raw_buffer_size = 512 + + // The size of the input buffer. + // It should be possible to decode the whole raw buffer. + input_buffer_size = input_raw_buffer_size * 3 + + // The size of the output buffer. + output_buffer_size = 128 + + // The size of the output raw buffer. + // It should be possible to encode the whole output buffer. + output_raw_buffer_size = (output_buffer_size*2 + 2) + + // The size of other stacks and queues. + initial_stack_size = 16 + initial_queue_size = 16 + initial_string_size = 16 +) + +// Check if the character at the specified position is an alphabetical +// character, a digit, '_', or '-'. +func is_alpha(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' +} + +// Check if the character at the specified position is a digit. +func is_digit(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' +} + +// Get the value of a digit. +func as_digit(b []byte, i int) int { + return int(b[i]) - '0' +} + +// Check if the character at the specified position is a hex-digit. +func is_hex(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' +} + +// Get the value of a hex-digit. +func as_hex(b []byte, i int) int { + bi := b[i] + if bi >= 'A' && bi <= 'F' { + return int(bi) - 'A' + 10 + } + if bi >= 'a' && bi <= 'f' { + return int(bi) - 'a' + 10 + } + return int(bi) - '0' +} + +// Check if the character is ASCII. +func is_ascii(b []byte, i int) bool { + return b[i] <= 0x7F +} + +// Check if the character at the start of the buffer can be printed unescaped. +func is_printable(b []byte, i int) bool { + return ((b[i] == 0x0A) || // . == #x0A + (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E + (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF + (b[i] > 0xC2 && b[i] < 0xED) || + (b[i] == 0xED && b[i+1] < 0xA0) || + (b[i] == 0xEE) || + (b[i] == 0xEF && // #xE000 <= . <= #xFFFD + !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF + !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) +} + +// Check if the character at the specified position is NUL. +func is_z(b []byte, i int) bool { + return b[i] == 0x00 +} + +// Check if the beginning of the buffer is a BOM. +func is_bom(b []byte, i int) bool { + return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF +} + +// Check if the character at the specified position is space. +func is_space(b []byte, i int) bool { + return b[i] == ' ' +} + +// Check if the character at the specified position is tab. +func is_tab(b []byte, i int) bool { + return b[i] == '\t' +} + +// Check if the character at the specified position is blank (space or tab). +func is_blank(b []byte, i int) bool { + //return is_space(b, i) || is_tab(b, i) + return b[i] == ' ' || b[i] == '\t' +} + +// Check if the character at the specified position is a line break. +func is_break(b []byte, i int) bool { + return (b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) +} + +func is_crlf(b []byte, i int) bool { + return b[i] == '\r' && b[i+1] == '\n' +} + +// Check if the character is a line break or NUL. +func is_breakz(b []byte, i int) bool { + //return is_break(b, i) || is_z(b, i) + return ( // is_break: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + // is_z: + b[i] == 0) +} + +// Check if the character is a line break, space, or NUL. +func is_spacez(b []byte, i int) bool { + //return is_space(b, i) || is_breakz(b, i) + return ( // is_space: + b[i] == ' ' || + // is_breakz: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + b[i] == 0) +} + +// Check if the character is a line break, space, tab, or NUL. +func is_blankz(b []byte, i int) bool { + //return is_blank(b, i) || is_breakz(b, i) + return ( // is_blank: + b[i] == ' ' || b[i] == '\t' || + // is_breakz: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + b[i] == 0) +} + +// Determine the width of the character. +func width(b byte) int { + // Don't replace these by a switch without first + // confirming that it is being inlined. + if b&0x80 == 0x00 { + return 1 + } + if b&0xE0 == 0xC0 { + return 2 + } + if b&0xF0 == 0xE0 { + return 3 + } + if b&0xF8 == 0xF0 { + return 4 + } + return 0 + +}