diff --git a/cmd/start.go b/cmd/start.go index eaa53acf59..7e37635a25 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -93,6 +93,8 @@ type Start struct { Storage string `long:"storage" description:"set the outgoing message storage option [self-hosted, dropbox] default=self-hosted"` BitcoinCash bool `long:"bitcoincash" description:"use a Bitcoin Cash wallet in a dedicated data directory"` ZCash string `long:"zcash" description:"use a ZCash wallet in a dedicated data directory. To use this you must pass in the location of the zcashd binary."` + + ForceKeyCachePurge bool `long:"forcekeypurge" description:"repair test for issue OpenBazaar/openbazaar-go#1593; use as instructed only"` } func (x *Start) Execute(args []string) error { @@ -430,6 +432,11 @@ func (x *Start) Execute(args []string) error { nd.Repo.Datastore().Delete(ipnskey) } + if x.ForceKeyCachePurge { + log.Infof("forcing key purge from namesys cache...") + nd.Repo.Datastore().Delete(ipnskey) + } + // Wallet mn, err := sqliteDB.Config().GetMnemonic() if err != nil { diff --git a/core/core.go b/core/core.go index 8c2e52940c..0f97ed2a41 100644 --- a/core/core.go +++ b/core/core.go @@ -32,7 +32,7 @@ import ( const ( // VERSION - current version - VERSION = "0.13.3" + VERSION = "0.13.4" // USERAGENT - user-agent header string USERAGENT = "/openbazaar-go:" + VERSION + "/" ) diff --git a/docs/install-linux.md b/docs/install-linux.md index d96a8a9c51..6b8d15e0fd 100644 --- a/docs/install-linux.md +++ b/docs/install-linux.md @@ -54,7 +54,7 @@ It will use git to checkout the source code into `$GOPATH/src/github.com/OpenBaz Checkout a release version: ``` -git checkout v0.13.3 +git checkout v0.13.4 ``` Note: `go get` leaves the repo pointing at `master` which is a branch used for active development. Running OpenBazaar from `master` is NOT recommended. Check the [release versions](https://github.com/OpenBazaar/openbazaar-go/releases) for the available versions that you use in checkout. diff --git a/docs/install-osx.md b/docs/install-osx.md index bee55edfdd..921974524f 100644 --- a/docs/install-osx.md +++ b/docs/install-osx.md @@ -56,7 +56,7 @@ It will use git to checkout the source code into `$GOPATH/src/github.com/OpenBaz Checkout a release version: ``` -git checkout v0.13.3 +git checkout v0.13.4 ``` Note: `go get` leaves the repo pointing at `master` which is a branch used for active development. Running OpenBazaar from `master` is NOT recommended. Check the [release versions](https://github.com/OpenBazaar/openbazaar-go/releases) for the available versions that you use in checkout. diff --git a/docs/install-pi3.md b/docs/install-pi3.md index 71a6fb60d4..22e73c0357 100644 --- a/docs/install-pi3.md +++ b/docs/install-pi3.md @@ -57,7 +57,7 @@ It will use git to checkout the source code into `$GOPATH/src/github.com/OpenBaz Checkout a release version: ``` -git checkout v0.13.3 +git checkout v0.13.4 ``` Note: `go get` leaves the repo pointing at `master` which is a branch used for active development. Running OpenBazaar from `master` is NOT recommended. Check the [release versions](https://github.com/OpenBazaar/openbazaar-go/releases) for the available versions that you use iin checkout. diff --git a/docs/install-windows.md b/docs/install-windows.md index dc77a4a034..e9695c1f1f 100644 --- a/docs/install-windows.md +++ b/docs/install-windows.md @@ -27,7 +27,7 @@ Create a directory to store all your Go projects (e.g. `C:\goprojects`): - Install `openbazaar-go`: + Open the command prompt and run: `go get github.com/OpenBazaar/openbazaar-go`. This will use git to checkout the source code into `%GOPATH%\src\github.com\OpenBazaar\openbazaar-go`. - Checkout an OpenBazaar release: - + Run `git checkout v0.13.3` to checkout a release version. + + Run `git checkout v0.13.4` to checkout a release version. + Note: `go get` leaves the repo pointing at `master` which is a branch used for active development. Running OpenBazaar from `master` is NOT recommended. Check the [release versions](https://github.com/OpenBazaar/openbazaar-go/releases) for the available versions that you use in checkout. - To compile and run `openbazaar-go`: + Open the command prompt and navigate the source directory: `cd %GOPATH%\src\github.com\OpenBazaar\openbazaar-go` diff --git a/ipfs/identity.go b/ipfs/identity.go index 4beb167a7e..d08d43da2d 100644 --- a/ipfs/identity.go +++ b/ipfs/identity.go @@ -17,7 +17,6 @@ func init() { } func IdentityFromKey(privkey []byte) (config.Identity, error) { - ident := config.Identity{} sk, err := crypto.UnmarshalPrivateKey(privkey) if err != nil { diff --git a/ipfs/resolve.go b/ipfs/resolve.go index 5562869862..09ef4c1aaf 100644 --- a/ipfs/resolve.go +++ b/ipfs/resolve.go @@ -2,13 +2,15 @@ package ipfs import ( "context" - ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore" "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" "time" ipath "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" + ipnspb "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns/pb" + "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" + "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32" "github.com/ipfs/go-ipfs/core" "github.com/ipfs/go-ipfs/namesys" @@ -16,6 +18,10 @@ import ( nameopts "gx/ipfs/QmXLwxifxwfc2bAwq6rdjbYqAsGzWsDE9RM5TWMGtykyj6/interface-go-ipfs-core/options/namesys" ) +const ( + persistentCacheDbPrefix = "/ipns/persistentcache/" +) + // Resolve an IPNS record. This is a multi-step process. // If the usecache flag is provided we will attempt to load the record from the database. If // it succeeds we will update the cache in a separate goroutine. @@ -37,7 +43,7 @@ func Resolve(n *core.IpfsNode, p peer.ID, timeout time.Duration, quorum uint, us return } if n.Identity != p { - if err := putToDatastore(n.Repo.Datastore(), p, pth); err != nil { + if err := putToDatastoreCache(n.Repo.Datastore(), p, pth); err != nil { log.Error("Error putting IPNS record to datastore: %s", err.Error()) } } @@ -56,7 +62,7 @@ func Resolve(n *core.IpfsNode, p peer.ID, timeout time.Duration, quorum uint, us } // Resolving succeeded. Update the cache. if n.Identity != p { - if err := putToDatastore(n.Repo.Datastore(), p, pth); err != nil { + if err := putToDatastoreCache(n.Repo.Datastore(), p, pth); err != nil { log.Error("Error putting IPNS record to datastore: %s", err.Error()) } } @@ -87,18 +93,36 @@ func ResolveAltRoot(n *core.IpfsNode, p peer.ID, altRoot string, timeout time.Du return pth.Segments()[1], nil } +// getFromDatastore looks in two places in the database for a record. First is +// under the /ipfs/ key which is sometimes used by the DHT. The value +// returned by this location is a serialized protobuf record. The second is +// under /ipfs/persistentcache/ which returns only the value (the path) +// inside the protobuf record. func getFromDatastore(datastore ds.Datastore, p peer.ID) (ipath.Path, error) { - // resolve to what we may already have in the datastore - data, err := datastore.Get(namesys.IpnsDsKey(p)) + ival, err := datastore.Get(namesys.IpnsDsKey(p)) if err != nil { - if err == ds.ErrNotFound { - return "", namesys.ErrResolveFailed + pth, err := datastore.Get(ipnsCacheDsKey(p)) + if err != nil { + if err == ds.ErrNotFound { + return "", namesys.ErrResolveFailed + } + return "", err } + return ipath.ParsePath(string(pth)) + } + + rec := new(ipnspb.IpnsEntry) + err = proto.Unmarshal(ival, rec) + if err != nil { return "", err } - return ipath.ParsePath(string(data)) + return ipath.ParsePath(string(rec.Value)) +} + +func putToDatastoreCache(datastore ds.Datastore, p peer.ID, pth ipath.Path) error { + return datastore.Put(ipnsCacheDsKey(p), []byte(pth.String())) } -func putToDatastore(datastore ds.Datastore, p peer.ID, pth ipath.Path) error { - return datastore.Put(namesys.IpnsDsKey(p), []byte(pth.String())) +func ipnsCacheDsKey(id peer.ID) ds.Key { + return ds.NewKey(persistentCacheDbPrefix + base32.RawStdEncoding.EncodeToString([]byte(id))) } diff --git a/ipfs/resolve_test.go b/ipfs/resolve_test.go new file mode 100644 index 0000000000..3270d8ed2e --- /dev/null +++ b/ipfs/resolve_test.go @@ -0,0 +1,79 @@ +package ipfs + +import ( + "github.com/ipfs/go-ipfs/core/mock" + "github.com/ipfs/go-ipfs/namesys" + ipath "gx/ipfs/QmQAgv6Gaoe2tQpcabqwKXKChp2MZ7i3UXv9DqTTaxCaTR/go-path" + ipnspb "gx/ipfs/QmUwMnKKjH3JwGKNVZ3TcP37W93xzqNA4ECFFiMo6sXkkc/go-ipns/pb" + "gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer" + "gx/ipfs/QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB/gogo-protobuf/proto" + "testing" +) + +// TestDatastoreCaching tests that the data can be inserted and retrieved from the +// database using the IPNS namespace as well as our persistent cache namespace and +// that the two do not conflict with each other. +func TestDatastoreCaching(t *testing.T) { + n, err := coremock.NewMockNode() + if err != nil { + t.Error(err) + } + var ( + pth = "/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h" + rec = &ipnspb.IpnsEntry{Value: []byte(pth), Signature: []byte{}} + peerIDStr = "QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB" + ) + + peerID, err := peer.IDB58Decode(peerIDStr) + if err != nil { + t.Fatal(err) + } + + // First put to db using the IPNS namespace. + serializedRecord, err := proto.Marshal(rec) + if err != nil { + t.Fatal(err) + } + + if err := n.Repo.Datastore().Put(namesys.IpnsDsKey(peerID), serializedRecord); err != nil { + t.Fatal(err) + } + + retreivedPath, err := getFromDatastore(n.Repo.Datastore(), peerID) + if err != nil { + t.Error(err) + } + + if retreivedPath.String() != pth { + t.Errorf("Retreived incorrect value. Expected %s, got %s", pth, retreivedPath.String()) + } + + // Next put to the database using the persistent cache namespace. + if err := putToDatastoreCache(n.Repo.Datastore(), peerID, ipath.Path(pth)); err != nil { + t.Fatal(err) + } + + retreivedPath, err = getFromDatastore(n.Repo.Datastore(), peerID) + if err != nil { + t.Error(err) + } + + if retreivedPath.String() != pth { + t.Errorf("Retreived incorrect value. Expected %s, got %s", pth, retreivedPath.String()) + } + + // Test the persistent cache put did not override the IPNS namespace. + ival, err := n.Repo.Datastore().Get(namesys.IpnsDsKey(peerID)) + if err != nil { + t.Fatal(err) + } + + rec2 := new(ipnspb.IpnsEntry) + err = proto.Unmarshal(ival, rec2) + if err != nil { + t.Error(err) + } + if string(rec2.Value) != pth { + t.Errorf("Retreived incorrect value. Expected %s, got %s", pth, string(rec2.Value)) + } +} diff --git a/net/service/handlers.go b/net/service/handlers.go index 0c44e9dfb7..f02489639e 100644 --- a/net/service/handlers.go +++ b/net/service/handlers.go @@ -1320,10 +1320,6 @@ func (service *OpenBazaarService) handleModeratorAdd(pid peer.ID, pmes *pb.Messa if err != nil { return nil, err } - n := repo.ModeratorAddNotification{repo.NewNotificationID(), "moderatorAdd", id.Pretty()} - service.broadcast <- n - - service.datastore.Notifications().PutRecord(repo.NewNotification(n, time.Now(), false)) log.Debugf("Received MODERATOR_ADD message from %s", id.Pretty()) return nil, nil @@ -1365,10 +1361,6 @@ func (service *OpenBazaarService) handleModeratorRemove(pid peer.ID, pmes *pb.Me if err != nil { return nil, err } - n := repo.ModeratorRemoveNotification{repo.NewNotificationID(), "moderatorRemove", id.Pretty()} - service.broadcast <- n - - service.datastore.Notifications().PutRecord(repo.NewNotification(n, time.Now(), false)) log.Debugf("Received MODERATOR_REMOVE message from %s", id.Pretty()) return nil, nil diff --git a/package.json b/package.json index 50539a3d03..24c0d0daaf 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,11 @@ { "name": "openbazaar-go", - "author": "chris", - "version": "0.13.3", - "releaseCmd": "git commit -a -m \"gx publish $VERSION\"", + "author": "OB1 Company", + "version": "0.13.4", "language": "english", "license": "", "bugs": { - "url": "https://github.com/OpenBazaar/openbazaar-go" + "url": "https://github.com/OpenBazaar/openbazaar-go/issues/new" }, "gxVersion": "0.11.0" } diff --git a/vendor/gx/ipfs/QmSVaJe1aRjc78cZARTtf4pqvXERYwihyYhZWoVWceHnsK/go-libp2p-secio/protocol.go b/vendor/gx/ipfs/QmSVaJe1aRjc78cZARTtf4pqvXERYwihyYhZWoVWceHnsK/go-libp2p-secio/protocol.go index 57547a63de..e6136ce6dd 100644 --- a/vendor/gx/ipfs/QmSVaJe1aRjc78cZARTtf4pqvXERYwihyYhZWoVWceHnsK/go-libp2p-secio/protocol.go +++ b/vendor/gx/ipfs/QmSVaJe1aRjc78cZARTtf4pqvXERYwihyYhZWoVWceHnsK/go-libp2p-secio/protocol.go @@ -205,26 +205,18 @@ func (s *secureSession) runHandshakeSync() error { // No peer set. We're accepting a remote connection. s.remotePeer = actualRemotePeer default: - // OpenBazaar: check that this peerID isn't the old style - // If it is then we're good. If not then we error. - // This code will be removed after enough OpenBazaar nodes - // upgrade to inline pubkeys. - pubkeyBytes, err := s.remote.permanentPubKey.Bytes() + // OpenBazaar: we are transitioning from hashed IDs to inline IDs, as a last + // resort, we should check that the peer isn't using the other variety. Both ID methods + // change their behavior based on peer.AdvancedEnableInlining but are written to + // operate opposite of each other. This will allow old nodes to successfully connect to + // new nodes, and vice versa. + altID, err := peer.AlternativeIDFromPublicKey(s.remote.permanentPubKey) if err != nil { return err } - oldMultihash, err := mh.Sum(pubkeyBytes, mh.SHA2_256, 32) - if err != nil { - return err - } - oldStylePeer, err := peer.IDB58Decode(oldMultihash.B58String()) - if err != nil { - return err - } - if s.remotePeer != oldStylePeer { - // Peer mismatch. Bail. + if s.remotePeer != altID { s.insecure.Close() - log.Debugf("expected peer %s, got peer %s", s.remotePeer, actualRemotePeer) + log.Debugf("expected peer %s, but ID produced from pubkey doesn't match", s.remotePeer) return ErrWrongPeer } } diff --git a/vendor/gx/ipfs/QmSVaJe1aRjc78cZARTtf4pqvXERYwihyYhZWoVWceHnsK/go-libp2p-secio/rw.go b/vendor/gx/ipfs/QmSVaJe1aRjc78cZARTtf4pqvXERYwihyYhZWoVWceHnsK/go-libp2p-secio/rw.go index a649a74f7a..2a1f3f3859 100644 --- a/vendor/gx/ipfs/QmSVaJe1aRjc78cZARTtf4pqvXERYwihyYhZWoVWceHnsK/go-libp2p-secio/rw.go +++ b/vendor/gx/ipfs/QmSVaJe1aRjc78cZARTtf4pqvXERYwihyYhZWoVWceHnsK/go-libp2p-secio/rw.go @@ -167,7 +167,9 @@ func (r *etmReader) Read(buf []byte) (int, error) { // If the destination buffer is too short, fill an internal buffer and then // drain as much of that into the output buffer as will fit. - if cap(buf) < fullLen { + // Applies patch from v0.0.3 release, see + // https://github.com/libp2p/go-libp2p-secio/commit/98b8bd9f149802621eeca00d5a606f6788973038 + if len(buf) < fullLen { err := r.fill() if err != nil { return 0, err diff --git a/vendor/gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer/peer.go b/vendor/gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer/peer.go index 63dd318e14..46d494925a 100644 --- a/vendor/gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer/peer.go +++ b/vendor/gx/ipfs/QmYVXrKrKHDC9FobgmcmshCDyWwdrfwfanNQN4oxJ9Fk3h/go-libp2p-peer/peer.go @@ -165,21 +165,58 @@ func IDFromPublicKey(pk ic.PubKey) (ID, error) { if AdvancedEnableInlining && len(b) <= maxInlineKeyLength { alg = mh.ID } - hash, _ := mh.Sum(b, alg, -1) + // OpenBazaar: handle the Sum error + hash, err := mh.Sum(b, alg, -1) + if err != nil { + return "", err + } return ID(hash), nil } -// OpenBazaar: temporary helper function to remain forward compatible with -// inline keys +// HashedIDFromPublicKey will always return the SHA256 hash of +// the pubkey bytes. +// OpenBazaar: temporary helper to isolate the +// hash-producing ID behavior. +func HashedIDFromPublicKey(pk ic.PubKey) (ID, error) { + b, err := pk.Bytes() + if err != nil { + return "", err + } + hash, err := mh.Sum(b, mh.SHA2_256, -1) + if err != nil { + return "", err + } + return ID(hash), nil +} + +// InlineIDFromPublicKey will always return the new inline ID format +// of the pubkey bytes. +// OpenBazaar: temporary helper function to +// remain forward compatible with inline keys func InlineIDFromPublicKey(pk ic.PubKey) (ID, error) { b, err := pk.Bytes() if err != nil { return "", err } - hash, _ := mh.Sum(b, mh.ID, -1) + hash, err := mh.Sum(b, mh.SHA2_256, -1) + if err != nil { + return "", err + } return ID(hash), nil } +// AlternativeIDFromPublicKey returns SHA256 hash ID when AdvancedEnableInlining +// is true, and returns new InlineID otherwise. +// OpenBazaar: This allows legacy IDs +// to be compared after they are no longer available by the default IDFromPublicKey +// function. +func AlternativeIDFromPublicKey(pubkey ic.PubKey) (ID, error) { + if AdvancedEnableInlining { + return HashedIDFromPublicKey(pubkey) + } + return InlineIDFromPublicKey(pubkey) +} + // IDFromPrivateKey returns the Peer ID corresponding to sk func IDFromPrivateKey(sk ic.PrivKey) (ID, error) { return IDFromPublicKey(sk.GetPublic())