Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enumerate Nvidia GPU IDs #1840

Merged
merged 3 commits into from
Apr 22, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@

#### Transcoder

- \#1840 Automatically use all GPUs when -nvidia=all flag is set (@jailuthra)

### Bug Fixes 🐞

#### General
16 changes: 12 additions & 4 deletions cmd/livepeer/livepeer.go
Original file line number Diff line number Diff line change
@@ -124,7 +124,7 @@ func main() {
maxAttempts := flag.Int("maxAttempts", 3, "Maximum transcode attempts")
maxSessions := flag.Int("maxSessions", 10, "Maximum number of concurrent transcoding sessions for Orchestrator, maximum number or RTMP streams for Broadcaster, or maximum capacity for transcoder")
currentManifest := flag.Bool("currentManifest", false, "Expose the currently active ManifestID as \"/stream/current.m3u8\"")
nvidia := flag.String("nvidia", "", "Comma-separated list of Nvidia GPU device IDs to use for transcoding")
nvidia := flag.String("nvidia", "", "Comma-separated list of Nvidia GPU device IDs (or \"all\" for all available devices)")
testTranscoder := flag.Bool("testTranscoder", true, "Test Nvidia GPU transcoding at startup")

// Onchain:
@@ -269,13 +269,21 @@ func main() {
if *transcoder {
core.WorkDir = *datadir
if *nvidia != "" {
// Get a list of device ids
devices, err := common.ParseNvidiaDevices(*nvidia)
if err != nil {
glog.Fatalf("Error while parsing '-nvidia %v' flag: %v", *nvidia, err)
}
glog.Infof("Transcoding on these Nvidia GPUs: %v", devices)
// Test transcoding with nvidia
if *testTranscoder {
err := core.TestNvidiaTranscoder(*nvidia)
err := core.TestNvidiaTranscoder(devices)
if err != nil {
glog.Fatalf("Unable to transcode using Nvidia gpu=%s err=%v", *nvidia, err)
glog.Fatalf("Unable to transcode using Nvidia gpu=%s err=%v", strings.Join(devices, ","), err)
}
}
n.Transcoder = core.NewLoadBalancingTranscoder(*nvidia, core.NewNvidiaTranscoder)
// Initialize LB transcoder
n.Transcoder = core.NewLoadBalancingTranscoder(devices, core.NewNvidiaTranscoder)
} else {
n.Transcoder = core.NewLocalTranscoder(*datadir)
}
12 changes: 10 additions & 2 deletions cmd/livepeer_bench/livepeer_bench.go
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ func main() {
concurrentSessions := flag.Int("concurrentSessions", 1, "# of concurrent transcode sessions")
segs := flag.Int("segs", 0, "Maximum # of segments to transcode (default all)")
transcodingOptions := flag.String("transcodingOptions", "P240p30fps16x9,P360p30fps16x9,P720p30fps16x9", "Transcoding options for broadcast job, or path to json config")
nvidia := flag.String("nvidia", "", "Comma-separated list of Nvidia GPU device IDs to use for transcoding")
nvidia := flag.String("nvidia", "", "Comma-separated list of Nvidia GPU device IDs (or \"all\" for all available devices)")
outPrefix := flag.String("outPrefix", "", "Output segments' prefix (no segments are generated by default)")

flag.Parse()
@@ -63,8 +63,12 @@ func main() {
accel := ffmpeg.Software
devices := []string{}
if *nvidia != "" {
var err error
accel = ffmpeg.Nvidia
devices = strings.Split(*nvidia, ",")
devices, err = common.ParseNvidiaDevices(*nvidia)
if err != nil {
glog.Fatalf("Error while parsing '-nvidia %v' flag: %v", *nvidia, err)
}
}

ffmpeg.InitFFmpeg()
@@ -79,6 +83,10 @@ func main() {
{"Live Mode", fmt.Sprintf("%v", *live)},
}

if accel == ffmpeg.Nvidia && len(devices) > 0 {
data = append(data, []string{"Nvidia GPU IDs", fmt.Sprintf("%v", strings.Join(devices, ","))})
}

table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetCenterSeparator("*")
table.SetColumnSeparator("|")
35 changes: 35 additions & 0 deletions common/util.go
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ import (

"github.com/ethereum/go-ethereum/crypto"
"github.com/golang/glog"
"github.com/jaypipes/ghw"
"github.com/livepeer/go-livepeer/net"
ffmpeg "github.com/livepeer/lpms/ffmpeg"
"github.com/pkg/errors"
@@ -410,3 +411,37 @@ func ReadAtMost(r io.Reader, n int) ([]byte, error) {
}
return b, err
}

func detectNvidiaDevices() ([]string, error) {
gpu, err := ghw.GPU()
if err != nil {
return nil, err
}

nvidiaCardCount := 0
re := regexp.MustCompile("(?i)nvidia") // case insensitive match
for _, card := range gpu.GraphicsCards {
if re.MatchString(card.DeviceInfo.Vendor.Name) {
nvidiaCardCount += 1
}
}
if nvidiaCardCount == 0 {
return nil, errors.New("no devices found with vendor name 'Nvidia'")
}

devices := []string{}

for i := 0; i < nvidiaCardCount; i++ {
s := strconv.Itoa(i)
devices = append(devices, s)
}

return devices, nil
}

func ParseNvidiaDevices(nvidia string) ([]string, error) {
if nvidia == "all" {
return detectNvidiaDevices()
}
return strings.Split(nvidia, ","), nil
}
6 changes: 2 additions & 4 deletions core/lb.go
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ import (
"context"
"errors"
"math"
"strings"
"sync"

"github.com/golang/glog"
@@ -34,10 +33,9 @@ type LoadBalancingTranscoder struct {
idx int // Ensures a non-tapered work distribution
}

func NewLoadBalancingTranscoder(devices string, newTranscoderFn newTranscoderFn) Transcoder {
d := strings.Split(devices, ",")
func NewLoadBalancingTranscoder(devices []string, newTranscoderFn newTranscoderFn) Transcoder {
return &LoadBalancingTranscoder{
transcoders: d,
transcoders: devices,
newT: newTranscoderFn,
mu: &sync.RWMutex{},
load: make(map[string]int),
11 changes: 5 additions & 6 deletions core/lb_test.go
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@ package core
import (
"context"
"strconv"
"strings"
"testing"
"time"

@@ -28,7 +27,7 @@ func TestLB_CalculateCost(t *testing.T) {

func TestLB_LeastLoaded(t *testing.T) {
assert := assert.New(t)
lb := NewLoadBalancingTranscoder("0,1,2,3,4", newStubTranscoder).(*LoadBalancingTranscoder)
lb := NewLoadBalancingTranscoder([]string{"0", "1", "2", "3", "4"}, newStubTranscoder).(*LoadBalancingTranscoder)
rapid.Check(t, func(t *rapid.T) {
cost := rapid.IntRange(1, 10).Draw(t, "cost").(int)
transcoder := lb.leastLoaded()
@@ -52,7 +51,7 @@ func TestLB_Ratchet(t *testing.T) {
// Test: Two transcoders, several sessions with the same set of profiles
// Run multiple transcodes.
assert := assert.New(t)
lb := NewLoadBalancingTranscoder("0,1", newStubTranscoder).(*LoadBalancingTranscoder)
lb := NewLoadBalancingTranscoder([]string{"0", "1"}, newStubTranscoder).(*LoadBalancingTranscoder)
sessions := []string{"a", "b", "c", "d", "e"}

rapid.Check(t, func(t *rapid.T) {
@@ -73,7 +72,7 @@ func TestLB_SessionCleanupRace(t *testing.T) {
// Reproduce race condition around session cleanup #1750

assert := assert.New(t)
lb := NewLoadBalancingTranscoder("0", newStubTranscoder).(*LoadBalancingTranscoder)
lb := NewLoadBalancingTranscoder([]string{"0"}, newStubTranscoder).(*LoadBalancingTranscoder)
sess := "sess"
// Force create a new session
_, err := lb.Transcode(stubMetadata(sess, ffmpeg.P144p30fps16x9))
@@ -112,7 +111,7 @@ func TestLB_LoadAssignment(t *testing.T) {
// Subsequent segments should ignore subsequent load costs.

assert := assert.New(t)
lb := NewLoadBalancingTranscoder("0,1,2,3,4", newStubTranscoder).(*LoadBalancingTranscoder)
lb := NewLoadBalancingTranscoder([]string{"0", "1", "2", "3", "4"}, newStubTranscoder).(*LoadBalancingTranscoder)
sessions := []string{"a", "b", "c", "d", "e"}
profiles := []ffmpeg.VideoProfile{}
for _, v := range ffmpeg.VideoProfileLookup {
@@ -324,7 +323,7 @@ func (m *lbMachine) Init(t *rapid.T) {
devices = append(devices, strconv.Itoa(i))
}

m.lb = NewLoadBalancingTranscoder(strings.Join(devices, ","), newStubTranscoder).(*LoadBalancingTranscoder)
m.lb = NewLoadBalancingTranscoder(devices, newStubTranscoder).(*LoadBalancingTranscoder)
m.states = make(map[string]*machineState)

assert.Equal(t, devices, m.lb.transcoders) // sanity check
3 changes: 1 addition & 2 deletions core/transcoder.go
Original file line number Diff line number Diff line change
@@ -96,8 +96,7 @@ func (nv *NvidiaTranscoder) Transcode(md *SegTranscodingMetadata) (*TranscodeDat
}

// TestNvidiaTranscoder tries to transcode test segment on all the devices
func TestNvidiaTranscoder(gpu string) error {
devices := strings.Split(gpu, ",")
func TestNvidiaTranscoder(devices []string) error {
b := bytes.NewReader(testSegment)
z, err := gzip.NewReader(b)
if err != nil {
24 changes: 17 additions & 7 deletions doc/gpu.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# GPU Support

Livepeer supports decoding and encoding on NVIDIA GPUs on Linux. GPU
transcoding can be enabled by starting Livepeer in `-transcoder` mode with the
`-nvidia <device-list>` flag. The `<device-list>` is a comma-separated
Livepeer supports decoding and encoding on NVIDIA GPUs on Linux and Windows.
GPU transcoding can be enabled by starting Livepeer in `-transcoder` mode with
the `-nvidia <device-list>` flag. The `<device-list>` is a comma-separated
numerical list of GPU devices that you wish to use for transcoding. If you are
unsure of your GPU device, use the `nvidia-smi` utility. For example, to select
devices 0, 2 and 4:
@@ -11,6 +11,13 @@ devices 0, 2 and 4:
./livepeer -transcoder -nvidia 0,2,4
```

Alternatively, if you want to use all the available NVIDIA GPUs on your system,
you can set the flag like:

```
./livepeer -transcoder -nvidia all
```

### Limitations

Currently the following limitations are observed:
@@ -22,16 +29,19 @@ interleaved). Anything else will return an error.

* **CUDA Availability** If running the Livepeer binary, the CUDA shared libraries are expected to be installed in `/usr/local/cuda`. If the CUDA location differs on your machine, run the node with `LD_LIBRARY_PATH=</path/to/cuda>` environment variable.

So far, Livepeer has been tested to work with the following driver versions:
So far, Livepeer has been tested to work with the following driver versions:

CUDA | Nvidia
--|--
10.0.130 |
10.1 | 418.39 , 430.50
10.2.89 | 440.33.01
yondonfu marked this conversation as resolved.
Show resolved Hide resolved
11.0 | 450.36.06
10.2 | 440.33.01, 440.118.02
11.1,11.2 | 460.39

Nvidia's 450.xx drivers can occassionally lead to stuck transcoding sessions.
Refer to this [forum post](https://forum.livepeer.org/t/working-around-occasional-transcoding-issues-with-nvidia-driver-450/1219) on how to switch to a different driver version.

* **Driver Limits** "Retail GPU cards may impose a software limit on the number of concurrent transcode sessions allowed on the system in official drivers.
* **Driver Limits** Retail GPU cards may impose a software limit on the number of concurrent transcode sessions allowed on the system in official drivers.

* **Linux Only** We've only tested this on Linux. We haven't tried other platforms; if it works elsewhere, especially on Windows or OSX, let us know!

1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@ require (
github.com/huin/goupnp v1.0.0 // indirect
github.com/influxdata/influxdb v1.7.8 // indirect
github.com/jackpal/go-nat-pmp v1.0.1 // indirect
github.com/jaypipes/ghw v0.7.0
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 // indirect
github.com/livepeer/lpms v0.0.0-20210405233628-731af8d684c5
github.com/livepeer/m3u8 v0.11.1
18 changes: 18 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -34,6 +34,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -104,6 +106,8 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -112,6 +116,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -180,11 +186,17 @@ github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb v1.7.8 h1:oXd5TjXzU1b+xyFaH/8Ij+nCoUgyuO3ZDpgCuo62yg0=
github.com/influxdata/influxdb v1.7.8/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/jackpal/go-nat-pmp v1.0.1 h1:i0LektDkO1QlrTm/cSuP+PyBCDnYvjPLGl4LdWEMiaA=
github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jaypipes/ghw v0.7.0 h1:DO0qK9hESxkOTWyd/93hjYBRL7MdVSFqaXdcR7n4pVY=
github.com/jaypipes/ghw v0.7.0/go.mod h1:+gR9bjm3W/HnFi90liF+Fj9GpCe/Dsibl9Im8KmC7c4=
github.com/jaypipes/pcidb v0.6.0 h1:VIM7GKVaW4qba30cvB67xSCgJPTzkG8Kzw/cbs5PHWU=
github.com/jaypipes/pcidb v0.6.0/go.mod h1:L2RGk04sfRhp5wvHO0gfRAMoLY/F3PKv/nwJeVoho0o=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
@@ -225,6 +237,8 @@ github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -291,6 +305,8 @@ github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 h1:ju5UTwk5Odtm4trrY+4Ca4RMj5OyXbmVeDAVad2T0Jw=
github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
@@ -555,6 +571,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
pgregory.net/rapid v0.4.0 h1:/boyXNQlDs1pmk7g1b9u2KrYqXnqjj0ARUDsZj5kapg=
pgregory.net/rapid v0.4.0/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=